Commit 8ce03034 authored by hidehiko@chromium.org's avatar hidehiko@chromium.org

Extract CopyOrMoveFile operation into classes.

To prepare implementing ProgressCallbak, Cancel and stream based copy/move
operation, this CL extracts main file-copying logic into classes.

BUG=279287, 278038, 278036
TEST=Ran content_unittests

Review URL: https://chromiumcodereview.appspot.com/23621026

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@222083 0039d316-1c4b-4281-b951-d872f2087c98
parent d68db3dc
......@@ -16,6 +16,263 @@
namespace fileapi {
class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
public:
virtual ~CopyOrMoveImpl() {}
virtual void Run(
const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
protected:
CopyOrMoveImpl() {}
DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
};
namespace {
// Copies a file on a (same) file system. Just delegate the operation to
// |operation_runner|.
class CopyOrMoveOnSameFileSystemImpl
: public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
public:
CopyOrMoveOnSameFileSystemImpl(
FileSystemOperationRunner* operation_runner,
CopyOrMoveOperationDelegate::OperationType operation_type,
const FileSystemURL& src_url,
const FileSystemURL& dest_url)
: operation_runner_(operation_runner),
operation_type_(operation_type),
src_url_(src_url),
dest_url_(dest_url) {
}
virtual void Run(
const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
operation_runner_->MoveFileLocal(src_url_, dest_url_, callback);
} else {
// TODO(hidehiko): Support progress callback.
operation_runner_->CopyFileLocal(
src_url_, dest_url_,
FileSystemOperationRunner::CopyFileProgressCallback(), callback);
}
}
private:
FileSystemOperationRunner* operation_runner_;
CopyOrMoveOperationDelegate::OperationType operation_type_;
FileSystemURL src_url_;
FileSystemURL dest_url_;
DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl);
};
// Specifically for cross file system copy/move operation, this class creates
// a snapshot file, validates it if necessary, runs copying process,
// validates the created file, and removes source file for move (noop for
// copy).
class SnapshotCopyOrMoveImpl
: public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
public:
SnapshotCopyOrMoveImpl(
FileSystemOperationRunner* operation_runner,
CopyOrMoveOperationDelegate::OperationType operation_type,
const FileSystemURL& src_url,
const FileSystemURL& dest_url,
CopyOrMoveFileValidatorFactory* validator_factory)
: operation_runner_(operation_runner),
operation_type_(operation_type),
src_url_(src_url),
dest_url_(dest_url),
validator_factory_(validator_factory),
weak_factory_(this) {
}
virtual void Run(
const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
operation_runner_->CreateSnapshotFile(
src_url_,
base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
weak_factory_.GetWeakPtr(), callback));
}
private:
void RunAfterCreateSnapshot(
const CopyOrMoveOperationDelegate::StatusCallback& callback,
base::PlatformFileError error,
const base::PlatformFileInfo& file_info,
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
if (error != base::PLATFORM_FILE_OK) {
callback.Run(error);
return;
}
// For now we assume CreateSnapshotFile always return a valid local file
// path.
DCHECK(!platform_path.empty());
if (!validator_factory_) {
// No validation is needed.
RunAfterPreWriteValidation(
platform_path, file_ref, callback, base::PLATFORM_FILE_OK);
return;
}
// Run pre write validation.
PreWriteValidation(
platform_path,
base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation,
weak_factory_.GetWeakPtr(),
platform_path, file_ref, callback));
}
void RunAfterPreWriteValidation(
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
const CopyOrMoveOperationDelegate::StatusCallback& callback,
base::PlatformFileError error) {
if (error != base::PLATFORM_FILE_OK) {
callback.Run(error);
return;
}
// |file_ref| is unused but necessary to keep the file alive until
// CopyInForeignFile() is completed.
operation_runner_->CopyInForeignFile(
platform_path, dest_url_,
base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile,
weak_factory_.GetWeakPtr(), file_ref, callback));
}
void RunAfterCopyInForeignFile(
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
const CopyOrMoveOperationDelegate::StatusCallback& callback,
base::PlatformFileError error) {
if (error != base::PLATFORM_FILE_OK) {
callback.Run(error);
return;
}
// |validator_| is NULL when the destination filesystem does not do
// validation.
if (!validator_) {
// No validation is needed.
RunAfterPostWriteValidation(callback, base::PLATFORM_FILE_OK);
return;
}
PostWriteValidation(
base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
weak_factory_.GetWeakPtr(), callback));
}
void RunAfterPostWriteValidation(
const CopyOrMoveOperationDelegate::StatusCallback& callback,
base::PlatformFileError error) {
if (error != base::PLATFORM_FILE_OK) {
// Failed to validate. Remove the destination file.
operation_runner_->Remove(
dest_url_, true /* recursive */,
base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError,
weak_factory_.GetWeakPtr(), error, callback));
return;
}
if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
callback.Run(base::PLATFORM_FILE_OK);
return;
}
DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
// Remove the source for finalizing move operation.
operation_runner_->Remove(
src_url_, true /* recursive */,
base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove,
weak_factory_.GetWeakPtr(), callback));
}
void RunAfterRemoveSourceForMove(
const CopyOrMoveOperationDelegate::StatusCallback& callback,
base::PlatformFileError error) {
if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
error = base::PLATFORM_FILE_OK;
callback.Run(error);
}
void DidRemoveDestForError(
base::PlatformFileError prior_error,
const CopyOrMoveOperationDelegate::StatusCallback& callback,
base::PlatformFileError error) {
if (error != base::PLATFORM_FILE_OK) {
VLOG(1) << "Error removing destination file after validation error: "
<< error;
}
callback.Run(prior_error);
}
// Runs pre-write validation.
void PreWriteValidation(
const base::FilePath& platform_path,
const CopyOrMoveOperationDelegate::StatusCallback& callback) {
DCHECK(validator_factory_);
validator_.reset(
validator_factory_->CreateCopyOrMoveFileValidator(
src_url_, platform_path));
validator_->StartPreWriteValidation(callback);
}
// Runs post-write validation.
void PostWriteValidation(
const CopyOrMoveOperationDelegate::StatusCallback& callback) {
operation_runner_->CreateSnapshotFile(
dest_url_,
base::Bind(
&SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile,
weak_factory_.GetWeakPtr(), callback));
}
void PostWriteValidationAfterCreateSnapshotFile(
const CopyOrMoveOperationDelegate::StatusCallback& callback,
base::PlatformFileError error,
const base::PlatformFileInfo& file_info,
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
if (error != base::PLATFORM_FILE_OK) {
callback.Run(error);
return;
}
DCHECK(validator_);
// Note: file_ref passed here to keep the file alive until after
// the StartPostWriteValidation operation finishes.
validator_->StartPostWriteValidation(
platform_path,
base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation,
weak_factory_.GetWeakPtr(), file_ref, callback));
}
// |file_ref| is unused; it is passed here to make sure the reference is
// alive until after post-write validation is complete.
void DidPostWriteValidation(
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
const CopyOrMoveOperationDelegate::StatusCallback& callback,
base::PlatformFileError error) {
callback.Run(error);
}
FileSystemOperationRunner* operation_runner_;
CopyOrMoveOperationDelegate::OperationType operation_type_;
FileSystemURL src_url_;
FileSystemURL dest_url_;
CopyOrMoveFileValidatorFactory* validator_factory_;
scoped_ptr<CopyOrMoveFileValidator> validator_;
base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl);
};
} // namespace
CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
FileSystemContext* file_system_context,
const FileSystemURL& src_root,
......@@ -32,6 +289,7 @@ CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
}
CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
STLDeleteElements(&running_copy_set_);
}
void CopyOrMoveOperationDelegate::Run() {
......@@ -55,14 +313,14 @@ void CopyOrMoveOperationDelegate::RunRecursively() {
}
// First try to copy/move it as a file.
CopyOrMoveFile(URLPair(src_root_, dest_root_),
CopyOrMoveFile(src_root_, dest_root_,
base::Bind(&CopyOrMoveOperationDelegate::DidTryCopyOrMoveFile,
weak_factory_.GetWeakPtr()));
}
void CopyOrMoveOperationDelegate::ProcessFile(const FileSystemURL& src_url,
const StatusCallback& callback) {
CopyOrMoveFile(URLPair(src_url, CreateDestURL(src_url)), callback);
CopyOrMoveFile(src_url, CreateDestURL(src_url), callback);
}
void CopyOrMoveOperationDelegate::ProcessDirectory(const FileSystemURL& src_url,
......@@ -116,85 +374,6 @@ void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
weak_factory_.GetWeakPtr(), src_root_, callback_));
}
void CopyOrMoveOperationDelegate::CopyOrMoveFile(
const URLPair& url_pair,
const StatusCallback& callback) {
// Same filesystem case.
if (same_file_system_) {
if (operation_type_ == OPERATION_MOVE) {
operation_runner()->MoveFileLocal(url_pair.src, url_pair.dest, callback);
} else {
// TODO(hidehiko): Support progress callback.
operation_runner()->CopyFileLocal(
url_pair.src, url_pair.dest,
FileSystemOperationRunner::CopyFileProgressCallback(), callback);
}
return;
}
// Cross filesystem case.
// Perform CreateSnapshotFile, CopyInForeignFile and then calls
// copy_callback which removes the source file if operation_type == MOVE.
StatusCallback copy_callback =
base::Bind(&CopyOrMoveOperationDelegate::DidFinishCopy,
weak_factory_.GetWeakPtr(), url_pair, callback);
operation_runner()->CreateSnapshotFile(
url_pair.src,
base::Bind(&CopyOrMoveOperationDelegate::DidCreateSnapshot,
weak_factory_.GetWeakPtr(), url_pair, copy_callback));
}
void CopyOrMoveOperationDelegate::DidCreateSnapshot(
const URLPair& url_pair,
const StatusCallback& callback,
base::PlatformFileError error,
const base::PlatformFileInfo& file_info,
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
if (error != base::PLATFORM_FILE_OK) {
callback.Run(error);
return;
}
current_file_ref_ = file_ref;
// For now we assume CreateSnapshotFile always return a valid local file path.
// TODO(kinuko): Otherwise create a FileStreamReader to perform a copy/move.
DCHECK(!platform_path.empty());
CopyOrMoveFileValidatorFactory* factory =
file_system_context()->GetCopyOrMoveFileValidatorFactory(
dest_root_.type(), &error);
if (error != base::PLATFORM_FILE_OK) {
callback.Run(error);
return;
}
if (!factory) {
DidValidateFile(url_pair.dest, callback, file_info, platform_path, error);
return;
}
validator_.reset(
factory->CreateCopyOrMoveFileValidator(url_pair.src, platform_path));
validator_->StartPreWriteValidation(
base::Bind(&CopyOrMoveOperationDelegate::DidValidateFile,
weak_factory_.GetWeakPtr(),
url_pair.dest, callback, file_info, platform_path));
}
void CopyOrMoveOperationDelegate::DidValidateFile(
const FileSystemURL& dest,
const StatusCallback& callback,
const base::PlatformFileInfo& file_info,
const base::FilePath& platform_path,
base::PlatformFileError error) {
if (error != base::PLATFORM_FILE_OK) {
callback.Run(error);
return;
}
operation_runner()->CopyInForeignFile(platform_path, dest, callback);
}
void CopyOrMoveOperationDelegate::DidFinishRecursiveCopyDir(
const FileSystemURL& src,
const StatusCallback& callback,
......@@ -214,89 +393,51 @@ void CopyOrMoveOperationDelegate::DidFinishRecursiveCopyDir(
weak_factory_.GetWeakPtr(), callback));
}
void CopyOrMoveOperationDelegate::DidFinishCopy(
const URLPair& url_pair,
void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
const StatusCallback& callback,
base::PlatformFileError error) {
if (error != base::PLATFORM_FILE_OK) {
callback.Run(error);
return;
}
// |validator_| is NULL in the same-filesystem case or when the destination
// filesystem does not do validation.
if (!validator_.get()) {
scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
DidPostWriteValidation(url_pair, callback, file_ref,
base::PLATFORM_FILE_OK);
return;
}
DCHECK(!same_file_system_);
operation_runner()->CreateSnapshotFile(
url_pair.dest,
base::Bind(&CopyOrMoveOperationDelegate::DoPostWriteValidation,
weak_factory_.GetWeakPtr(), url_pair, callback));
if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
error = base::PLATFORM_FILE_OK;
callback.Run(error);
}
void CopyOrMoveOperationDelegate::DoPostWriteValidation(
const URLPair& url_pair,
const StatusCallback& callback,
base::PlatformFileError error,
const base::PlatformFileInfo& file_info,
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
if (error != base::PLATFORM_FILE_OK) {
operation_runner()->Remove(
url_pair.dest, true,
base::Bind(&CopyOrMoveOperationDelegate::DidRemoveDestForError,
weak_factory_.GetWeakPtr(), error, callback));
return;
}
DCHECK(validator_.get());
// Note: file_ref passed here to keep the file alive until after
// the StartPostWriteValidation operation finishes.
validator_->StartPostWriteValidation(
platform_path,
base::Bind(&CopyOrMoveOperationDelegate::DidPostWriteValidation,
weak_factory_.GetWeakPtr(), url_pair, callback, file_ref));
}
void CopyOrMoveOperationDelegate::CopyOrMoveFile(
const FileSystemURL& src_url,
const FileSystemURL& dest_url,
const StatusCallback& callback) {
CopyOrMoveImpl* impl = NULL;
if (same_file_system_) {
impl = new CopyOrMoveOnSameFileSystemImpl(
operation_runner(), operation_type_, src_url, dest_url);
} else {
// Cross filesystem case.
// TODO(hidehiko): Support stream based copy. crbug.com/279287.
base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
CopyOrMoveFileValidatorFactory* validator_factory =
file_system_context()->GetCopyOrMoveFileValidatorFactory(
dest_root_.type(), &error);
if (error != base::PLATFORM_FILE_OK) {
callback.Run(error);
return;
}
// |file_ref| is unused; it is passed here to make sure the reference is
// alive until after post-write validation is complete.
void CopyOrMoveOperationDelegate::DidPostWriteValidation(
const URLPair& url_pair,
const StatusCallback& callback,
const scoped_refptr<webkit_blob::ShareableFileReference>& /*file_ref*/,
base::PlatformFileError error) {
if (error != base::PLATFORM_FILE_OK) {
operation_runner()->Remove(
url_pair.dest, true,
base::Bind(&CopyOrMoveOperationDelegate::DidRemoveDestForError,
weak_factory_.GetWeakPtr(), error, callback));
return;
impl = new SnapshotCopyOrMoveImpl(
operation_runner(), operation_type_, src_url, dest_url,
validator_factory);
}
if (operation_type_ == OPERATION_COPY) {
callback.Run(error);
return;
}
DCHECK_EQ(OPERATION_MOVE, operation_type_);
// Remove the source for finalizing move operation.
operation_runner()->Remove(
url_pair.src, true /* recursive */,
base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
weak_factory_.GetWeakPtr(), callback));
// Register the running task.
running_copy_set_.insert(impl);
impl->Run(base::Bind(&CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
weak_factory_.GetWeakPtr(), impl, callback));
}
void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
CopyOrMoveImpl* impl,
const StatusCallback& callback,
base::PlatformFileError error) {
if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
error = base::PLATFORM_FILE_OK;
running_copy_set_.erase(impl);
delete impl;
callback.Run(error);
}
......@@ -314,15 +455,4 @@ FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
relative);
}
void CopyOrMoveOperationDelegate::DidRemoveDestForError(
base::PlatformFileError prior_error,
const StatusCallback& callback,
base::PlatformFileError error) {
if (error != base::PLATFORM_FILE_OK) {
VLOG(1) << "Error removing destination file after validation error: "
<< error;
}
callback.Run(prior_error);
}
} // namespace fileapi
......@@ -23,6 +23,8 @@ class CopyOrMoveFileValidator;
class CopyOrMoveOperationDelegate
: public RecursiveOperationDelegate {
public:
class CopyOrMoveImpl;
enum OperationType {
OPERATION_COPY,
OPERATION_MOVE
......@@ -45,60 +47,23 @@ class CopyOrMoveOperationDelegate
const StatusCallback& callback) OVERRIDE;
private:
struct URLPair {
URLPair(const FileSystemURL& src, const FileSystemURL& dest)
: src(src),
dest(dest) {
}
FileSystemURL src;
FileSystemURL dest;
};
void DidTryCopyOrMoveFile(base::PlatformFileError error);
void DidTryRemoveDestRoot(base::PlatformFileError error);
void CopyOrMoveFile(
const URLPair& url_pair,
const StatusCallback& callback);
void DidCreateSnapshot(
const URLPair& url_pair,
const StatusCallback& callback,
base::PlatformFileError error,
const base::PlatformFileInfo& file_info,
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref);
void DidValidateFile(
const FileSystemURL& dest,
const StatusCallback& callback,
const base::PlatformFileInfo& file_info,
const base::FilePath& platform_path,
base::PlatformFileError error);
void DidFinishRecursiveCopyDir(
const FileSystemURL& src,
const StatusCallback& callback,
base::PlatformFileError error);
void DidFinishCopy(
const URLPair& url_pair,
const StatusCallback& callback,
base::PlatformFileError error);
void DoPostWriteValidation(
const URLPair& url_pair,
const StatusCallback& callback,
base::PlatformFileError error,
const base::PlatformFileInfo& file_info,
const base::FilePath& platform_path,
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref);
void DidPostWriteValidation(
const URLPair& url_pair,
const StatusCallback& callback,
const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
base::PlatformFileError error);
void DidRemoveSourceForMove(
const StatusCallback& callback,
base::PlatformFileError error);
void DidRemoveDestForError(
base::PlatformFileError prior_error,
const StatusCallback& callback,
base::PlatformFileError error);
void DidFinishRecursiveCopyDir(const FileSystemURL& src,
const StatusCallback& callback,
base::PlatformFileError error);
void DidRemoveSourceForMove(const StatusCallback& callback,
base::PlatformFileError error);
// Starts Copy (or Move based on |operation_type_|) from |src_url| to
// |dest_url|. Upon completion |callback| is invoked.
// This can be run for multiple files in parallel.
void CopyOrMoveFile(const FileSystemURL& src_url,
const FileSystemURL& dest_url,
const StatusCallback& callback);
void DidCopyOrMoveFile(CopyOrMoveImpl* impl,
const StatusCallback& callback,
base::PlatformFileError error);
FileSystemURL CreateDestURL(const FileSystemURL& src_url) const;
......@@ -108,10 +73,7 @@ class CopyOrMoveOperationDelegate
OperationType operation_type_;
StatusCallback callback_;
scoped_refptr<webkit_blob::ShareableFileReference> current_file_ref_;
scoped_ptr<CopyOrMoveFileValidator> validator_;
std::set<CopyOrMoveImpl*> running_copy_set_;
base::WeakPtrFactory<CopyOrMoveOperationDelegate> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationDelegate);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment