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
DownloadFileImpl(scoped_ptr<DownloadSaveInfo> save_info,const base::FilePath & default_download_directory,const GURL & url,const GURL & referrer_url,bool calculate_hash,scoped_ptr<ByteStreamReader> stream,const net::BoundNetLog & bound_net_log,scoped_ptr<PowerSaveBlocker> power_save_blocker,base::WeakPtr<DownloadDestinationObserver> observer)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
~DownloadFileImpl()58 DownloadFileImpl::~DownloadFileImpl() {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
60 --number_active_objects_;
61 }
62
Initialize(const InitializeCallback & callback)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
AppendDataToFile(const char * data,size_t data_len)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
RenameAndUniquify(const base::FilePath & full_path,const RenameCompletionCallback & callback)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
RenameAndAnnotate(const base::FilePath & full_path,const RenameCompletionCallback & callback)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
Detach()175 void DownloadFileImpl::Detach() {
176 file_.Detach();
177 }
178
Cancel()179 void DownloadFileImpl::Cancel() {
180 file_.Cancel();
181 }
182
FullPath() const183 base::FilePath DownloadFileImpl::FullPath() const {
184 return file_.full_path();
185 }
186
InProgress() const187 bool DownloadFileImpl::InProgress() const {
188 return file_.in_progress();
189 }
190
CurrentSpeed() const191 int64 DownloadFileImpl::CurrentSpeed() const {
192 return rate_estimator_.GetCountPerSecond();
193 }
194
GetHash(std::string * hash)195 bool DownloadFileImpl::GetHash(std::string* hash) {
196 return file_.GetHash(hash);
197 }
198
GetHashState()199 std::string DownloadFileImpl::GetHashState() {
200 return file_.GetHashState();
201 }
202
SetClientGuid(const std::string & guid)203 void DownloadFileImpl::SetClientGuid(const std::string& guid) {
204 file_.SetClientGuid(guid);
205 }
206
StreamActive()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
SendUpdate()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
GetNumberOfDownloadFiles()317 int DownloadFile::GetNumberOfDownloadFiles() {
318 return number_active_objects_;
319 }
320
321 } // namespace content
322