// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "webkit/fileapi/file_writer_delegate.h" #include "base/message_loop.h" #include "base/threading/thread_restrictions.h" #include "net/base/net_errors.h" #include "webkit/fileapi/file_system_operation.h" namespace fileapi { static const int kReadBufSize = 32768; FileWriterDelegate::FileWriterDelegate( FileSystemOperation* file_system_operation, int64 offset) : file_system_operation_(file_system_operation), file_(base::kInvalidPlatformFileValue), offset_(offset), bytes_read_backlog_(0), bytes_written_(0), bytes_read_(0), io_buffer_(new net::IOBufferWithSize(kReadBufSize)), io_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this), &FileWriterDelegate::OnDataWritten), method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { } FileWriterDelegate::~FileWriterDelegate() { } void FileWriterDelegate::Start(base::PlatformFile file, net::URLRequest* request) { file_ = file; request_ = request; file_stream_.reset( new net::FileStream( file, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC)); request_->Start(); } void FileWriterDelegate::OnReceivedRedirect( net::URLRequest* request, const GURL& new_url, bool* defer_redirect) { NOTREACHED(); OnError(base::PLATFORM_FILE_ERROR_SECURITY); } void FileWriterDelegate::OnAuthRequired( net::URLRequest* request, net::AuthChallengeInfo* auth_info) { NOTREACHED(); OnError(base::PLATFORM_FILE_ERROR_SECURITY); } void FileWriterDelegate::OnCertificateRequested( net::URLRequest* request, net::SSLCertRequestInfo* cert_request_info) { NOTREACHED(); OnError(base::PLATFORM_FILE_ERROR_SECURITY); } void FileWriterDelegate::OnSSLCertificateError( net::URLRequest* request, int cert_error, net::X509Certificate* cert) { NOTREACHED(); OnError(base::PLATFORM_FILE_ERROR_SECURITY); } void FileWriterDelegate::OnResponseStarted(net::URLRequest* request) { DCHECK_EQ(request_, request); // file_stream_->Seek() blocks the IO thread. // See http://crbug.com/75548. base::ThreadRestrictions::ScopedAllowIO allow_io; if (!request->status().is_success()) { OnError(base::PLATFORM_FILE_ERROR_FAILED); return; } int64 error = file_stream_->Seek(net::FROM_BEGIN, offset_); if (error != offset_) { OnError(base::PLATFORM_FILE_ERROR_FAILED); return; } Read(); } void FileWriterDelegate::OnReadCompleted(net::URLRequest* request, int bytes_read) { DCHECK_EQ(request_, request); if (!request->status().is_success()) { OnError(base::PLATFORM_FILE_ERROR_FAILED); return; } OnDataReceived(bytes_read); } void FileWriterDelegate::Read() { bytes_written_ = 0; bytes_read_ = 0; if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) { MessageLoop::current()->PostTask( FROM_HERE, method_factory_.NewRunnableMethod( &FileWriterDelegate::OnDataReceived, bytes_read_)); } else if (!request_->status().is_io_pending()) { OnError(base::PLATFORM_FILE_ERROR_FAILED); } } void FileWriterDelegate::OnDataReceived(int bytes_read) { bytes_read_ = bytes_read; if (!bytes_read_) { // We're done. OnProgress(0, true); } else { // This could easily be optimized to rotate between a pool of buffers, so // that we could read and write at the same time. It's not yet clear that // it's necessary. Write(); } } void FileWriterDelegate::Write() { int write_response = file_stream_->Write(io_buffer_->data() + bytes_written_, bytes_read_ - bytes_written_, &io_callback_); if (write_response > 0) MessageLoop::current()->PostTask( FROM_HERE, method_factory_.NewRunnableMethod( &FileWriterDelegate::OnDataWritten, write_response)); else if (net::ERR_IO_PENDING != write_response) OnError(base::PLATFORM_FILE_ERROR_FAILED); } void FileWriterDelegate::OnDataWritten(int write_response) { if (write_response > 0) { OnProgress(write_response, false); bytes_written_ += write_response; if (bytes_written_ == bytes_read_) Read(); else Write(); } else { OnError(base::PLATFORM_FILE_ERROR_FAILED); } } void FileWriterDelegate::OnError(base::PlatformFileError error) { request_->Cancel(); file_system_operation_->DidWrite(error, 0, true); } void FileWriterDelegate::OnProgress(int bytes_read, bool done) { DCHECK(bytes_read + bytes_read_backlog_ >= bytes_read_backlog_); static const int kMinProgressDelayMS = 200; base::Time currentTime = base::Time::Now(); if (done || last_progress_event_time_.is_null() || (currentTime - last_progress_event_time_).InMilliseconds() > kMinProgressDelayMS) { bytes_read += bytes_read_backlog_; last_progress_event_time_ = currentTime; bytes_read_backlog_ = 0; file_system_operation_->DidWrite( base::PLATFORM_FILE_OK, bytes_read, done); return; } bytes_read_backlog_ += bytes_read; } } // namespace fileapi