Index: webkit/browser/fileapi/copy_or_move_operation_delegate.cc |
diff --git a/webkit/browser/fileapi/copy_or_move_operation_delegate.cc b/webkit/browser/fileapi/copy_or_move_operation_delegate.cc |
index 845e33b72e3c7fa70c6a7cf214fc63f69284c70d..fed1abd7dd6da511232982b6d07c1e459f35a190 100644 |
--- a/webkit/browser/fileapi/copy_or_move_operation_delegate.cc |
+++ b/webkit/browser/fileapi/copy_or_move_operation_delegate.cc |
@@ -6,7 +6,11 @@ |
#include "base/bind.h" |
#include "base/files/file_path.h" |
+#include "net/base/io_buffer.h" |
+#include "net/base/net_errors.h" |
+#include "webkit/browser/blob/file_stream_reader.h" |
#include "webkit/browser/fileapi/copy_or_move_file_validator.h" |
+#include "webkit/browser/fileapi/file_stream_writer.h" |
#include "webkit/browser/fileapi/file_system_context.h" |
#include "webkit/browser/fileapi/file_system_operation_runner.h" |
#include "webkit/browser/fileapi/file_system_url.h" |
@@ -289,8 +293,230 @@ class SnapshotCopyOrMoveImpl |
DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl); |
}; |
+// The size of buffer for StreamCopyHelper. |
+const int kReadBufferSize = 32768; |
+ |
+// To avoid too many progress callbacks, it should be called less |
+// frequently than 50ms. |
+const int kMinProgressCallbackInvocationSpanInMilliseconds = 50; |
+ |
+// Specifically for cross file system copy/move operation, this class uses |
+// stream reader and writer for copying. Validator is not supported, so if |
+// necessary SnapshotCopyOrMoveImpl should be used. |
+class StreamCopyOrMoveImpl |
+ : public CopyOrMoveOperationDelegate::CopyOrMoveImpl { |
+ public: |
+ StreamCopyOrMoveImpl( |
+ FileSystemOperationRunner* operation_runner, |
+ CopyOrMoveOperationDelegate::OperationType operation_type, |
+ const FileSystemURL& src_url, |
+ const FileSystemURL& dest_url, |
+ scoped_ptr<webkit_blob::FileStreamReader> reader, |
+ scoped_ptr<FileStreamWriter> writer, |
+ const FileSystemOperation::CopyFileProgressCallback& |
+ file_progress_callback) |
+ : operation_runner_(operation_runner), |
+ operation_type_(operation_type), |
+ src_url_(src_url), |
+ dest_url_(dest_url), |
+ reader_(reader.Pass()), |
+ writer_(writer.Pass()), |
+ file_progress_callback_(file_progress_callback), |
+ weak_factory_(this) { |
+ } |
+ |
+ virtual void Run( |
+ const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { |
+ // Reader can be created even if the entry does not exist or the entry is |
+ // a directory. To check errors before destination file creation, |
+ // check metadata first. |
+ operation_runner_->GetMetadata( |
+ src_url_, |
+ base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource, |
+ weak_factory_.GetWeakPtr(), callback)); |
+ } |
+ |
+ private: |
+ void RunAfterGetMetadataForSource( |
+ const CopyOrMoveOperationDelegate::StatusCallback& callback, |
+ base::PlatformFileError error, |
+ const base::PlatformFileInfo& file_info) { |
+ if (error != base::PLATFORM_FILE_OK) { |
+ callback.Run(error); |
+ return; |
+ } |
+ if (file_info.is_directory) { |
+ // If not a directory, failed with appropriate error code. |
+ callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE); |
+ return; |
+ } |
+ |
+ // To use FileStreamWriter, we need to ensure the destination file exists. |
+ operation_runner_->CreateFile( |
+ dest_url_, false /* exclusive */, |
+ base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination, |
+ weak_factory_.GetWeakPtr(), callback)); |
+ } |
+ |
+ void RunAfterCreateFileForDestination( |
+ const CopyOrMoveOperationDelegate::StatusCallback& callback, |
+ base::PlatformFileError error) { |
+ if (error != base::PLATFORM_FILE_OK) { |
+ callback.Run(error); |
+ return; |
+ } |
+ |
+ DCHECK(!copy_helper_); |
+ copy_helper_.reset( |
+ new CopyOrMoveOperationDelegate::StreamCopyHelper( |
+ reader_.Pass(), writer_.Pass(), |
+ kReadBufferSize, |
+ file_progress_callback_, |
+ base::TimeDelta::FromMilliseconds( |
+ kMinProgressCallbackInvocationSpanInMilliseconds))); |
+ copy_helper_->Run( |
+ base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy, |
+ weak_factory_.GetWeakPtr(), callback)); |
+ } |
+ |
+ void RunAfterStreamCopy( |
+ const CopyOrMoveOperationDelegate::StatusCallback& callback, |
+ base::PlatformFileError error) { |
+ if (error != base::PLATFORM_FILE_OK) { |
+ callback.Run(error); |
+ 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_, false /* recursive */, |
+ base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove, |
+ weak_factory_.GetWeakPtr(), callback)); |
+ } |
+ |
+ void RunAfterRemoveForMove( |
+ const CopyOrMoveOperationDelegate::StatusCallback& callback, |
+ base::PlatformFileError error) { |
+ if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) |
+ error = base::PLATFORM_FILE_OK; |
+ callback.Run(error); |
+ } |
+ |
+ FileSystemOperationRunner* operation_runner_; |
+ CopyOrMoveOperationDelegate::OperationType operation_type_; |
+ FileSystemURL src_url_; |
+ FileSystemURL dest_url_; |
+ scoped_ptr<webkit_blob::FileStreamReader> reader_; |
+ scoped_ptr<FileStreamWriter> writer_; |
+ FileSystemOperation::CopyFileProgressCallback file_progress_callback_; |
+ scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_; |
+ |
+ base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_; |
+ DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl); |
+}; |
+ |
} // namespace |
+CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper( |
+ scoped_ptr<webkit_blob::FileStreamReader> reader, |
+ scoped_ptr<FileStreamWriter> writer, |
+ int buffer_size, |
+ const FileSystemOperation::CopyFileProgressCallback& |
+ file_progress_callback, |
+ const base::TimeDelta& min_progress_callback_invocation_span) |
+ : reader_(reader.Pass()), |
+ writer_(writer.Pass()), |
+ file_progress_callback_(file_progress_callback), |
+ io_buffer_(new net::IOBufferWithSize(buffer_size)), |
+ num_copied_bytes_(0), |
+ min_progress_callback_invocation_span_( |
+ min_progress_callback_invocation_span), |
+ weak_factory_(this) { |
+} |
+ |
+CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() { |
+} |
+ |
+void CopyOrMoveOperationDelegate::StreamCopyHelper::Run( |
+ const StatusCallback& callback) { |
+ file_progress_callback_.Run(0); |
+ last_progress_callback_invocation_time_ = base::Time::Now(); |
+ Read(callback); |
+} |
+ |
+void CopyOrMoveOperationDelegate::StreamCopyHelper::Read( |
+ const StatusCallback& callback) { |
+ int result = reader_->Read( |
+ io_buffer_.get(), io_buffer_->size(), |
+ base::Bind(&StreamCopyHelper::DidRead, |
+ weak_factory_.GetWeakPtr(), callback)); |
+ if (result != net::ERR_IO_PENDING) |
+ DidRead(callback, result); |
+} |
+ |
+void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead( |
+ const StatusCallback& callback, int result) { |
+ if (result < 0) { |
+ callback.Run(NetErrorToPlatformFileError(result)); |
+ return; |
+ } |
+ |
+ if (result == 0) { |
+ // Here is the EOF. |
+ callback.Run(base::PLATFORM_FILE_OK); |
+ return; |
+ } |
+ |
+ Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result)); |
+} |
+ |
+void CopyOrMoveOperationDelegate::StreamCopyHelper::Write( |
+ const StatusCallback& callback, |
+ scoped_refptr<net::DrainableIOBuffer> buffer) { |
+ DCHECK_GT(buffer->BytesRemaining(), 0); |
+ |
+ int result = writer_->Write( |
+ buffer.get(), buffer->BytesRemaining(), |
+ base::Bind(&StreamCopyHelper::DidWrite, |
+ weak_factory_.GetWeakPtr(), callback, buffer)); |
+ if (result != net::ERR_IO_PENDING) |
+ DidWrite(callback, buffer, result); |
+} |
+ |
+void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite( |
+ const StatusCallback& callback, |
+ scoped_refptr<net::DrainableIOBuffer> buffer, |
+ int result) { |
+ if (result < 0) { |
+ callback.Run(NetErrorToPlatformFileError(result)); |
+ return; |
+ } |
+ |
+ buffer->DidConsume(result); |
+ num_copied_bytes_ += result; |
+ |
+ // Check the elapsed time since last |file_progress_callback_| invocation. |
+ base::Time now = base::Time::Now(); |
+ if (now - last_progress_callback_invocation_time_ >= |
+ min_progress_callback_invocation_span_) { |
+ file_progress_callback_.Run(num_copied_bytes_); |
+ last_progress_callback_invocation_time_ = now; |
+ } |
+ |
+ if (buffer->BytesRemaining() > 0) { |
+ Write(callback, buffer); |
+ return; |
+ } |
+ |
+ Read(callback); |
+} |
CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate( |
FileSystemContext* file_system_context, |
@@ -359,7 +585,6 @@ void CopyOrMoveOperationDelegate::ProcessFile( |
weak_factory_.GetWeakPtr(), src_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( |
@@ -369,11 +594,28 @@ void CopyOrMoveOperationDelegate::ProcessFile( |
return; |
} |
- impl = new SnapshotCopyOrMoveImpl( |
- operation_runner(), operation_type_, src_url, dest_url, option_, |
- validator_factory, |
- base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, |
- weak_factory_.GetWeakPtr(), src_url)); |
+ if (!validator_factory) { |
+ scoped_ptr<webkit_blob::FileStreamReader> reader = |
+ file_system_context()->CreateFileStreamReader( |
+ src_url, 0, base::Time()); |
+ scoped_ptr<FileStreamWriter> writer = |
+ file_system_context()->CreateFileStreamWriter(dest_url, 0); |
+ if (reader && writer) { |
+ impl = new StreamCopyOrMoveImpl( |
+ operation_runner(), operation_type_, src_url, dest_url, |
+ reader.Pass(), writer.Pass(), |
+ base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, |
+ weak_factory_.GetWeakPtr(), src_url)); |
+ } |
+ } |
+ |
+ if (!impl) { |
+ impl = new SnapshotCopyOrMoveImpl( |
+ operation_runner(), operation_type_, src_url, dest_url, option_, |
+ validator_factory, |
+ base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, |
+ weak_factory_.GetWeakPtr(), src_url)); |
+ } |
} |
// Register the running task. |