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 "webkit/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 "webkit/browser/fileapi/file_stream_writer.h"
16 #include "webkit/browser/fileapi/file_system_context.h"
17 #include "webkit/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(net::URLRequest
* request
,
66 bool* defer_redirect
) {
68 OnError(base::File::FILE_ERROR_SECURITY
);
71 void FileWriterDelegate::OnAuthRequired(net::URLRequest
* request
,
72 net::AuthChallengeInfo
* auth_info
) {
74 OnError(base::File::FILE_ERROR_SECURITY
);
77 void FileWriterDelegate::OnCertificateRequested(
78 net::URLRequest
* request
,
79 net::SSLCertRequestInfo
* cert_request_info
) {
81 OnError(base::File::FILE_ERROR_SECURITY
);
84 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest
* request
,
85 const net::SSLInfo
& ssl_info
,
88 OnError(base::File::FILE_ERROR_SECURITY
);
91 void FileWriterDelegate::OnResponseStarted(net::URLRequest
* request
) {
92 DCHECK_EQ(request_
.get(), request
);
93 if (!request
->status().is_success() || request
->GetResponseCode() != 200) {
94 OnError(base::File::FILE_ERROR_FAILED
);
100 void FileWriterDelegate::OnReadCompleted(net::URLRequest
* request
,
102 DCHECK_EQ(request_
.get(), request
);
103 if (!request
->status().is_success()) {
104 OnError(base::File::FILE_ERROR_FAILED
);
107 OnDataReceived(bytes_read
);
110 void FileWriterDelegate::Read() {
113 if (request_
->Read(io_buffer_
.get(), io_buffer_
->size(), &bytes_read_
)) {
114 base::MessageLoop::current()->PostTask(
116 base::Bind(&FileWriterDelegate::OnDataReceived
,
117 weak_factory_
.GetWeakPtr(), bytes_read_
));
118 } else if (!request_
->status().is_io_pending()) {
119 OnError(base::File::FILE_ERROR_FAILED
);
123 void FileWriterDelegate::OnDataReceived(int bytes_read
) {
124 bytes_read_
= bytes_read
;
125 if (!bytes_read_
) { // We're done.
128 // This could easily be optimized to rotate between a pool of buffers, so
129 // that we could read and write at the same time. It's not yet clear that
131 cursor_
= new net::DrainableIOBuffer(io_buffer_
.get(), bytes_read_
);
136 void FileWriterDelegate::Write() {
137 writing_started_
= true;
138 int64 bytes_to_write
= bytes_read_
- bytes_written_
;
140 file_stream_writer_
->Write(cursor_
.get(),
141 static_cast<int>(bytes_to_write
),
142 base::Bind(&FileWriterDelegate::OnDataWritten
,
143 weak_factory_
.GetWeakPtr()));
144 if (write_response
> 0) {
145 base::MessageLoop::current()->PostTask(
147 base::Bind(&FileWriterDelegate::OnDataWritten
,
148 weak_factory_
.GetWeakPtr(), write_response
));
149 } else if (net::ERR_IO_PENDING
!= write_response
) {
150 OnError(NetErrorToFileError(write_response
));
154 void FileWriterDelegate::OnDataWritten(int write_response
) {
155 if (write_response
> 0) {
156 OnProgress(write_response
, false);
157 cursor_
->DidConsume(write_response
);
158 bytes_written_
+= write_response
;
159 if (bytes_written_
== bytes_read_
)
164 OnError(NetErrorToFileError(write_response
));
168 FileWriterDelegate::WriteProgressStatus
169 FileWriterDelegate::GetCompletionStatusOnError() const {
170 return writing_started_
? ERROR_WRITE_STARTED
: ERROR_WRITE_NOT_STARTED
;
173 void FileWriterDelegate::OnError(base::File::Error error
) {
175 request_
->set_delegate(NULL
);
179 if (writing_started_
)
180 MaybeFlushForCompletion(error
, 0, ERROR_WRITE_STARTED
);
182 write_callback_
.Run(error
, 0, ERROR_WRITE_NOT_STARTED
);
185 void FileWriterDelegate::OnProgress(int bytes_written
, bool done
) {
186 DCHECK(bytes_written
+ bytes_written_backlog_
>= bytes_written_backlog_
);
187 static const int kMinProgressDelayMS
= 200;
188 base::Time currentTime
= base::Time::Now();
189 if (done
|| last_progress_event_time_
.is_null() ||
190 (currentTime
- last_progress_event_time_
).InMilliseconds() >
191 kMinProgressDelayMS
) {
192 bytes_written
+= bytes_written_backlog_
;
193 last_progress_event_time_
= currentTime
;
194 bytes_written_backlog_
= 0;
197 MaybeFlushForCompletion(base::File::FILE_OK
, bytes_written
,
200 write_callback_
.Run(base::File::FILE_OK
, bytes_written
,
205 bytes_written_backlog_
+= bytes_written
;
208 void FileWriterDelegate::OnWriteCancelled(int status
) {
209 write_callback_
.Run(base::File::FILE_ERROR_ABORT
, 0,
210 GetCompletionStatusOnError());
213 void FileWriterDelegate::MaybeFlushForCompletion(
214 base::File::Error error
,
216 WriteProgressStatus progress_status
) {
217 if (flush_policy_
== NO_FLUSH_ON_COMPLETION
) {
218 write_callback_
.Run(error
, bytes_written
, progress_status
);
221 DCHECK_EQ(FLUSH_ON_COMPLETION
, flush_policy_
);
223 int flush_error
= file_stream_writer_
->Flush(
224 base::Bind(&FileWriterDelegate::OnFlushed
, weak_factory_
.GetWeakPtr(),
225 error
, bytes_written
, progress_status
));
226 if (flush_error
!= net::ERR_IO_PENDING
)
227 OnFlushed(error
, bytes_written
, progress_status
, flush_error
);
230 void FileWriterDelegate::OnFlushed(base::File::Error error
,
232 WriteProgressStatus progress_status
,
234 if (error
== base::File::FILE_OK
&& flush_error
!= net::OK
) {
235 // If the Flush introduced an error, overwrite the status.
236 // Otherwise, keep the original error status.
237 error
= NetErrorToFileError(flush_error
);
238 progress_status
= GetCompletionStatusOnError();
240 write_callback_
.Run(error
, bytes_written
, progress_status
);
243 } // namespace fileapi