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"
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/sequenced_task_runner.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "net/base/net_errors.h"
15 #include "storage/browser/fileapi/file_stream_writer.h"
16 #include "storage/browser/fileapi/file_system_context.h"
17 #include "storage/common/fileapi/file_system_mount_option.h"
18 #include "storage/common/fileapi/file_system_util.h"
22 static const int kReadBufSize
= 32768;
24 FileWriterDelegate::FileWriterDelegate(
25 scoped_ptr
<FileStreamWriter
> file_stream_writer
,
26 FlushPolicy flush_policy
)
27 : file_stream_writer_(file_stream_writer
.Pass()),
28 writing_started_(false),
29 flush_policy_(flush_policy
),
30 bytes_written_backlog_(0),
33 io_buffer_(new net::IOBufferWithSize(kReadBufSize
)),
37 FileWriterDelegate::~FileWriterDelegate() {
40 void FileWriterDelegate::Start(scoped_ptr
<net::URLRequest
> request
,
41 const DelegateWriteCallback
& write_callback
) {
42 write_callback_
= write_callback
;
43 request_
= request
.Pass();
47 void FileWriterDelegate::Cancel() {
49 // This halts any callbacks on this delegate.
50 request_
->set_delegate(NULL
);
54 const int status
= file_stream_writer_
->Cancel(
55 base::Bind(&FileWriterDelegate::OnWriteCancelled
,
56 weak_factory_
.GetWeakPtr()));
57 // Return true to finish immediately if we have no pending writes.
58 // Otherwise we'll do the final cleanup in the Cancel callback.
59 if (status
!= net::ERR_IO_PENDING
) {
60 write_callback_
.Run(base::File::FILE_ERROR_ABORT
, 0,
61 GetCompletionStatusOnError());
65 void FileWriterDelegate::OnReceivedRedirect(
66 net::URLRequest
* request
,
67 const net::RedirectInfo
& redirect_info
,
68 bool* defer_redirect
) {
70 OnError(base::File::FILE_ERROR_SECURITY
);
73 void FileWriterDelegate::OnAuthRequired(net::URLRequest
* request
,
74 net::AuthChallengeInfo
* auth_info
) {
76 OnError(base::File::FILE_ERROR_SECURITY
);
79 void FileWriterDelegate::OnCertificateRequested(
80 net::URLRequest
* request
,
81 net::SSLCertRequestInfo
* cert_request_info
) {
83 OnError(base::File::FILE_ERROR_SECURITY
);
86 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest
* request
,
87 const net::SSLInfo
& ssl_info
,
90 OnError(base::File::FILE_ERROR_SECURITY
);
93 void FileWriterDelegate::OnResponseStarted(net::URLRequest
* request
) {
94 DCHECK_EQ(request_
.get(), request
);
95 if (!request
->status().is_success() || request
->GetResponseCode() != 200) {
96 OnError(base::File::FILE_ERROR_FAILED
);
102 void FileWriterDelegate::OnReadCompleted(net::URLRequest
* request
,
104 DCHECK_EQ(request_
.get(), request
);
105 if (!request
->status().is_success()) {
106 OnError(base::File::FILE_ERROR_FAILED
);
109 OnDataReceived(bytes_read
);
112 void FileWriterDelegate::Read() {
115 if (request_
->Read(io_buffer_
.get(), io_buffer_
->size(), &bytes_read_
)) {
116 base::MessageLoop::current()->PostTask(
118 base::Bind(&FileWriterDelegate::OnDataReceived
,
119 weak_factory_
.GetWeakPtr(), bytes_read_
));
120 } else if (!request_
->status().is_io_pending()) {
121 OnError(base::File::FILE_ERROR_FAILED
);
125 void FileWriterDelegate::OnDataReceived(int bytes_read
) {
126 bytes_read_
= bytes_read
;
127 if (!bytes_read_
) { // We're done.
130 // This could easily be optimized to rotate between a pool of buffers, so
131 // that we could read and write at the same time. It's not yet clear that
133 cursor_
= new net::DrainableIOBuffer(io_buffer_
.get(), bytes_read_
);
138 void FileWriterDelegate::Write() {
139 writing_started_
= true;
140 int64 bytes_to_write
= bytes_read_
- bytes_written_
;
142 file_stream_writer_
->Write(cursor_
.get(),
143 static_cast<int>(bytes_to_write
),
144 base::Bind(&FileWriterDelegate::OnDataWritten
,
145 weak_factory_
.GetWeakPtr()));
146 if (write_response
> 0) {
147 base::MessageLoop::current()->PostTask(
149 base::Bind(&FileWriterDelegate::OnDataWritten
,
150 weak_factory_
.GetWeakPtr(), write_response
));
151 } else if (net::ERR_IO_PENDING
!= write_response
) {
152 OnError(NetErrorToFileError(write_response
));
156 void FileWriterDelegate::OnDataWritten(int write_response
) {
157 if (write_response
> 0) {
158 OnProgress(write_response
, false);
159 cursor_
->DidConsume(write_response
);
160 bytes_written_
+= write_response
;
161 if (bytes_written_
== bytes_read_
)
166 OnError(NetErrorToFileError(write_response
));
170 FileWriterDelegate::WriteProgressStatus
171 FileWriterDelegate::GetCompletionStatusOnError() const {
172 return writing_started_
? ERROR_WRITE_STARTED
: ERROR_WRITE_NOT_STARTED
;
175 void FileWriterDelegate::OnError(base::File::Error error
) {
177 request_
->set_delegate(NULL
);
181 if (writing_started_
)
182 MaybeFlushForCompletion(error
, 0, ERROR_WRITE_STARTED
);
184 write_callback_
.Run(error
, 0, ERROR_WRITE_NOT_STARTED
);
187 void FileWriterDelegate::OnProgress(int bytes_written
, bool done
) {
188 DCHECK(bytes_written
+ bytes_written_backlog_
>= bytes_written_backlog_
);
189 static const int kMinProgressDelayMS
= 200;
190 base::Time currentTime
= base::Time::Now();
191 if (done
|| last_progress_event_time_
.is_null() ||
192 (currentTime
- last_progress_event_time_
).InMilliseconds() >
193 kMinProgressDelayMS
) {
194 bytes_written
+= bytes_written_backlog_
;
195 last_progress_event_time_
= currentTime
;
196 bytes_written_backlog_
= 0;
199 MaybeFlushForCompletion(base::File::FILE_OK
, bytes_written
,
202 write_callback_
.Run(base::File::FILE_OK
, bytes_written
,
207 bytes_written_backlog_
+= bytes_written
;
210 void FileWriterDelegate::OnWriteCancelled(int status
) {
211 write_callback_
.Run(base::File::FILE_ERROR_ABORT
, 0,
212 GetCompletionStatusOnError());
215 void FileWriterDelegate::MaybeFlushForCompletion(
216 base::File::Error error
,
218 WriteProgressStatus progress_status
) {
219 if (flush_policy_
== FlushPolicy::NO_FLUSH_ON_COMPLETION
) {
220 write_callback_
.Run(error
, bytes_written
, progress_status
);
223 // DCHECK_EQ on enum classes is not supported.
224 DCHECK(flush_policy_
== FlushPolicy::FLUSH_ON_COMPLETION
);
226 int flush_error
= file_stream_writer_
->Flush(
227 base::Bind(&FileWriterDelegate::OnFlushed
, weak_factory_
.GetWeakPtr(),
228 error
, bytes_written
, progress_status
));
229 if (flush_error
!= net::ERR_IO_PENDING
)
230 OnFlushed(error
, bytes_written
, progress_status
, flush_error
);
233 void FileWriterDelegate::OnFlushed(base::File::Error error
,
235 WriteProgressStatus progress_status
,
237 if (error
== base::File::FILE_OK
&& flush_error
!= net::OK
) {
238 // If the Flush introduced an error, overwrite the status.
239 // Otherwise, keep the original error status.
240 error
= NetErrorToFileError(flush_error
);
241 progress_status
= GetCompletionStatusOnError();
243 write_callback_
.Run(error
, bytes_written
, progress_status
);
246 } // namespace storage