Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / storage / browser / fileapi / file_writer_delegate.cc
blobab264d3b517fc1919a362e6750557c3b52dde6ec
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/sequenced_task_runner.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "net/base/net_errors.h"
14 #include "storage/browser/fileapi/file_stream_writer.h"
15 #include "storage/browser/fileapi/file_system_context.h"
16 #include "storage/common/fileapi/file_system_mount_option.h"
17 #include "storage/common/fileapi/file_system_util.h"
19 namespace storage {
21 static const int kReadBufSize = 32768;
23 FileWriterDelegate::FileWriterDelegate(
24 scoped_ptr<FileStreamWriter> file_stream_writer,
25 FlushPolicy flush_policy)
26 : file_stream_writer_(file_stream_writer.Pass()),
27 writing_started_(false),
28 flush_policy_(flush_policy),
29 bytes_written_backlog_(0),
30 bytes_written_(0),
31 bytes_read_(0),
32 io_buffer_(new net::IOBufferWithSize(kReadBufSize)),
33 weak_factory_(this) {
36 FileWriterDelegate::~FileWriterDelegate() {
39 void FileWriterDelegate::Start(scoped_ptr<net::URLRequest> request,
40 const DelegateWriteCallback& write_callback) {
41 write_callback_ = write_callback;
42 request_ = request.Pass();
43 request_->Start();
46 void FileWriterDelegate::Cancel() {
47 if (request_) {
48 // This halts any callbacks on this delegate.
49 request_->set_delegate(NULL);
50 request_->Cancel();
53 const int status = file_stream_writer_->Cancel(
54 base::Bind(&FileWriterDelegate::OnWriteCancelled,
55 weak_factory_.GetWeakPtr()));
56 // Return true to finish immediately if we have no pending writes.
57 // Otherwise we'll do the final cleanup in the Cancel callback.
58 if (status != net::ERR_IO_PENDING) {
59 write_callback_.Run(base::File::FILE_ERROR_ABORT, 0,
60 GetCompletionStatusOnError());
64 void FileWriterDelegate::OnReceivedRedirect(
65 net::URLRequest* request,
66 const net::RedirectInfo& redirect_info,
67 bool* defer_redirect) {
68 NOTREACHED();
69 OnError(base::File::FILE_ERROR_SECURITY);
72 void FileWriterDelegate::OnAuthRequired(net::URLRequest* request,
73 net::AuthChallengeInfo* auth_info) {
74 NOTREACHED();
75 OnError(base::File::FILE_ERROR_SECURITY);
78 void FileWriterDelegate::OnCertificateRequested(
79 net::URLRequest* request,
80 net::SSLCertRequestInfo* cert_request_info) {
81 NOTREACHED();
82 OnError(base::File::FILE_ERROR_SECURITY);
85 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest* request,
86 const net::SSLInfo& ssl_info,
87 bool fatal) {
88 NOTREACHED();
89 OnError(base::File::FILE_ERROR_SECURITY);
92 void FileWriterDelegate::OnResponseStarted(net::URLRequest* request) {
93 DCHECK_EQ(request_.get(), request);
94 if (!request->status().is_success() || request->GetResponseCode() != 200) {
95 OnError(base::File::FILE_ERROR_FAILED);
96 return;
98 Read();
101 void FileWriterDelegate::OnReadCompleted(net::URLRequest* request,
102 int bytes_read) {
103 DCHECK_EQ(request_.get(), request);
104 if (!request->status().is_success()) {
105 OnError(base::File::FILE_ERROR_FAILED);
106 return;
108 OnDataReceived(bytes_read);
111 void FileWriterDelegate::Read() {
112 bytes_written_ = 0;
113 bytes_read_ = 0;
114 if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) {
115 base::MessageLoop::current()->PostTask(
116 FROM_HERE,
117 base::Bind(&FileWriterDelegate::OnDataReceived,
118 weak_factory_.GetWeakPtr(), bytes_read_));
119 } else if (!request_->status().is_io_pending()) {
120 OnError(base::File::FILE_ERROR_FAILED);
124 void FileWriterDelegate::OnDataReceived(int bytes_read) {
125 bytes_read_ = bytes_read;
126 if (!bytes_read_) { // We're done.
127 OnProgress(0, true);
128 } else {
129 // This could easily be optimized to rotate between a pool of buffers, so
130 // that we could read and write at the same time. It's not yet clear that
131 // it's necessary.
132 cursor_ = new net::DrainableIOBuffer(io_buffer_.get(), bytes_read_);
133 Write();
137 void FileWriterDelegate::Write() {
138 writing_started_ = true;
139 int64 bytes_to_write = bytes_read_ - bytes_written_;
140 int write_response =
141 file_stream_writer_->Write(cursor_.get(),
142 static_cast<int>(bytes_to_write),
143 base::Bind(&FileWriterDelegate::OnDataWritten,
144 weak_factory_.GetWeakPtr()));
145 if (write_response > 0) {
146 base::MessageLoop::current()->PostTask(
147 FROM_HERE,
148 base::Bind(&FileWriterDelegate::OnDataWritten,
149 weak_factory_.GetWeakPtr(), write_response));
150 } else if (net::ERR_IO_PENDING != write_response) {
151 OnError(NetErrorToFileError(write_response));
155 void FileWriterDelegate::OnDataWritten(int write_response) {
156 if (write_response > 0) {
157 OnProgress(write_response, false);
158 cursor_->DidConsume(write_response);
159 bytes_written_ += write_response;
160 if (bytes_written_ == bytes_read_)
161 Read();
162 else
163 Write();
164 } else {
165 OnError(NetErrorToFileError(write_response));
169 FileWriterDelegate::WriteProgressStatus
170 FileWriterDelegate::GetCompletionStatusOnError() const {
171 return writing_started_ ? ERROR_WRITE_STARTED : ERROR_WRITE_NOT_STARTED;
174 void FileWriterDelegate::OnError(base::File::Error error) {
175 if (request_) {
176 request_->set_delegate(NULL);
177 request_->Cancel();
180 if (writing_started_)
181 MaybeFlushForCompletion(error, 0, ERROR_WRITE_STARTED);
182 else
183 write_callback_.Run(error, 0, ERROR_WRITE_NOT_STARTED);
186 void FileWriterDelegate::OnProgress(int bytes_written, bool done) {
187 DCHECK(bytes_written + bytes_written_backlog_ >= bytes_written_backlog_);
188 static const int kMinProgressDelayMS = 200;
189 base::Time currentTime = base::Time::Now();
190 if (done || last_progress_event_time_.is_null() ||
191 (currentTime - last_progress_event_time_).InMilliseconds() >
192 kMinProgressDelayMS) {
193 bytes_written += bytes_written_backlog_;
194 last_progress_event_time_ = currentTime;
195 bytes_written_backlog_ = 0;
197 if (done) {
198 MaybeFlushForCompletion(base::File::FILE_OK, bytes_written,
199 SUCCESS_COMPLETED);
200 } else {
201 write_callback_.Run(base::File::FILE_OK, bytes_written,
202 SUCCESS_IO_PENDING);
204 return;
206 bytes_written_backlog_ += bytes_written;
209 void FileWriterDelegate::OnWriteCancelled(int status) {
210 write_callback_.Run(base::File::FILE_ERROR_ABORT, 0,
211 GetCompletionStatusOnError());
214 void FileWriterDelegate::MaybeFlushForCompletion(
215 base::File::Error error,
216 int bytes_written,
217 WriteProgressStatus progress_status) {
218 if (flush_policy_ == FlushPolicy::NO_FLUSH_ON_COMPLETION) {
219 write_callback_.Run(error, bytes_written, progress_status);
220 return;
222 // DCHECK_EQ on enum classes is not supported.
223 DCHECK(flush_policy_ == FlushPolicy::FLUSH_ON_COMPLETION);
225 int flush_error = file_stream_writer_->Flush(
226 base::Bind(&FileWriterDelegate::OnFlushed, weak_factory_.GetWeakPtr(),
227 error, bytes_written, progress_status));
228 if (flush_error != net::ERR_IO_PENDING)
229 OnFlushed(error, bytes_written, progress_status, flush_error);
232 void FileWriterDelegate::OnFlushed(base::File::Error error,
233 int bytes_written,
234 WriteProgressStatus progress_status,
235 int flush_error) {
236 if (error == base::File::FILE_OK && flush_error != net::OK) {
237 // If the Flush introduced an error, overwrite the status.
238 // Otherwise, keep the original error status.
239 error = NetErrorToFileError(flush_error);
240 progress_status = GetCompletionStatusOnError();
242 write_callback_.Run(error, bytes_written, progress_status);
245 } // namespace storage