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/fileapi/file_writer_delegate.h"
8 #include "base/callback.h"
9 #include "base/files/file_util_proxy.h"
10 #include "base/message_loop.h"
11 #include "base/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/fileapi/file_stream_writer.h"
16 #include "webkit/fileapi/file_system_context.h"
20 static const int kReadBufSize
= 32768;
24 base::PlatformFileError
NetErrorToPlatformFileError(int error
) {
25 // TODO(kinuko): Move this static method to more convenient place.
28 return base::PLATFORM_FILE_OK
;
29 case net::ERR_FILE_NO_SPACE
:
30 return base::PLATFORM_FILE_ERROR_NO_SPACE
;
31 case net::ERR_FILE_NOT_FOUND
:
32 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
33 case net::ERR_ACCESS_DENIED
:
34 return base::PLATFORM_FILE_ERROR_ACCESS_DENIED
;
36 return base::PLATFORM_FILE_ERROR_FAILED
;
42 FileWriterDelegate::FileWriterDelegate(
43 const DelegateWriteCallback
& write_callback
,
44 scoped_ptr
<FileStreamWriter
> file_stream_writer
)
45 : write_callback_(write_callback
),
46 file_stream_writer_(file_stream_writer
.Pass()),
47 writing_started_(false),
48 bytes_written_backlog_(0),
51 io_buffer_(new net::IOBufferWithSize(kReadBufSize
)),
52 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
55 FileWriterDelegate::~FileWriterDelegate() {
58 void FileWriterDelegate::Start(scoped_ptr
<net::URLRequest
> request
) {
59 request_
= request
.Pass();
63 bool FileWriterDelegate::Cancel() {
65 // This halts any callbacks on this delegate.
66 request_
->set_delegate(NULL
);
70 const int status
= file_stream_writer_
->Cancel(
71 base::Bind(&FileWriterDelegate::OnWriteCancelled
,
72 weak_factory_
.GetWeakPtr()));
73 // Return true to finish immediately if we have no pending writes.
74 // Otherwise we'll do the final cleanup in the Cancel callback.
75 return (status
!= net::ERR_IO_PENDING
);
78 void FileWriterDelegate::OnReceivedRedirect(net::URLRequest
* request
,
80 bool* defer_redirect
) {
82 OnError(base::PLATFORM_FILE_ERROR_SECURITY
);
85 void FileWriterDelegate::OnAuthRequired(net::URLRequest
* request
,
86 net::AuthChallengeInfo
* auth_info
) {
88 OnError(base::PLATFORM_FILE_ERROR_SECURITY
);
91 void FileWriterDelegate::OnCertificateRequested(
92 net::URLRequest
* request
,
93 net::SSLCertRequestInfo
* cert_request_info
) {
95 OnError(base::PLATFORM_FILE_ERROR_SECURITY
);
98 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest
* request
,
99 const net::SSLInfo
& ssl_info
,
102 OnError(base::PLATFORM_FILE_ERROR_SECURITY
);
105 void FileWriterDelegate::OnResponseStarted(net::URLRequest
* request
) {
106 DCHECK_EQ(request_
.get(), request
);
107 if (!request
->status().is_success() || request
->GetResponseCode() != 200) {
108 OnError(base::PLATFORM_FILE_ERROR_FAILED
);
114 void FileWriterDelegate::OnReadCompleted(net::URLRequest
* request
,
116 DCHECK_EQ(request_
.get(), request
);
117 if (!request
->status().is_success()) {
118 OnError(base::PLATFORM_FILE_ERROR_FAILED
);
121 OnDataReceived(bytes_read
);
124 void FileWriterDelegate::Read() {
127 if (request_
->Read(io_buffer_
.get(), io_buffer_
->size(), &bytes_read_
)) {
128 MessageLoop::current()->PostTask(
130 base::Bind(&FileWriterDelegate::OnDataReceived
,
131 weak_factory_
.GetWeakPtr(), bytes_read_
));
132 } else if (!request_
->status().is_io_pending()) {
133 OnError(base::PLATFORM_FILE_ERROR_FAILED
);
137 void FileWriterDelegate::OnDataReceived(int bytes_read
) {
138 bytes_read_
= bytes_read
;
139 if (!bytes_read_
) { // We're done.
142 // This could easily be optimized to rotate between a pool of buffers, so
143 // that we could read and write at the same time. It's not yet clear that
145 cursor_
= new net::DrainableIOBuffer(io_buffer_
, bytes_read_
);
150 void FileWriterDelegate::Write() {
151 writing_started_
= true;
152 int64 bytes_to_write
= bytes_read_
- bytes_written_
;
154 file_stream_writer_
->Write(cursor_
,
155 static_cast<int>(bytes_to_write
),
156 base::Bind(&FileWriterDelegate::OnDataWritten
,
157 weak_factory_
.GetWeakPtr()));
158 if (write_response
> 0)
159 MessageLoop::current()->PostTask(
161 base::Bind(&FileWriterDelegate::OnDataWritten
,
162 weak_factory_
.GetWeakPtr(), write_response
));
163 else if (net::ERR_IO_PENDING
!= write_response
)
164 OnError(NetErrorToPlatformFileError(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(NetErrorToPlatformFileError(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::PlatformFileError error
) {
187 if (request_
.get()) {
188 request_
->set_delegate(NULL
);
192 if (writing_started_
)
193 FlushForCompletion(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 FlushForCompletion(base::PLATFORM_FILE_OK
, bytes_written
,
213 write_callback_
.Run(base::PLATFORM_FILE_OK
, bytes_written
,
218 bytes_written_backlog_
+= bytes_written
;
221 void FileWriterDelegate::OnWriteCancelled(int status
) {
222 write_callback_
.Run(base::PLATFORM_FILE_ERROR_ABORT
, 0,
223 GetCompletionStatusOnError());
226 void FileWriterDelegate::FlushForCompletion(
227 base::PlatformFileError error
,
229 WriteProgressStatus progress_status
) {
230 int flush_error
= file_stream_writer_
->Flush(
231 base::Bind(&FileWriterDelegate::OnFlushed
,
232 weak_factory_
.GetWeakPtr(),
233 error
, bytes_written
, progress_status
));
234 if (flush_error
!= net::ERR_IO_PENDING
)
235 OnFlushed(error
, bytes_written
, progress_status
, flush_error
);
238 void FileWriterDelegate::OnFlushed(base::PlatformFileError error
,
240 WriteProgressStatus progress_status
,
242 if (error
== base::PLATFORM_FILE_OK
&& flush_error
!= net::OK
) {
243 // If the Flush introduced an error, overwrite the status.
244 // Otherwise, keep the original error status.
245 error
= NetErrorToPlatformFileError(flush_error
);
246 progress_status
= GetCompletionStatusOnError();
248 write_callback_
.Run(error
, bytes_written
, progress_status
);
251 } // namespace fileapi