blob: 21f69b110929c60169bf39cec8a81876bc3975f2 [file] [log] [blame]
// Copyright (c) 2010 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 "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)),
callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
}
FileWriterDelegate::~FileWriterDelegate() {
}
void FileWriterDelegate::Start(base::PlatformFile file, 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(
URLRequest* request, const GURL& new_url, bool* defer_redirect) {
NOTREACHED();
OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}
void FileWriterDelegate::OnAuthRequired(
URLRequest* request, net::AuthChallengeInfo* auth_info) {
NOTREACHED();
OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}
void FileWriterDelegate::OnCertificateRequested(
URLRequest* request, net::SSLCertRequestInfo* cert_request_info) {
NOTREACHED();
OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}
void FileWriterDelegate::OnSSLCertificateError(
URLRequest* request, int cert_error, net::X509Certificate* cert) {
NOTREACHED();
OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}
void FileWriterDelegate::OnResponseStarted(URLRequest* request) {
DCHECK_EQ(request_, request);
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(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_,
callback_factory_.NewCallback(&FileWriterDelegate::OnDataWritten));
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