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#include "webkit/common/fileapi/file_system_util.h" 18 19namespace fileapi { 20 21static const int kReadBufSize = 32768; 22 23FileWriterDelegate::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), 30 bytes_written_(0), 31 bytes_read_(0), 32 io_buffer_(new net::IOBufferWithSize(kReadBufSize)), 33 weak_factory_(this) { 34} 35 36FileWriterDelegate::~FileWriterDelegate() { 37} 38 39void FileWriterDelegate::Start(scoped_ptr<net::URLRequest> request, 40 const DelegateWriteCallback& write_callback) { 41 write_callback_ = write_callback; 42 request_ = request.Pass(); 43 request_->Start(); 44} 45 46void FileWriterDelegate::Cancel() { 47 if (request_) { 48 // This halts any callbacks on this delegate. 49 request_->set_delegate(NULL); 50 request_->Cancel(); 51 } 52 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()); 61 } 62} 63 64void FileWriterDelegate::OnReceivedRedirect(net::URLRequest* request, 65 const GURL& new_url, 66 bool* defer_redirect) { 67 NOTREACHED(); 68 OnError(base::File::FILE_ERROR_SECURITY); 69} 70 71void FileWriterDelegate::OnAuthRequired(net::URLRequest* request, 72 net::AuthChallengeInfo* auth_info) { 73 NOTREACHED(); 74 OnError(base::File::FILE_ERROR_SECURITY); 75} 76 77void FileWriterDelegate::OnCertificateRequested( 78 net::URLRequest* request, 79 net::SSLCertRequestInfo* cert_request_info) { 80 NOTREACHED(); 81 OnError(base::File::FILE_ERROR_SECURITY); 82} 83 84void FileWriterDelegate::OnSSLCertificateError(net::URLRequest* request, 85 const net::SSLInfo& ssl_info, 86 bool fatal) { 87 NOTREACHED(); 88 OnError(base::File::FILE_ERROR_SECURITY); 89} 90 91void 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); 95 return; 96 } 97 Read(); 98} 99 100void FileWriterDelegate::OnReadCompleted(net::URLRequest* request, 101 int bytes_read) { 102 DCHECK_EQ(request_.get(), request); 103 if (!request->status().is_success()) { 104 OnError(base::File::FILE_ERROR_FAILED); 105 return; 106 } 107 OnDataReceived(bytes_read); 108} 109 110void FileWriterDelegate::Read() { 111 bytes_written_ = 0; 112 bytes_read_ = 0; 113 if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) { 114 base::MessageLoop::current()->PostTask( 115 FROM_HERE, 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); 120 } 121} 122 123void FileWriterDelegate::OnDataReceived(int bytes_read) { 124 bytes_read_ = bytes_read; 125 if (!bytes_read_) { // We're done. 126 OnProgress(0, true); 127 } else { 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 130 // it's necessary. 131 cursor_ = new net::DrainableIOBuffer(io_buffer_.get(), bytes_read_); 132 Write(); 133 } 134} 135 136void FileWriterDelegate::Write() { 137 writing_started_ = true; 138 int64 bytes_to_write = bytes_read_ - bytes_written_; 139 int write_response = 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( 146 FROM_HERE, 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)); 151 } 152} 153 154void 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_) 160 Read(); 161 else 162 Write(); 163 } else { 164 OnError(NetErrorToFileError(write_response)); 165 } 166} 167 168FileWriterDelegate::WriteProgressStatus 169FileWriterDelegate::GetCompletionStatusOnError() const { 170 return writing_started_ ? ERROR_WRITE_STARTED : ERROR_WRITE_NOT_STARTED; 171} 172 173void FileWriterDelegate::OnError(base::File::Error error) { 174 if (request_) { 175 request_->set_delegate(NULL); 176 request_->Cancel(); 177 } 178 179 if (writing_started_) 180 MaybeFlushForCompletion(error, 0, ERROR_WRITE_STARTED); 181 else 182 write_callback_.Run(error, 0, ERROR_WRITE_NOT_STARTED); 183} 184 185void 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; 195 196 if (done) { 197 MaybeFlushForCompletion(base::File::FILE_OK, bytes_written, 198 SUCCESS_COMPLETED); 199 } else { 200 write_callback_.Run(base::File::FILE_OK, bytes_written, 201 SUCCESS_IO_PENDING); 202 } 203 return; 204 } 205 bytes_written_backlog_ += bytes_written; 206} 207 208void FileWriterDelegate::OnWriteCancelled(int status) { 209 write_callback_.Run(base::File::FILE_ERROR_ABORT, 0, 210 GetCompletionStatusOnError()); 211} 212 213void FileWriterDelegate::MaybeFlushForCompletion( 214 base::File::Error error, 215 int bytes_written, 216 WriteProgressStatus progress_status) { 217 if (flush_policy_ == NO_FLUSH_ON_COMPLETION) { 218 write_callback_.Run(error, bytes_written, progress_status); 219 return; 220 } 221 DCHECK_EQ(FLUSH_ON_COMPLETION, flush_policy_); 222 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); 228} 229 230void FileWriterDelegate::OnFlushed(base::File::Error error, 231 int bytes_written, 232 WriteProgressStatus progress_status, 233 int flush_error) { 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(); 239 } 240 write_callback_.Run(error, bytes_written, progress_status); 241} 242 243} // namespace fileapi 244