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