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/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"
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),
32 io_buffer_(new net::IOBufferWithSize(kReadBufSize
)),
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();
46 void FileWriterDelegate::Cancel() {
48 // This halts any callbacks on this delegate.
49 request_
->set_delegate(NULL
);
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
) {
69 OnError(base::File::FILE_ERROR_SECURITY
);
72 void FileWriterDelegate::OnAuthRequired(net::URLRequest
* request
,
73 net::AuthChallengeInfo
* auth_info
) {
75 OnError(base::File::FILE_ERROR_SECURITY
);
78 void FileWriterDelegate::OnCertificateRequested(
79 net::URLRequest
* request
,
80 net::SSLCertRequestInfo
* cert_request_info
) {
82 OnError(base::File::FILE_ERROR_SECURITY
);
85 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest
* request
,
86 const net::SSLInfo
& ssl_info
,
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
);
101 void FileWriterDelegate::OnReadCompleted(net::URLRequest
* request
,
103 DCHECK_EQ(request_
.get(), request
);
104 if (!request
->status().is_success()) {
105 OnError(base::File::FILE_ERROR_FAILED
);
108 OnDataReceived(bytes_read
);
111 void FileWriterDelegate::Read() {
114 if (request_
->Read(io_buffer_
.get(), io_buffer_
->size(), &bytes_read_
)) {
115 base::MessageLoop::current()->PostTask(
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.
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
132 cursor_
= new net::DrainableIOBuffer(io_buffer_
.get(), bytes_read_
);
137 void FileWriterDelegate::Write() {
138 writing_started_
= true;
139 int64 bytes_to_write
= bytes_read_
- bytes_written_
;
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(
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_
)
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
) {
176 request_
->set_delegate(NULL
);
180 if (writing_started_
)
181 MaybeFlushForCompletion(error
, 0, ERROR_WRITE_STARTED
);
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;
198 MaybeFlushForCompletion(base::File::FILE_OK
, bytes_written
,
201 write_callback_
.Run(base::File::FILE_OK
, bytes_written
,
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
,
217 WriteProgressStatus progress_status
) {
218 if (flush_policy_
== FlushPolicy::NO_FLUSH_ON_COMPLETION
) {
219 write_callback_
.Run(error
, bytes_written
, progress_status
);
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
,
234 WriteProgressStatus progress_status
,
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