- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / browser / fileapi / file_writer_delegate.cc
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
23 FileWriterDelegate::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
34 FileWriterDelegate::~FileWriterDelegate() {
35 }
36
37 void 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
44 void 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
62 void 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
69 void FileWriterDelegate::OnAuthRequired(net::URLRequest* request,
70                                         net::AuthChallengeInfo* auth_info) {
71   NOTREACHED();
72   OnError(base::PLATFORM_FILE_ERROR_SECURITY);
73 }
74
75 void FileWriterDelegate::OnCertificateRequested(
76     net::URLRequest* request,
77     net::SSLCertRequestInfo* cert_request_info) {
78   NOTREACHED();
79   OnError(base::PLATFORM_FILE_ERROR_SECURITY);
80 }
81
82 void 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
89 void 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
98 void 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
108 void 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
121 void 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
134 void 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
152 void 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
166 FileWriterDelegate::WriteProgressStatus
167 FileWriterDelegate::GetCompletionStatusOnError() const {
168   return writing_started_ ? ERROR_WRITE_STARTED : ERROR_WRITE_NOT_STARTED;
169 }
170
171 void 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
183 void 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
206 void FileWriterDelegate::OnWriteCancelled(int status) {
207   write_callback_.Run(base::PLATFORM_FILE_ERROR_ABORT, 0,
208                       GetCompletionStatusOnError());
209 }
210
211 void 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
222 void 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