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/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"
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),
34 io_buffer_(new net::IOBufferWithSize(kReadBufSize
)),
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();
48 void FileWriterDelegate::Cancel() {
50 // This halts any callbacks on this delegate.
51 request_
->set_delegate(NULL
);
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
) {
71 OnError(base::File::FILE_ERROR_SECURITY
);
74 void FileWriterDelegate::OnAuthRequired(net::URLRequest
* request
,
75 net::AuthChallengeInfo
* auth_info
) {
77 OnError(base::File::FILE_ERROR_SECURITY
);
80 void FileWriterDelegate::OnCertificateRequested(
81 net::URLRequest
* request
,
82 net::SSLCertRequestInfo
* cert_request_info
) {
84 OnError(base::File::FILE_ERROR_SECURITY
);
87 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest
* request
,
88 const net::SSLInfo
& ssl_info
,
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
);
108 void FileWriterDelegate::OnReadCompleted(net::URLRequest
* request
,
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
);
120 OnDataReceived(bytes_read
);
123 void FileWriterDelegate::Read() {
126 if (request_
->Read(io_buffer_
.get(), io_buffer_
->size(), &bytes_read_
)) {
127 base::MessageLoop::current()->PostTask(
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.
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
144 cursor_
= new net::DrainableIOBuffer(io_buffer_
.get(), bytes_read_
);
149 void FileWriterDelegate::Write() {
150 writing_started_
= true;
151 int64 bytes_to_write
= bytes_read_
- bytes_written_
;
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(
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_
)
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
) {
188 request_
->set_delegate(NULL
);
192 if (writing_started_
)
193 MaybeFlushForCompletion(error
, 0, ERROR_WRITE_STARTED
);
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;
210 MaybeFlushForCompletion(base::File::FILE_OK
, bytes_written
,
213 write_callback_
.Run(base::File::FILE_OK
, bytes_written
,
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
,
229 WriteProgressStatus progress_status
) {
230 if (flush_policy_
== FlushPolicy::NO_FLUSH_ON_COMPLETION
) {
231 write_callback_
.Run(error
, bytes_written
, progress_status
);
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
,
246 WriteProgressStatus progress_status
,
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