| 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.
|
|
|