We started redesigning GpuMemoryBuffer interface to handle multiple buffers [0].
[chromium-blink-merge.git] / storage / browser / fileapi / file_writer_delegate.cc
blobabc9976e6d5be4ece24ee745ea86ab69464fb74d
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "storage/browser/fileapi/file_writer_delegate.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/files/file_util_proxy.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "net/base/net_errors.h"
16 #include "storage/browser/fileapi/file_stream_writer.h"
17 #include "storage/browser/fileapi/file_system_context.h"
18 #include "storage/common/fileapi/file_system_mount_option.h"
19 #include "storage/common/fileapi/file_system_util.h"
21 namespace storage {
23 static const int kReadBufSize = 32768;
25 FileWriterDelegate::FileWriterDelegate(
26 scoped_ptr<FileStreamWriter> file_stream_writer,
27 FlushPolicy flush_policy)
28 : file_stream_writer_(file_stream_writer.Pass()),
29 writing_started_(false),
30 flush_policy_(flush_policy),
31 bytes_written_backlog_(0),
32 bytes_written_(0),
33 bytes_read_(0),
34 io_buffer_(new net::IOBufferWithSize(kReadBufSize)),
35 weak_factory_(this) {
38 FileWriterDelegate::~FileWriterDelegate() {
41 void FileWriterDelegate::Start(scoped_ptr<net::URLRequest> request,
42 const DelegateWriteCallback& write_callback) {
43 write_callback_ = write_callback;
44 request_ = request.Pass();
45 request_->Start();
48 void FileWriterDelegate::Cancel() {
49 if (request_) {
50 // This halts any callbacks on this delegate.
51 request_->set_delegate(NULL);
52 request_->Cancel();
55 const int status = file_stream_writer_->Cancel(
56 base::Bind(&FileWriterDelegate::OnWriteCancelled,
57 weak_factory_.GetWeakPtr()));
58 // Return true to finish immediately if we have no pending writes.
59 // Otherwise we'll do the final cleanup in the Cancel callback.
60 if (status != net::ERR_IO_PENDING) {
61 write_callback_.Run(base::File::FILE_ERROR_ABORT, 0,
62 GetCompletionStatusOnError());
66 void FileWriterDelegate::OnReceivedRedirect(
67 net::URLRequest* request,
68 const net::RedirectInfo& redirect_info,
69 bool* defer_redirect) {
70 NOTREACHED();
71 OnError(base::File::FILE_ERROR_SECURITY);
74 void FileWriterDelegate::OnAuthRequired(net::URLRequest* request,
75 net::AuthChallengeInfo* auth_info) {
76 NOTREACHED();
77 OnError(base::File::FILE_ERROR_SECURITY);
80 void FileWriterDelegate::OnCertificateRequested(
81 net::URLRequest* request,
82 net::SSLCertRequestInfo* cert_request_info) {
83 NOTREACHED();
84 OnError(base::File::FILE_ERROR_SECURITY);
87 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest* request,
88 const net::SSLInfo& ssl_info,
89 bool fatal) {
90 NOTREACHED();
91 OnError(base::File::FILE_ERROR_SECURITY);
94 void FileWriterDelegate::OnResponseStarted(net::URLRequest* request) {
95 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
96 tracked_objects::ScopedTracker tracking_profile(
97 FROM_HERE_WITH_EXPLICIT_FUNCTION(
98 "423948 FileWriterDelegate::OnResponseStarted"));
100 DCHECK_EQ(request_.get(), request);
101 if (!request->status().is_success() || request->GetResponseCode() != 200) {
102 OnError(base::File::FILE_ERROR_FAILED);
103 return;
105 Read();
108 void FileWriterDelegate::OnReadCompleted(net::URLRequest* request,
109 int bytes_read) {
110 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
111 tracked_objects::ScopedTracker tracking_profile(
112 FROM_HERE_WITH_EXPLICIT_FUNCTION(
113 "423948 FileWriterDelegate::OnReadCompleted"));
115 DCHECK_EQ(request_.get(), request);
116 if (!request->status().is_success()) {
117 OnError(base::File::FILE_ERROR_FAILED);
118 return;
120 OnDataReceived(bytes_read);
123 void FileWriterDelegate::Read() {
124 bytes_written_ = 0;
125 bytes_read_ = 0;
126 if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) {
127 base::MessageLoop::current()->PostTask(
128 FROM_HERE,
129 base::Bind(&FileWriterDelegate::OnDataReceived,
130 weak_factory_.GetWeakPtr(), bytes_read_));
131 } else if (!request_->status().is_io_pending()) {
132 OnError(base::File::FILE_ERROR_FAILED);
136 void FileWriterDelegate::OnDataReceived(int bytes_read) {
137 bytes_read_ = bytes_read;
138 if (!bytes_read_) { // We're done.
139 OnProgress(0, true);
140 } else {
141 // This could easily be optimized to rotate between a pool of buffers, so
142 // that we could read and write at the same time. It's not yet clear that
143 // it's necessary.
144 cursor_ = new net::DrainableIOBuffer(io_buffer_.get(), bytes_read_);
145 Write();
149 void FileWriterDelegate::Write() {
150 writing_started_ = true;
151 int64 bytes_to_write = bytes_read_ - bytes_written_;
152 int write_response =
153 file_stream_writer_->Write(cursor_.get(),
154 static_cast<int>(bytes_to_write),
155 base::Bind(&FileWriterDelegate::OnDataWritten,
156 weak_factory_.GetWeakPtr()));
157 if (write_response > 0) {
158 base::MessageLoop::current()->PostTask(
159 FROM_HERE,
160 base::Bind(&FileWriterDelegate::OnDataWritten,
161 weak_factory_.GetWeakPtr(), write_response));
162 } else if (net::ERR_IO_PENDING != write_response) {
163 OnError(NetErrorToFileError(write_response));
167 void FileWriterDelegate::OnDataWritten(int write_response) {
168 if (write_response > 0) {
169 OnProgress(write_response, false);
170 cursor_->DidConsume(write_response);
171 bytes_written_ += write_response;
172 if (bytes_written_ == bytes_read_)
173 Read();
174 else
175 Write();
176 } else {
177 OnError(NetErrorToFileError(write_response));
181 FileWriterDelegate::WriteProgressStatus
182 FileWriterDelegate::GetCompletionStatusOnError() const {
183 return writing_started_ ? ERROR_WRITE_STARTED : ERROR_WRITE_NOT_STARTED;
186 void FileWriterDelegate::OnError(base::File::Error error) {
187 if (request_) {
188 request_->set_delegate(NULL);
189 request_->Cancel();
192 if (writing_started_)
193 MaybeFlushForCompletion(error, 0, ERROR_WRITE_STARTED);
194 else
195 write_callback_.Run(error, 0, ERROR_WRITE_NOT_STARTED);
198 void FileWriterDelegate::OnProgress(int bytes_written, bool done) {
199 DCHECK(bytes_written + bytes_written_backlog_ >= bytes_written_backlog_);
200 static const int kMinProgressDelayMS = 200;
201 base::Time currentTime = base::Time::Now();
202 if (done || last_progress_event_time_.is_null() ||
203 (currentTime - last_progress_event_time_).InMilliseconds() >
204 kMinProgressDelayMS) {
205 bytes_written += bytes_written_backlog_;
206 last_progress_event_time_ = currentTime;
207 bytes_written_backlog_ = 0;
209 if (done) {
210 MaybeFlushForCompletion(base::File::FILE_OK, bytes_written,
211 SUCCESS_COMPLETED);
212 } else {
213 write_callback_.Run(base::File::FILE_OK, bytes_written,
214 SUCCESS_IO_PENDING);
216 return;
218 bytes_written_backlog_ += bytes_written;
221 void FileWriterDelegate::OnWriteCancelled(int status) {
222 write_callback_.Run(base::File::FILE_ERROR_ABORT, 0,
223 GetCompletionStatusOnError());
226 void FileWriterDelegate::MaybeFlushForCompletion(
227 base::File::Error error,
228 int bytes_written,
229 WriteProgressStatus progress_status) {
230 if (flush_policy_ == FlushPolicy::NO_FLUSH_ON_COMPLETION) {
231 write_callback_.Run(error, bytes_written, progress_status);
232 return;
234 // DCHECK_EQ on enum classes is not supported.
235 DCHECK(flush_policy_ == FlushPolicy::FLUSH_ON_COMPLETION);
237 int flush_error = file_stream_writer_->Flush(
238 base::Bind(&FileWriterDelegate::OnFlushed, weak_factory_.GetWeakPtr(),
239 error, bytes_written, progress_status));
240 if (flush_error != net::ERR_IO_PENDING)
241 OnFlushed(error, bytes_written, progress_status, flush_error);
244 void FileWriterDelegate::OnFlushed(base::File::Error error,
245 int bytes_written,
246 WriteProgressStatus progress_status,
247 int flush_error) {
248 if (error == base::File::FILE_OK && flush_error != net::OK) {
249 // If the Flush introduced an error, overwrite the status.
250 // Otherwise, keep the original error status.
251 error = NetErrorToFileError(flush_error);
252 progress_status = GetCompletionStatusOnError();
254 write_callback_.Run(error, bytes_written, progress_status);
257 } // namespace storage