• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 namespace fileapi {
20 
21 static const int kReadBufSize = 32768;
22 
FileWriterDelegate(scoped_ptr<FileStreamWriter> file_stream_writer,FlushPolicy flush_policy)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 
~FileWriterDelegate()36 FileWriterDelegate::~FileWriterDelegate() {
37 }
38 
Start(scoped_ptr<net::URLRequest> request,const DelegateWriteCallback & write_callback)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 
Cancel()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 
OnReceivedRedirect(net::URLRequest * request,const GURL & new_url,bool * defer_redirect)64 void 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 
OnAuthRequired(net::URLRequest * request,net::AuthChallengeInfo * auth_info)71 void FileWriterDelegate::OnAuthRequired(net::URLRequest* request,
72                                         net::AuthChallengeInfo* auth_info) {
73   NOTREACHED();
74   OnError(base::File::FILE_ERROR_SECURITY);
75 }
76 
OnCertificateRequested(net::URLRequest * request,net::SSLCertRequestInfo * cert_request_info)77 void FileWriterDelegate::OnCertificateRequested(
78     net::URLRequest* request,
79     net::SSLCertRequestInfo* cert_request_info) {
80   NOTREACHED();
81   OnError(base::File::FILE_ERROR_SECURITY);
82 }
83 
OnSSLCertificateError(net::URLRequest * request,const net::SSLInfo & ssl_info,bool fatal)84 void 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 
OnResponseStarted(net::URLRequest * request)91 void 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 
OnReadCompleted(net::URLRequest * request,int bytes_read)100 void 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 
Read()110 void 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 
OnDataReceived(int bytes_read)123 void 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 
Write()136 void 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 
OnDataWritten(int write_response)154 void 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 
168 FileWriterDelegate::WriteProgressStatus
GetCompletionStatusOnError() const169 FileWriterDelegate::GetCompletionStatusOnError() const {
170   return writing_started_ ? ERROR_WRITE_STARTED : ERROR_WRITE_NOT_STARTED;
171 }
172 
OnError(base::File::Error error)173 void 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 
OnProgress(int bytes_written,bool done)185 void 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 
OnWriteCancelled(int status)208 void FileWriterDelegate::OnWriteCancelled(int status) {
209   write_callback_.Run(base::File::FILE_ERROR_ABORT, 0,
210                       GetCompletionStatusOnError());
211 }
212 
MaybeFlushForCompletion(base::File::Error error,int bytes_written,WriteProgressStatus progress_status)213 void 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 
OnFlushed(base::File::Error error,int bytes_written,WriteProgressStatus progress_status,int flush_error)230 void 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