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