- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / download / download_file_impl.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 "content/browser/download/download_file_impl.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/file_util.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "content/browser/byte_stream.h"
15 #include "content/browser/download/download_create_info.h"
16 #include "content/browser/download/download_interrupt_reasons_impl.h"
17 #include "content/browser/download/download_net_log_parameters.h"
18 #include "content/browser/download/download_stats.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/download_destination_observer.h"
21 #include "content/public/browser/power_save_blocker.h"
22 #include "net/base/io_buffer.h"
23
24 namespace content {
25
26 const int kUpdatePeriodMs = 500;
27 const int kMaxTimeBlockingFileThreadMs = 1000;
28
29 int DownloadFile::number_active_objects_ = 0;
30
31 DownloadFileImpl::DownloadFileImpl(
32     scoped_ptr<DownloadSaveInfo> save_info,
33     const base::FilePath& default_download_directory,
34     const GURL& url,
35     const GURL& referrer_url,
36     bool calculate_hash,
37     scoped_ptr<ByteStreamReader> stream,
38     const net::BoundNetLog& bound_net_log,
39     scoped_ptr<PowerSaveBlocker> power_save_blocker,
40     base::WeakPtr<DownloadDestinationObserver> observer)
41         : file_(save_info->file_path,
42                 url,
43                 referrer_url,
44                 save_info->offset,
45                 calculate_hash,
46                 save_info->hash_state,
47                 save_info->file_stream.Pass(),
48                 bound_net_log),
49           default_download_directory_(default_download_directory),
50           stream_reader_(stream.Pass()),
51           bytes_seen_(0),
52           bound_net_log_(bound_net_log),
53           observer_(observer),
54           weak_factory_(this),
55           power_save_blocker_(power_save_blocker.Pass()) {
56 }
57
58 DownloadFileImpl::~DownloadFileImpl() {
59   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
60   --number_active_objects_;
61 }
62
63 void DownloadFileImpl::Initialize(const InitializeCallback& callback) {
64   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
65
66   update_timer_.reset(new base::RepeatingTimer<DownloadFileImpl>());
67   DownloadInterruptReason result =
68       file_.Initialize(default_download_directory_);
69   if (result != DOWNLOAD_INTERRUPT_REASON_NONE) {
70     BrowserThread::PostTask(
71         BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
72     return;
73   }
74
75   stream_reader_->RegisterCallback(
76       base::Bind(&DownloadFileImpl::StreamActive, weak_factory_.GetWeakPtr()));
77
78   download_start_ = base::TimeTicks::Now();
79
80   // Primarily to make reset to zero in restart visible to owner.
81   SendUpdate();
82
83   // Initial pull from the straw.
84   StreamActive();
85
86   BrowserThread::PostTask(
87       BrowserThread::UI, FROM_HERE, base::Bind(
88           callback, DOWNLOAD_INTERRUPT_REASON_NONE));
89
90   ++number_active_objects_;
91 }
92
93 DownloadInterruptReason DownloadFileImpl::AppendDataToFile(
94     const char* data, size_t data_len) {
95   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
96
97   if (!update_timer_->IsRunning()) {
98     update_timer_->Start(FROM_HERE,
99                          base::TimeDelta::FromMilliseconds(kUpdatePeriodMs),
100                          this, &DownloadFileImpl::SendUpdate);
101   }
102   rate_estimator_.Increment(data_len);
103   return file_.AppendDataToFile(data, data_len);
104 }
105
106 void DownloadFileImpl::RenameAndUniquify(
107     const base::FilePath& full_path,
108     const RenameCompletionCallback& callback) {
109   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
110
111   base::FilePath new_path(full_path);
112
113   int uniquifier = file_util::GetUniquePathNumber(
114       new_path, base::FilePath::StringType());
115   if (uniquifier > 0) {
116     new_path = new_path.InsertBeforeExtensionASCII(
117         base::StringPrintf(" (%d)", uniquifier));
118   }
119
120   DownloadInterruptReason reason = file_.Rename(new_path);
121   if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
122     // Make sure our information is updated, since we're about to
123     // error out.
124     SendUpdate();
125
126     // Null out callback so that we don't do any more stream processing.
127     stream_reader_->RegisterCallback(base::Closure());
128
129     new_path.clear();
130   }
131
132   BrowserThread::PostTask(
133       BrowserThread::UI, FROM_HERE,
134       base::Bind(callback, reason, new_path));
135 }
136
137 void DownloadFileImpl::RenameAndAnnotate(
138     const base::FilePath& full_path,
139     const RenameCompletionCallback& callback) {
140   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
141
142   base::FilePath new_path(full_path);
143
144   DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_NONE;
145   // Short circuit null rename.
146   if (full_path != file_.full_path())
147     reason = file_.Rename(new_path);
148
149   if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) {
150     // Doing the annotation after the rename rather than before leaves
151     // a very small window during which the file has the final name but
152     // hasn't been marked with the Mark Of The Web.  However, it allows
153     // anti-virus scanners on Windows to actually see the data
154     // (http://crbug.com/127999) under the correct name (which is information
155     // it uses).
156     reason = file_.AnnotateWithSourceInformation();
157   }
158
159   if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
160     // Make sure our information is updated, since we're about to
161     // error out.
162     SendUpdate();
163
164     // Null out callback so that we don't do any more stream processing.
165     stream_reader_->RegisterCallback(base::Closure());
166
167     new_path.clear();
168   }
169
170   BrowserThread::PostTask(
171       BrowserThread::UI, FROM_HERE,
172       base::Bind(callback, reason, new_path));
173 }
174
175 void DownloadFileImpl::Detach() {
176   file_.Detach();
177 }
178
179 void DownloadFileImpl::Cancel() {
180   file_.Cancel();
181 }
182
183 base::FilePath DownloadFileImpl::FullPath() const {
184   return file_.full_path();
185 }
186
187 bool DownloadFileImpl::InProgress() const {
188   return file_.in_progress();
189 }
190
191 int64 DownloadFileImpl::CurrentSpeed() const {
192   return rate_estimator_.GetCountPerSecond();
193 }
194
195 bool DownloadFileImpl::GetHash(std::string* hash) {
196   return file_.GetHash(hash);
197 }
198
199 std::string DownloadFileImpl::GetHashState() {
200   return file_.GetHashState();
201 }
202
203 void DownloadFileImpl::SetClientGuid(const std::string& guid) {
204   file_.SetClientGuid(guid);
205 }
206
207 void DownloadFileImpl::StreamActive() {
208   base::TimeTicks start(base::TimeTicks::Now());
209   base::TimeTicks now;
210   scoped_refptr<net::IOBuffer> incoming_data;
211   size_t incoming_data_size = 0;
212   size_t total_incoming_data_size = 0;
213   size_t num_buffers = 0;
214   ByteStreamReader::StreamState state(ByteStreamReader::STREAM_EMPTY);
215   DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_NONE;
216   base::TimeDelta delta(
217       base::TimeDelta::FromMilliseconds(kMaxTimeBlockingFileThreadMs));
218
219   // Take care of any file local activity required.
220   do {
221     state = stream_reader_->Read(&incoming_data, &incoming_data_size);
222
223     switch (state) {
224       case ByteStreamReader::STREAM_EMPTY:
225         break;
226       case ByteStreamReader::STREAM_HAS_DATA:
227         {
228           ++num_buffers;
229           base::TimeTicks write_start(base::TimeTicks::Now());
230           reason = AppendDataToFile(
231               incoming_data.get()->data(), incoming_data_size);
232           disk_writes_time_ += (base::TimeTicks::Now() - write_start);
233           bytes_seen_ += incoming_data_size;
234           total_incoming_data_size += incoming_data_size;
235         }
236         break;
237       case ByteStreamReader::STREAM_COMPLETE:
238         {
239           reason = static_cast<DownloadInterruptReason>(
240               stream_reader_->GetStatus());
241           SendUpdate();
242           base::TimeTicks close_start(base::TimeTicks::Now());
243           file_.Finish();
244           base::TimeTicks now(base::TimeTicks::Now());
245           disk_writes_time_ += (now - close_start);
246           RecordFileBandwidth(
247               bytes_seen_, disk_writes_time_, now - download_start_);
248           update_timer_.reset();
249         }
250         break;
251       default:
252         NOTREACHED();
253         break;
254     }
255     now = base::TimeTicks::Now();
256   } while (state == ByteStreamReader::STREAM_HAS_DATA &&
257            reason == DOWNLOAD_INTERRUPT_REASON_NONE &&
258            now - start <= delta);
259
260   // If we're stopping to yield the thread, post a task so we come back.
261   if (state == ByteStreamReader::STREAM_HAS_DATA &&
262       now - start > delta) {
263     BrowserThread::PostTask(
264         BrowserThread::FILE, FROM_HERE,
265         base::Bind(&DownloadFileImpl::StreamActive,
266                    weak_factory_.GetWeakPtr()));
267   }
268
269   if (total_incoming_data_size)
270     RecordFileThreadReceiveBuffers(num_buffers);
271
272   RecordContiguousWriteTime(now - start);
273
274   // Take care of communication with our observer.
275   if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
276     // Error case for both upstream source and file write.
277     // Shut down processing and signal an error to our observer.
278     // Our observer will clean us up.
279     stream_reader_->RegisterCallback(base::Closure());
280     weak_factory_.InvalidateWeakPtrs();
281     SendUpdate();                       // Make info up to date before error.
282     BrowserThread::PostTask(
283         BrowserThread::UI, FROM_HERE,
284         base::Bind(&DownloadDestinationObserver::DestinationError,
285                    observer_, reason));
286   } else if (state == ByteStreamReader::STREAM_COMPLETE) {
287     // Signal successful completion and shut down processing.
288     stream_reader_->RegisterCallback(base::Closure());
289     weak_factory_.InvalidateWeakPtrs();
290     std::string hash;
291     if (!GetHash(&hash) || file_.IsEmptyHash(hash))
292       hash.clear();
293     SendUpdate();
294     BrowserThread::PostTask(
295         BrowserThread::UI, FROM_HERE,
296         base::Bind(
297             &DownloadDestinationObserver::DestinationCompleted,
298             observer_, hash));
299   }
300   if (bound_net_log_.IsLoggingAllEvents()) {
301     bound_net_log_.AddEvent(
302         net::NetLog::TYPE_DOWNLOAD_STREAM_DRAINED,
303         base::Bind(&FileStreamDrainedNetLogCallback, total_incoming_data_size,
304                    num_buffers));
305   }
306 }
307
308 void DownloadFileImpl::SendUpdate() {
309   BrowserThread::PostTask(
310       BrowserThread::UI, FROM_HERE,
311       base::Bind(&DownloadDestinationObserver::DestinationUpdate,
312                  observer_, file_.bytes_so_far(), CurrentSpeed(),
313                  GetHashState()));
314 }
315
316 // static
317 int DownloadFile::GetNumberOfDownloadFiles() {
318   return number_active_objects_;
319 }
320
321 }  // namespace content