Chromium Code Reviews| Index: webkit/fileapi/cross_operation_delegate.cc |
| diff --git a/webkit/fileapi/cross_operation_delegate.cc b/webkit/fileapi/cross_operation_delegate.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..73c0e4dc4a1d6c43191ddcf573e5346c52eea4d7 |
| --- /dev/null |
| +++ b/webkit/fileapi/cross_operation_delegate.cc |
| @@ -0,0 +1,264 @@ |
| +// Copyright (c) 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 "webkit/fileapi/cross_operation_delegate.h" |
| + |
| +#include "base/bind.h" |
| +#include "webkit/blob/shareable_file_reference.h" |
| +#include "webkit/fileapi/file_system_context.h" |
| +#include "webkit/fileapi/file_system_operation_context.h" |
| +#include "webkit/fileapi/local_file_system_operation.h" |
| + |
| +namespace fileapi { |
| + |
| +CrossOperationDelegate::CrossOperationDelegate( |
| + LocalFileSystemOperation* original_operation, |
| + const FileSystemURL& src_root, |
| + const FileSystemURL& dest_root, |
| + OperationType operation_type, |
| + const StatusCallback& callback) |
| + : RecursiveOperationDelegate(original_operation), |
| + src_root_(src_root), |
| + dest_root_(dest_root), |
| + operation_type_(operation_type), |
| + callback_(callback), |
| + src_root_operation_(NULL) { |
| + same_file_system_ = |
| + src_root_.origin() == dest_root_.origin() && |
| + src_root_.type() == dest_root_.type(); |
| +} |
| + |
| +CrossOperationDelegate::~CrossOperationDelegate() { |
| + if (src_root_operation_) |
| + delete src_root_operation_; |
| +} |
| + |
| +void CrossOperationDelegate::Run() { |
| + // Not supported; this should never be called. |
| + NOTREACHED(); |
| +} |
| + |
| +void CrossOperationDelegate::RunRecursively() { |
| + // Perform light-weight checks first. |
| + |
| + // It is an error to try to copy/move an entry into its child. |
| + if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) { |
| + callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION); |
| + return; |
| + } |
| + |
| + // It is an error to copy/move an entry into the same path. |
| + if (same_file_system_ && src_root_.path() == dest_root_.path()) { |
| + callback_.Run(base::PLATFORM_FILE_ERROR_EXISTS); |
| + return; |
| + } |
| + |
| + // Initialize the src_root_operation_ for the src root URL. |
| + DCHECK(!src_root_operation_); |
| + if (!same_file_system_) { |
| + base::PlatformFileError error = base::PLATFORM_FILE_OK; |
| + FileSystemOperation* operation = file_system_context()-> |
| + CreateFileSystemOperation(src_root_, &error); |
| + if (error != base::PLATFORM_FILE_OK) { |
| + DCHECK(!operation); |
| + callback_.Run(error); |
| + return; |
| + } |
| + src_root_operation_ = operation->AsLocalFileSystemOperation(); |
| + DCHECK(src_root_operation_); |
| + } |
| + |
| + // First try to copy/move it as a file. |
| + CopyOrMoveFile(src_root_, dest_root_, |
| + base::Bind(&CrossOperationDelegate::DidTryCopyOrMoveFile, |
| + AsWeakPtr())); |
| +} |
| + |
| +void CrossOperationDelegate::ProcessFile(const FileSystemURL& src_url, |
| + const StatusCallback& callback) { |
| + CopyOrMoveFile(src_url, CreateDestURL(src_url), callback); |
| +} |
| + |
| +void CrossOperationDelegate::ProcessDirectory(const FileSystemURL& src_url, |
| + const StatusCallback& callback) { |
| + FileSystemURL dest_url = CreateDestURL(src_url); |
| + LocalFileSystemOperation* dest_operation = NewDestOperation(dest_url); |
| + if (!dest_operation) { |
| + return; |
| + } |
| + |
| + // If operation_type == Move we may need to record directories and |
| + // restore directory timestamps in the end, though it may have |
| + // negative performance impact. |
| + // See http://crbug.com/171284 for more details. |
| + dest_operation->CreateDirectory( |
| + dest_url, false /* exclusive */, false /* recursive */, callback); |
| +} |
| + |
| +void CrossOperationDelegate::DidTryCopyOrMoveFile( |
| + base::PlatformFileError error) { |
| + if (error == base::PLATFORM_FILE_OK || |
| + error != base::PLATFORM_FILE_ERROR_NOT_A_FILE) { |
| + callback_.Run(error); |
| + return; |
| + } |
| + |
| + // The src_root_ looks to be a directory. |
| + // Try removing the dest_root_ to see if it exists and/or it is an |
| + // empty directory. |
| + LocalFileSystemOperation* dest_operation = NewDestOperation(dest_root_); |
| + if (!dest_operation) |
| + return; |
| + dest_operation->RemoveDirectory( |
| + dest_root_, base::Bind(&CrossOperationDelegate::DidTryRemoveDestRoot, |
| + AsWeakPtr())); |
| +} |
| + |
| +void CrossOperationDelegate::DidTryRemoveDestRoot( |
| + base::PlatformFileError error) { |
| + if (error == base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY) { |
| + callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION); |
| + return; |
| + } |
| + if (error != base::PLATFORM_FILE_OK && |
| + error != base::PLATFORM_FILE_ERROR_NOT_FOUND) { |
| + callback_.Run(error); |
| + return; |
| + } |
| + |
| + // Start to process the source directory recursively. |
| + StartRecursiveOperation( |
| + src_root_, base::Bind(&CrossOperationDelegate::DidFinishCopy, |
| + AsWeakPtr(), src_root_, callback_)); |
| +} |
| + |
| +void CrossOperationDelegate::CopyOrMoveFile( |
| + const FileSystemURL& src, |
| + const FileSystemURL& dest, |
| + const StatusCallback& callback) { |
| + LocalFileSystemOperation* src_operation = NewSourceOperation(src); |
| + if (!src_operation) |
| + return; |
| + |
| + // Same filesystem case. |
| + if (same_file_system_) { |
|
ericu
2013/01/23 19:27:23
I'm getting a bit lost in all the redirection. If
|
| + if (operation_type_ == OPERATION_MOVE) |
| + src_operation->MoveLocalFile(src, dest, callback); |
| + else |
| + src_operation->CopyLocalFile(src, dest, callback); |
| + return; |
| + } |
| + |
| + // Cross filesystem case. |
| + // Performs CreateSnapshotFile, CopyInForeignFile and then calls |
| + // copy_callback which removes the source file if operation_type == MOVE. |
| + StatusCallback copy_callback = |
| + base::Bind(&CrossOperationDelegate::DidFinishCopy, AsWeakPtr(), |
| + src, callback); |
| + src_operation->CreateSnapshotFile( |
| + src, base::Bind(&CrossOperationDelegate::DidCreateSnapshot, AsWeakPtr(), |
| + dest, copy_callback)); |
| +} |
| + |
| +void CrossOperationDelegate::DidCreateSnapshot( |
| + const FileSystemURL& dest, |
| + const StatusCallback& callback, |
| + base::PlatformFileError error, |
| + const base::PlatformFileInfo& file_info, |
| + const 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()); |
| + |
| + LocalFileSystemOperation* dest_operation = NewDestOperation(dest); |
| + if (!dest_operation) |
| + return; |
| + dest_operation->CopyInForeignFile(platform_path, dest, callback); |
| +} |
| + |
| +void CrossOperationDelegate::DidFinishCopy( |
| + const FileSystemURL& src, |
| + const StatusCallback& callback, |
| + base::PlatformFileError error) { |
| + if (error != base::PLATFORM_FILE_OK || |
| + operation_type_ == OPERATION_COPY) { |
| + callback.Run(error); |
| + return; |
| + } |
| + |
| + DCHECK_EQ(OPERATION_MOVE, operation_type_); |
| + |
| + // Remove the source for finalizing move oepration. |
|
ericu
2013/01/23 19:27:23
typo: operation
|
| + LocalFileSystemOperation* src_operation = NewSourceOperation(src); |
| + if (!src_operation) |
| + return; |
| + src_operation->Remove( |
| + src_root_, true /* recursive */, |
| + base::Bind(&CrossOperationDelegate::DidRemoveSourceForMove, |
| + AsWeakPtr(), callback)); |
| +} |
| + |
| +void CrossOperationDelegate::DidRemoveSourceForMove( |
| + const StatusCallback& callback, |
| + base::PlatformFileError error) { |
| + if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) |
| + error = base::PLATFORM_FILE_OK; |
| + callback.Run(error); |
| +} |
| + |
| +FileSystemURL CrossOperationDelegate::CreateDestURL( |
| + const FileSystemURL& src_url) const { |
| + DCHECK_EQ(src_root_.type(), src_url.type()); |
| + DCHECK_EQ(src_root_.origin(), src_url.origin()); |
| + |
| + FilePath path = dest_root_.path(); |
| + src_root_.path().AppendRelativePath(src_url.path(), &path); |
| + return dest_root_.WithPath(path); |
| +} |
| + |
| +LocalFileSystemOperation* CrossOperationDelegate::NewDestOperation( |
| + const FileSystemURL& url) { |
| + base::PlatformFileError error = base::PLATFORM_FILE_OK; |
| + LocalFileSystemOperation* operation = |
| + RecursiveOperationDelegate::NewOperation(url, &error); |
| + if (!operation) { |
| + DCHECK_NE(base::PLATFORM_FILE_OK, error); |
| + callback_.Run(error); |
| + return NULL; |
| + } |
| + return operation; |
| +} |
| + |
| +LocalFileSystemOperation* CrossOperationDelegate::NewSourceOperation( |
| + const FileSystemURL& url) { |
| + if (same_file_system_) |
| + return NewDestOperation(url); |
| + |
| + base::PlatformFileError error = base::PLATFORM_FILE_OK; |
| + FileSystemOperation* operation = file_system_context()-> |
| + CreateFileSystemOperation(url, &error); |
| + if (!operation) { |
| + DCHECK_NE(base::PLATFORM_FILE_OK, error); |
| + callback_.Run(error); |
| + return NULL; |
| + } |
| + LocalFileSystemOperation* local_operation = |
| + operation->AsLocalFileSystemOperation(); |
| + DCHECK(local_operation); |
| + DCHECK(src_root_operation_); |
| + |
| + // Let the new operation inherit from the root operation. |
| + local_operation->set_overriding_operation_context( |
| + src_root_operation_->operation_context()); |
| + return local_operation; |
| +} |
| + |
| +} // namespace fileapi |