| // Copyright 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 "chrome/browser/chromeos/drive/webkit_file_stream_writer_impl.h" |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "chrome/browser/chromeos/drive/file_system_util.h" |
| #include "chrome/browser/chromeos/drive/fileapi_worker.h" |
| #include "chrome/browser/google_apis/task_util.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| #include "webkit/browser/fileapi/file_stream_writer.h" |
| |
| using content::BrowserThread; |
| |
| namespace drive { |
| namespace internal { |
| namespace { |
| |
| // Creates a writable snapshot file of the |drive_path|. |
| void CreateWritableSnapshotFile( |
| const WebkitFileStreamWriterImpl::FileSystemGetter& file_system_getter, |
| const base::FilePath& drive_path, |
| const fileapi_internal::CreateWritableSnapshotFileCallback& callback) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| BrowserThread::PostTask( |
| BrowserThread::UI, |
| FROM_HERE, |
| base::Bind( |
| &fileapi_internal::RunFileSystemCallback, |
| file_system_getter, |
| base::Bind(&fileapi_internal::CreateWritableSnapshotFile, |
| drive_path, google_apis::CreateRelayCallback(callback)), |
| google_apis::CreateRelayCallback(base::Bind( |
| callback, base::PLATFORM_FILE_ERROR_FAILED, base::FilePath(), |
| base::Closure())))); |
| } |
| |
| } // namespace |
| |
| WebkitFileStreamWriterImpl::WebkitFileStreamWriterImpl( |
| const FileSystemGetter& file_system_getter, |
| base::TaskRunner* file_task_runner, |
| const base::FilePath& file_path, |
| int64 offset) |
| : file_system_getter_(file_system_getter), |
| file_task_runner_(file_task_runner), |
| file_path_(file_path), |
| offset_(offset), |
| weak_ptr_factory_(this) { |
| } |
| |
| WebkitFileStreamWriterImpl::~WebkitFileStreamWriterImpl() { |
| if (local_file_writer_) { |
| // If the file is opened, close it at destructor. |
| // It is necessary to close the local file in advance. |
| local_file_writer_.reset(); |
| DCHECK(!close_callback_on_ui_thread_.is_null()); |
| BrowserThread::PostTask(BrowserThread::UI, |
| FROM_HERE, |
| close_callback_on_ui_thread_); |
| } |
| } |
| |
| int WebkitFileStreamWriterImpl::Write(net::IOBuffer* buf, |
| int buf_len, |
| const net::CompletionCallback& callback) { |
| DCHECK(pending_write_callback_.is_null()); |
| DCHECK(pending_cancel_callback_.is_null()); |
| DCHECK(!callback.is_null()); |
| |
| // If the local file is already available, just delegate to it. |
| if (local_file_writer_) |
| return local_file_writer_->Write(buf, buf_len, callback); |
| |
| // The local file is not yet ready. Create the writable snapshot. |
| if (file_path_.empty()) |
| return net::ERR_FILE_NOT_FOUND; |
| |
| pending_write_callback_ = callback; |
| CreateWritableSnapshotFile( |
| file_system_getter_, file_path_, |
| base::Bind( |
| &WebkitFileStreamWriterImpl::WriteAfterCreateWritableSnapshotFile, |
| weak_ptr_factory_.GetWeakPtr(), make_scoped_refptr(buf), buf_len)); |
| return net::ERR_IO_PENDING; |
| } |
| |
| int WebkitFileStreamWriterImpl::Cancel( |
| const net::CompletionCallback& callback) { |
| DCHECK(pending_cancel_callback_.is_null()); |
| DCHECK(!callback.is_null()); |
| |
| // If LocalFileWriter is already created, just delegate the cancel to it. |
| if (local_file_writer_) |
| return local_file_writer_->Cancel(callback); |
| |
| // If file open operation is in-flight, wait for its completion and cancel |
| // further write operation in WriteAfterCreateWritableSnapshotFile. |
| if (!pending_write_callback_.is_null()) { |
| // Dismiss pending write callback immediately. |
| pending_write_callback_.Reset(); |
| pending_cancel_callback_ = callback; |
| return net::ERR_IO_PENDING; |
| } |
| |
| // Write() is not called yet. |
| return net::ERR_UNEXPECTED; |
| } |
| |
| int WebkitFileStreamWriterImpl::Flush(const net::CompletionCallback& callback) { |
| DCHECK(pending_cancel_callback_.is_null()); |
| DCHECK(!callback.is_null()); |
| |
| // If LocalFileWriter is already created, just delegate to it. |
| if (local_file_writer_) |
| return local_file_writer_->Flush(callback); |
| |
| // There shouldn't be in-flight Write operation. |
| DCHECK(pending_write_callback_.is_null()); |
| |
| // Here is the case Flush() is called before any Write() invocation. |
| // Do nothing. |
| // Synchronization to the remote server is not done until the file is closed. |
| return net::OK; |
| } |
| |
| void WebkitFileStreamWriterImpl::WriteAfterCreateWritableSnapshotFile( |
| net::IOBuffer* buf, |
| int buf_len, |
| base::PlatformFileError open_result, |
| const base::FilePath& local_path, |
| const base::Closure& close_callback_on_ui_thread) { |
| DCHECK(!local_file_writer_); |
| |
| if (!pending_cancel_callback_.is_null()) { |
| DCHECK(pending_write_callback_.is_null()); |
| // Cancel() is called during the creation of the snapshot file. |
| // Don't write to the file. |
| if (open_result == base::PLATFORM_FILE_OK) { |
| // Here the file is internally created. To revert the operation, close |
| // the file. |
| DCHECK(!close_callback_on_ui_thread.is_null()); |
| BrowserThread::PostTask(BrowserThread::UI, |
| FROM_HERE, |
| close_callback_on_ui_thread); |
| } |
| |
| base::ResetAndReturn(&pending_cancel_callback_).Run(net::OK); |
| return; |
| } |
| |
| DCHECK(!pending_write_callback_.is_null()); |
| |
| const net::CompletionCallback callback = |
| base::ResetAndReturn(&pending_write_callback_); |
| if (open_result != base::PLATFORM_FILE_OK) { |
| DCHECK(close_callback_on_ui_thread.is_null()); |
| callback.Run(net::PlatformFileErrorToNetError(open_result)); |
| return; |
| } |
| |
| // Keep |close_callback| to close the file when the stream is destructed. |
| DCHECK(!close_callback_on_ui_thread.is_null()); |
| close_callback_on_ui_thread_ = close_callback_on_ui_thread; |
| local_file_writer_.reset(fileapi::FileStreamWriter::CreateForLocalFile( |
| file_task_runner_.get(), local_path, offset_)); |
| int result = local_file_writer_->Write(buf, buf_len, callback); |
| if (result != net::ERR_IO_PENDING) |
| callback.Run(result); |
| } |
| |
| } // namespace internal |
| } // namespace drive |