| // Copyright (c) 2012 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 "storage/browser/fileapi/local_file_stream_writer.h" |
| |
| #include <stdint.h> |
| |
| #include "net/base/file_stream.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| |
| namespace storage { |
| |
| namespace { |
| |
| const int kOpenFlagsForWrite = base::File::FLAG_OPEN | |
| base::File::FLAG_WRITE | |
| base::File::FLAG_ASYNC; |
| const int kCreateFlagsForWrite = base::File::FLAG_CREATE | |
| base::File::FLAG_WRITE | |
| base::File::FLAG_ASYNC; |
| |
| } // namespace |
| |
| FileStreamWriter* FileStreamWriter::CreateForLocalFile( |
| base::TaskRunner* task_runner, |
| const base::FilePath& file_path, |
| int64_t initial_offset, |
| OpenOrCreate open_or_create) { |
| return new LocalFileStreamWriter( |
| task_runner, file_path, initial_offset, open_or_create); |
| } |
| |
| LocalFileStreamWriter::~LocalFileStreamWriter() { |
| // Invalidate weak pointers so that we won't receive any callbacks from |
| // in-flight stream operations, which might be triggered during the file close |
| // in the FileStream destructor. |
| weak_factory_.InvalidateWeakPtrs(); |
| |
| // FileStream's destructor closes the file safely, since we opened the file |
| // by its Open() method. |
| } |
| |
| int LocalFileStreamWriter::Write(net::IOBuffer* buf, |
| int buf_len, |
| net::CompletionOnceCallback callback) { |
| DCHECK(!has_pending_operation_); |
| DCHECK(write_callback_.is_null()); |
| DCHECK(cancel_callback_.is_null()); |
| |
| has_pending_operation_ = true; |
| write_callback_ = std::move(callback); |
| if (stream_impl_) { |
| int result = InitiateWrite(buf, buf_len); |
| if (result != net::ERR_IO_PENDING) |
| has_pending_operation_ = false; |
| return result; |
| } |
| return InitiateOpen(base::BindOnce(&LocalFileStreamWriter::ReadyToWrite, |
| weak_factory_.GetWeakPtr(), |
| base::RetainedRef(buf), buf_len)); |
| } |
| |
| int LocalFileStreamWriter::Cancel(net::CompletionOnceCallback callback) { |
| if (!has_pending_operation_) |
| return net::ERR_UNEXPECTED; |
| |
| DCHECK(!callback.is_null()); |
| cancel_callback_ = std::move(callback); |
| return net::ERR_IO_PENDING; |
| } |
| |
| int LocalFileStreamWriter::Flush(net::CompletionOnceCallback callback) { |
| DCHECK(!has_pending_operation_); |
| DCHECK(cancel_callback_.is_null()); |
| |
| // Write() is not called yet, so there's nothing to flush. |
| if (!stream_impl_) |
| return net::OK; |
| |
| has_pending_operation_ = true; |
| int result = InitiateFlush(std::move(callback)); |
| if (result != net::ERR_IO_PENDING) |
| has_pending_operation_ = false; |
| return result; |
| } |
| |
| LocalFileStreamWriter::LocalFileStreamWriter(base::TaskRunner* task_runner, |
| const base::FilePath& file_path, |
| int64_t initial_offset, |
| OpenOrCreate open_or_create) |
| : file_path_(file_path), |
| open_or_create_(open_or_create), |
| initial_offset_(initial_offset), |
| task_runner_(task_runner), |
| has_pending_operation_(false), |
| weak_factory_(this) {} |
| |
| int LocalFileStreamWriter::InitiateOpen(base::OnceClosure main_operation) { |
| DCHECK(has_pending_operation_); |
| DCHECK(!stream_impl_.get()); |
| |
| stream_impl_.reset(new net::FileStream(task_runner_)); |
| |
| int open_flags = 0; |
| switch (open_or_create_) { |
| case OPEN_EXISTING_FILE: |
| open_flags = kOpenFlagsForWrite; |
| break; |
| case CREATE_NEW_FILE: |
| open_flags = kCreateFlagsForWrite; |
| break; |
| } |
| |
| return stream_impl_->Open( |
| file_path_, open_flags, |
| base::BindOnce(&LocalFileStreamWriter::DidOpen, |
| weak_factory_.GetWeakPtr(), std::move(main_operation))); |
| } |
| |
| void LocalFileStreamWriter::DidOpen(base::OnceClosure main_operation, |
| int result) { |
| DCHECK(has_pending_operation_); |
| DCHECK(stream_impl_.get()); |
| |
| if (CancelIfRequested()) |
| return; |
| |
| if (result != net::OK) { |
| has_pending_operation_ = false; |
| stream_impl_.reset(nullptr); |
| std::move(write_callback_).Run(result); |
| return; |
| } |
| |
| InitiateSeek(std::move(main_operation)); |
| } |
| |
| void LocalFileStreamWriter::InitiateSeek(base::OnceClosure main_operation) { |
| DCHECK(has_pending_operation_); |
| DCHECK(stream_impl_.get()); |
| |
| if (initial_offset_ == 0) { |
| // No need to seek. |
| std::move(main_operation).Run(); |
| return; |
| } |
| |
| int result = stream_impl_->Seek( |
| initial_offset_, |
| base::BindOnce(&LocalFileStreamWriter::DidSeek, |
| weak_factory_.GetWeakPtr(), std::move(main_operation))); |
| if (result != net::ERR_IO_PENDING) { |
| has_pending_operation_ = false; |
| std::move(write_callback_).Run(result); |
| } |
| } |
| |
| void LocalFileStreamWriter::DidSeek(base::OnceClosure main_operation, |
| int64_t result) { |
| DCHECK(has_pending_operation_); |
| |
| if (CancelIfRequested()) |
| return; |
| |
| if (result != initial_offset_) { |
| // TODO(kinaba) add a more specific error code. |
| result = net::ERR_FAILED; |
| } |
| |
| if (result < 0) { |
| has_pending_operation_ = false; |
| std::move(write_callback_).Run(static_cast<int>(result)); |
| return; |
| } |
| |
| std::move(main_operation).Run(); |
| } |
| |
| void LocalFileStreamWriter::ReadyToWrite(net::IOBuffer* buf, int buf_len) { |
| DCHECK(has_pending_operation_); |
| |
| int result = InitiateWrite(buf, buf_len); |
| if (result != net::ERR_IO_PENDING) { |
| has_pending_operation_ = false; |
| std::move(write_callback_).Run(result); |
| } |
| } |
| |
| int LocalFileStreamWriter::InitiateWrite(net::IOBuffer* buf, int buf_len) { |
| DCHECK(has_pending_operation_); |
| DCHECK(stream_impl_.get()); |
| |
| return stream_impl_->Write(buf, buf_len, |
| base::BindOnce(&LocalFileStreamWriter::DidWrite, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void LocalFileStreamWriter::DidWrite(int result) { |
| DCHECK(has_pending_operation_); |
| |
| if (CancelIfRequested()) |
| return; |
| has_pending_operation_ = false; |
| std::move(write_callback_).Run(result); |
| } |
| |
| int LocalFileStreamWriter::InitiateFlush(net::CompletionOnceCallback callback) { |
| DCHECK(has_pending_operation_); |
| DCHECK(stream_impl_.get()); |
| |
| return stream_impl_->Flush(base::BindOnce(&LocalFileStreamWriter::DidFlush, |
| weak_factory_.GetWeakPtr(), |
| std::move(callback))); |
| } |
| |
| void LocalFileStreamWriter::DidFlush(net::CompletionOnceCallback callback, |
| int result) { |
| DCHECK(has_pending_operation_); |
| |
| if (CancelIfRequested()) |
| return; |
| has_pending_operation_ = false; |
| std::move(callback).Run(result); |
| } |
| |
| bool LocalFileStreamWriter::CancelIfRequested() { |
| DCHECK(has_pending_operation_); |
| |
| if (cancel_callback_.is_null()) |
| return false; |
| |
| has_pending_operation_ = false; |
| std::move(cancel_callback_).Run(net::OK); |
| return true; |
| } |
| |
| } // namespace storage |