OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "webkit/fileapi/file_writer_delegate.h" | |
6 | |
7 #include "base/bind.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" | |
17 | |
18 namespace fileapi { | |
19 | |
20 static const int kReadBufSize = 32768; | |
21 | |
22 namespace { | |
23 | |
24 base::PlatformFileError NetErrorToPlatformFileError(int error) { | |
25 // TODO(kinuko): Move this static method to more convenient place. | |
26 switch (error) { | |
27 case net::OK: | |
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; | |
35 default: | |
36 return base::PLATFORM_FILE_ERROR_FAILED; | |
37 } | |
38 } | |
39 | |
40 } // namespace | |
41 | |
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), | |
49 bytes_written_(0), | |
50 bytes_read_(0), | |
51 io_buffer_(new net::IOBufferWithSize(kReadBufSize)), | |
52 weak_factory_(this) { | |
53 } | |
54 | |
55 FileWriterDelegate::~FileWriterDelegate() { | |
56 } | |
57 | |
58 void FileWriterDelegate::Start(scoped_ptr<net::URLRequest> request) { | |
59 request_ = request.Pass(); | |
60 request_->Start(); | |
61 } | |
62 | |
63 bool FileWriterDelegate::Cancel() { | |
64 if (request_) { | |
65 // This halts any callbacks on this delegate. | |
66 request_->set_delegate(NULL); | |
67 request_->Cancel(); | |
68 } | |
69 | |
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); | |
76 } | |
77 | |
78 void FileWriterDelegate::OnReceivedRedirect(net::URLRequest* request, | |
79 const GURL& new_url, | |
80 bool* defer_redirect) { | |
81 NOTREACHED(); | |
82 OnError(base::PLATFORM_FILE_ERROR_SECURITY); | |
83 } | |
84 | |
85 void FileWriterDelegate::OnAuthRequired(net::URLRequest* request, | |
86 net::AuthChallengeInfo* auth_info) { | |
87 NOTREACHED(); | |
88 OnError(base::PLATFORM_FILE_ERROR_SECURITY); | |
89 } | |
90 | |
91 void FileWriterDelegate::OnCertificateRequested( | |
92 net::URLRequest* request, | |
93 net::SSLCertRequestInfo* cert_request_info) { | |
94 NOTREACHED(); | |
95 OnError(base::PLATFORM_FILE_ERROR_SECURITY); | |
96 } | |
97 | |
98 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest* request, | |
99 const net::SSLInfo& ssl_info, | |
100 bool fatal) { | |
101 NOTREACHED(); | |
102 OnError(base::PLATFORM_FILE_ERROR_SECURITY); | |
103 } | |
104 | |
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); | |
109 return; | |
110 } | |
111 Read(); | |
112 } | |
113 | |
114 void FileWriterDelegate::OnReadCompleted(net::URLRequest* request, | |
115 int bytes_read) { | |
116 DCHECK_EQ(request_.get(), request); | |
117 if (!request->status().is_success()) { | |
118 OnError(base::PLATFORM_FILE_ERROR_FAILED); | |
119 return; | |
120 } | |
121 OnDataReceived(bytes_read); | |
122 } | |
123 | |
124 void FileWriterDelegate::Read() { | |
125 bytes_written_ = 0; | |
126 bytes_read_ = 0; | |
127 if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) { | |
128 base::MessageLoop::current()->PostTask( | |
129 FROM_HERE, | |
130 base::Bind(&FileWriterDelegate::OnDataReceived, | |
131 weak_factory_.GetWeakPtr(), | |
132 bytes_read_)); | |
133 } else if (!request_->status().is_io_pending()) { | |
134 OnError(base::PLATFORM_FILE_ERROR_FAILED); | |
135 } | |
136 } | |
137 | |
138 void FileWriterDelegate::OnDataReceived(int bytes_read) { | |
139 bytes_read_ = bytes_read; | |
140 if (!bytes_read_) { // We're done. | |
141 OnProgress(0, true); | |
142 } else { | |
143 // This could easily be optimized to rotate between a pool of buffers, so | |
144 // that we could read and write at the same time. It's not yet clear that | |
145 // it's necessary. | |
146 cursor_ = new net::DrainableIOBuffer(io_buffer_, bytes_read_); | |
147 Write(); | |
148 } | |
149 } | |
150 | |
151 void FileWriterDelegate::Write() { | |
152 writing_started_ = true; | |
153 int64 bytes_to_write = bytes_read_ - bytes_written_; | |
154 int write_response = | |
155 file_stream_writer_->Write(cursor_, | |
156 static_cast<int>(bytes_to_write), | |
157 base::Bind(&FileWriterDelegate::OnDataWritten, | |
158 weak_factory_.GetWeakPtr())); | |
159 if (write_response > 0) | |
160 base::MessageLoop::current()->PostTask( | |
161 FROM_HERE, | |
162 base::Bind(&FileWriterDelegate::OnDataWritten, | |
163 weak_factory_.GetWeakPtr(), | |
164 write_response)); | |
165 else if (net::ERR_IO_PENDING != write_response) | |
166 OnError(NetErrorToPlatformFileError(write_response)); | |
167 } | |
168 | |
169 void FileWriterDelegate::OnDataWritten(int write_response) { | |
170 if (write_response > 0) { | |
171 OnProgress(write_response, false); | |
172 cursor_->DidConsume(write_response); | |
173 bytes_written_ += write_response; | |
174 if (bytes_written_ == bytes_read_) | |
175 Read(); | |
176 else | |
177 Write(); | |
178 } else { | |
179 OnError(NetErrorToPlatformFileError(write_response)); | |
180 } | |
181 } | |
182 | |
183 FileWriterDelegate::WriteProgressStatus | |
184 FileWriterDelegate::GetCompletionStatusOnError() const { | |
185 return writing_started_ ? ERROR_WRITE_STARTED : ERROR_WRITE_NOT_STARTED; | |
186 } | |
187 | |
188 void FileWriterDelegate::OnError(base::PlatformFileError error) { | |
189 if (request_) { | |
190 request_->set_delegate(NULL); | |
191 request_->Cancel(); | |
192 } | |
193 | |
194 if (writing_started_) | |
195 FlushForCompletion(error, 0, ERROR_WRITE_STARTED); | |
196 else | |
197 write_callback_.Run(error, 0, ERROR_WRITE_NOT_STARTED); | |
198 } | |
199 | |
200 void FileWriterDelegate::OnProgress(int bytes_written, bool done) { | |
201 DCHECK(bytes_written + bytes_written_backlog_ >= bytes_written_backlog_); | |
202 static const int kMinProgressDelayMS = 200; | |
203 base::Time currentTime = base::Time::Now(); | |
204 if (done || last_progress_event_time_.is_null() || | |
205 (currentTime - last_progress_event_time_).InMilliseconds() > | |
206 kMinProgressDelayMS) { | |
207 bytes_written += bytes_written_backlog_; | |
208 last_progress_event_time_ = currentTime; | |
209 bytes_written_backlog_ = 0; | |
210 | |
211 if (done) { | |
212 FlushForCompletion(base::PLATFORM_FILE_OK, bytes_written, | |
213 SUCCESS_COMPLETED); | |
214 } else { | |
215 write_callback_.Run(base::PLATFORM_FILE_OK, bytes_written, | |
216 SUCCESS_IO_PENDING); | |
217 } | |
218 return; | |
219 } | |
220 bytes_written_backlog_ += bytes_written; | |
221 } | |
222 | |
223 void FileWriterDelegate::OnWriteCancelled(int status) { | |
224 write_callback_.Run(base::PLATFORM_FILE_ERROR_ABORT, 0, | |
225 GetCompletionStatusOnError()); | |
226 } | |
227 | |
228 void FileWriterDelegate::FlushForCompletion( | |
229 base::PlatformFileError error, | |
230 int bytes_written, | |
231 WriteProgressStatus progress_status) { | |
232 int flush_error = file_stream_writer_->Flush( | |
233 base::Bind(&FileWriterDelegate::OnFlushed, | |
234 weak_factory_.GetWeakPtr(), | |
235 error, bytes_written, progress_status)); | |
236 if (flush_error != net::ERR_IO_PENDING) | |
237 OnFlushed(error, bytes_written, progress_status, flush_error); | |
238 } | |
239 | |
240 void FileWriterDelegate::OnFlushed(base::PlatformFileError error, | |
241 int bytes_written, | |
242 WriteProgressStatus progress_status, | |
243 int flush_error) { | |
244 if (error == base::PLATFORM_FILE_OK && flush_error != net::OK) { | |
245 // If the Flush introduced an error, overwrite the status. | |
246 // Otherwise, keep the original error status. | |
247 error = NetErrorToPlatformFileError(flush_error); | |
248 progress_status = GetCompletionStatusOnError(); | |
249 } | |
250 write_callback_.Run(error, bytes_written, progress_status); | |
251 } | |
252 | |
253 } // namespace fileapi | |
OLD | NEW |