Home | History | Annotate | Download | only in fileapi
      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 "storage/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 "storage/browser/fileapi/file_stream_writer.h"
     16 #include "storage/browser/fileapi/file_system_context.h"
     17 #include "storage/common/fileapi/file_system_util.h"
     18 
     19 namespace storage {
     20 
     21 static const int kReadBufSize = 32768;
     22 
     23 FileWriterDelegate::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 
     36 FileWriterDelegate::~FileWriterDelegate() {
     37 }
     38 
     39 void 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 
     46 void 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 
     64 void FileWriterDelegate::OnReceivedRedirect(
     65     net::URLRequest* request,
     66     const net::RedirectInfo& redirect_info,
     67     bool* defer_redirect) {
     68   NOTREACHED();
     69   OnError(base::File::FILE_ERROR_SECURITY);
     70 }
     71 
     72 void FileWriterDelegate::OnAuthRequired(net::URLRequest* request,
     73                                         net::AuthChallengeInfo* auth_info) {
     74   NOTREACHED();
     75   OnError(base::File::FILE_ERROR_SECURITY);
     76 }
     77 
     78 void FileWriterDelegate::OnCertificateRequested(
     79     net::URLRequest* request,
     80     net::SSLCertRequestInfo* cert_request_info) {
     81   NOTREACHED();
     82   OnError(base::File::FILE_ERROR_SECURITY);
     83 }
     84 
     85 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest* request,
     86                                                const net::SSLInfo& ssl_info,
     87                                                bool fatal) {
     88   NOTREACHED();
     89   OnError(base::File::FILE_ERROR_SECURITY);
     90 }
     91 
     92 void FileWriterDelegate::OnResponseStarted(net::URLRequest* request) {
     93   DCHECK_EQ(request_.get(), request);
     94   if (!request->status().is_success() || request->GetResponseCode() != 200) {
     95     OnError(base::File::FILE_ERROR_FAILED);
     96     return;
     97   }
     98   Read();
     99 }
    100 
    101 void FileWriterDelegate::OnReadCompleted(net::URLRequest* request,
    102                                          int bytes_read) {
    103   DCHECK_EQ(request_.get(), request);
    104   if (!request->status().is_success()) {
    105     OnError(base::File::FILE_ERROR_FAILED);
    106     return;
    107   }
    108   OnDataReceived(bytes_read);
    109 }
    110 
    111 void FileWriterDelegate::Read() {
    112   bytes_written_ = 0;
    113   bytes_read_ = 0;
    114   if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) {
    115     base::MessageLoop::current()->PostTask(
    116         FROM_HERE,
    117         base::Bind(&FileWriterDelegate::OnDataReceived,
    118                    weak_factory_.GetWeakPtr(), bytes_read_));
    119   } else if (!request_->status().is_io_pending()) {
    120     OnError(base::File::FILE_ERROR_FAILED);
    121   }
    122 }
    123 
    124 void FileWriterDelegate::OnDataReceived(int bytes_read) {
    125   bytes_read_ = bytes_read;
    126   if (!bytes_read_) {  // We're done.
    127     OnProgress(0, true);
    128   } else {
    129     // This could easily be optimized to rotate between a pool of buffers, so
    130     // that we could read and write at the same time.  It's not yet clear that
    131     // it's necessary.
    132     cursor_ = new net::DrainableIOBuffer(io_buffer_.get(), bytes_read_);
    133     Write();
    134   }
    135 }
    136 
    137 void FileWriterDelegate::Write() {
    138   writing_started_ = true;
    139   int64 bytes_to_write = bytes_read_ - bytes_written_;
    140   int write_response =
    141       file_stream_writer_->Write(cursor_.get(),
    142                                  static_cast<int>(bytes_to_write),
    143                                  base::Bind(&FileWriterDelegate::OnDataWritten,
    144                                             weak_factory_.GetWeakPtr()));
    145   if (write_response > 0) {
    146     base::MessageLoop::current()->PostTask(
    147         FROM_HERE,
    148         base::Bind(&FileWriterDelegate::OnDataWritten,
    149                    weak_factory_.GetWeakPtr(), write_response));
    150   } else if (net::ERR_IO_PENDING != write_response) {
    151     OnError(NetErrorToFileError(write_response));
    152   }
    153 }
    154 
    155 void FileWriterDelegate::OnDataWritten(int write_response) {
    156   if (write_response > 0) {
    157     OnProgress(write_response, false);
    158     cursor_->DidConsume(write_response);
    159     bytes_written_ += write_response;
    160     if (bytes_written_ == bytes_read_)
    161       Read();
    162     else
    163       Write();
    164   } else {
    165     OnError(NetErrorToFileError(write_response));
    166   }
    167 }
    168 
    169 FileWriterDelegate::WriteProgressStatus
    170 FileWriterDelegate::GetCompletionStatusOnError() const {
    171   return writing_started_ ? ERROR_WRITE_STARTED : ERROR_WRITE_NOT_STARTED;
    172 }
    173 
    174 void FileWriterDelegate::OnError(base::File::Error error) {
    175   if (request_) {
    176     request_->set_delegate(NULL);
    177     request_->Cancel();
    178   }
    179 
    180   if (writing_started_)
    181     MaybeFlushForCompletion(error, 0, ERROR_WRITE_STARTED);
    182   else
    183     write_callback_.Run(error, 0, ERROR_WRITE_NOT_STARTED);
    184 }
    185 
    186 void FileWriterDelegate::OnProgress(int bytes_written, bool done) {
    187   DCHECK(bytes_written + bytes_written_backlog_ >= bytes_written_backlog_);
    188   static const int kMinProgressDelayMS = 200;
    189   base::Time currentTime = base::Time::Now();
    190   if (done || last_progress_event_time_.is_null() ||
    191       (currentTime - last_progress_event_time_).InMilliseconds() >
    192           kMinProgressDelayMS) {
    193     bytes_written += bytes_written_backlog_;
    194     last_progress_event_time_ = currentTime;
    195     bytes_written_backlog_ = 0;
    196 
    197     if (done) {
    198       MaybeFlushForCompletion(base::File::FILE_OK, bytes_written,
    199                               SUCCESS_COMPLETED);
    200     } else {
    201       write_callback_.Run(base::File::FILE_OK, bytes_written,
    202                           SUCCESS_IO_PENDING);
    203     }
    204     return;
    205   }
    206   bytes_written_backlog_ += bytes_written;
    207 }
    208 
    209 void FileWriterDelegate::OnWriteCancelled(int status) {
    210   write_callback_.Run(base::File::FILE_ERROR_ABORT, 0,
    211                       GetCompletionStatusOnError());
    212 }
    213 
    214 void FileWriterDelegate::MaybeFlushForCompletion(
    215     base::File::Error error,
    216     int bytes_written,
    217     WriteProgressStatus progress_status) {
    218   if (flush_policy_ == NO_FLUSH_ON_COMPLETION) {
    219     write_callback_.Run(error, bytes_written, progress_status);
    220     return;
    221   }
    222   DCHECK_EQ(FLUSH_ON_COMPLETION, flush_policy_);
    223 
    224   int flush_error = file_stream_writer_->Flush(
    225       base::Bind(&FileWriterDelegate::OnFlushed, weak_factory_.GetWeakPtr(),
    226                  error, bytes_written, progress_status));
    227   if (flush_error != net::ERR_IO_PENDING)
    228     OnFlushed(error, bytes_written, progress_status, flush_error);
    229 }
    230 
    231 void FileWriterDelegate::OnFlushed(base::File::Error error,
    232                                    int bytes_written,
    233                                    WriteProgressStatus progress_status,
    234                                    int flush_error) {
    235   if (error == base::File::FILE_OK && flush_error != net::OK) {
    236     // If the Flush introduced an error, overwrite the status.
    237     // Otherwise, keep the original error status.
    238     error = NetErrorToFileError(flush_error);
    239     progress_status = GetCompletionStatusOnError();
    240   }
    241   write_callback_.Run(error, bytes_written, progress_status);
    242 }
    243 
    244 }  // namespace storage
    245