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.
5 #include "content/browser/download/download_file_impl.h"
10 #include "base/file_util.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/time.h"
13 #include "content/browser/byte_stream.h"
14 #include "content/browser/download/download_create_info.h"
15 #include "content/browser/download/download_interrupt_reasons_impl.h"
16 #include "content/browser/download/download_net_log_parameters.h"
17 #include "content/browser/download/download_stats.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/download_destination_observer.h"
20 #include "content/public/browser/power_save_blocker.h"
21 #include "net/base/io_buffer.h"
25 const int kUpdatePeriodMs
= 500;
26 const int kMaxTimeBlockingFileThreadMs
= 1000;
28 int DownloadFile::number_active_objects_
= 0;
30 DownloadFileImpl::DownloadFileImpl(
31 scoped_ptr
<DownloadSaveInfo
> save_info
,
32 const base::FilePath
& default_download_directory
,
34 const GURL
& referrer_url
,
36 scoped_ptr
<ByteStreamReader
> stream
,
37 const net::BoundNetLog
& bound_net_log
,
38 scoped_ptr
<PowerSaveBlocker
> power_save_blocker
,
39 base::WeakPtr
<DownloadDestinationObserver
> observer
)
40 : file_(save_info
->file_path
,
45 save_info
->hash_state
,
46 save_info
->file_stream
.Pass(),
48 default_download_directory_(default_download_directory
),
49 stream_reader_(stream
.Pass()),
51 bound_net_log_(bound_net_log
),
54 power_save_blocker_(power_save_blocker
.Pass()) {
57 DownloadFileImpl::~DownloadFileImpl() {
58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
59 --number_active_objects_
;
62 void DownloadFileImpl::Initialize(const InitializeCallback
& callback
) {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
65 update_timer_
.reset(new base::RepeatingTimer
<DownloadFileImpl
>());
66 DownloadInterruptReason result
=
67 file_
.Initialize(default_download_directory_
);
68 if (result
!= DOWNLOAD_INTERRUPT_REASON_NONE
) {
69 BrowserThread::PostTask(
70 BrowserThread::UI
, FROM_HERE
, base::Bind(callback
, result
));
74 stream_reader_
->RegisterCallback(
75 base::Bind(&DownloadFileImpl::StreamActive
, weak_factory_
.GetWeakPtr()));
77 download_start_
= base::TimeTicks::Now();
79 // Primarily to make reset to zero in restart visible to owner.
82 // Initial pull from the straw.
85 BrowserThread::PostTask(
86 BrowserThread::UI
, FROM_HERE
, base::Bind(
87 callback
, DOWNLOAD_INTERRUPT_REASON_NONE
));
89 ++number_active_objects_
;
92 DownloadInterruptReason
DownloadFileImpl::AppendDataToFile(
93 const char* data
, size_t data_len
) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
96 if (!update_timer_
->IsRunning()) {
97 update_timer_
->Start(FROM_HERE
,
98 base::TimeDelta::FromMilliseconds(kUpdatePeriodMs
),
99 this, &DownloadFileImpl::SendUpdate
);
101 rate_estimator_
.Increment(data_len
);
102 return file_
.AppendDataToFile(data
, data_len
);
105 void DownloadFileImpl::RenameAndUniquify(
106 const base::FilePath
& full_path
,
107 const RenameCompletionCallback
& callback
) {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
110 base::FilePath
new_path(full_path
);
112 int uniquifier
= file_util::GetUniquePathNumber(
113 new_path
, base::FilePath::StringType());
114 if (uniquifier
> 0) {
115 new_path
= new_path
.InsertBeforeExtensionASCII(
116 base::StringPrintf(" (%d)", uniquifier
));
119 DownloadInterruptReason reason
= file_
.Rename(new_path
);
120 if (reason
!= DOWNLOAD_INTERRUPT_REASON_NONE
) {
121 // Make sure our information is updated, since we're about to
125 // Null out callback so that we don't do any more stream processing.
126 stream_reader_
->RegisterCallback(base::Closure());
131 BrowserThread::PostTask(
132 BrowserThread::UI
, FROM_HERE
,
133 base::Bind(callback
, reason
, new_path
));
136 void DownloadFileImpl::RenameAndAnnotate(
137 const base::FilePath
& full_path
,
138 const RenameCompletionCallback
& callback
) {
139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
141 base::FilePath
new_path(full_path
);
143 DownloadInterruptReason reason
= DOWNLOAD_INTERRUPT_REASON_NONE
;
144 // Short circuit null rename.
145 if (full_path
!= file_
.full_path())
146 reason
= file_
.Rename(new_path
);
148 if (reason
== DOWNLOAD_INTERRUPT_REASON_NONE
) {
149 // Doing the annotation after the rename rather than before leaves
150 // a very small window during which the file has the final name but
151 // hasn't been marked with the Mark Of The Web. However, it allows
152 // anti-virus scanners on Windows to actually see the data
153 // (http://crbug.com/127999) under the correct name (which is information
155 reason
= file_
.AnnotateWithSourceInformation();
158 if (reason
!= DOWNLOAD_INTERRUPT_REASON_NONE
) {
159 // Make sure our information is updated, since we're about to
163 // Null out callback so that we don't do any more stream processing.
164 stream_reader_
->RegisterCallback(base::Closure());
169 BrowserThread::PostTask(
170 BrowserThread::UI
, FROM_HERE
,
171 base::Bind(callback
, reason
, new_path
));
174 void DownloadFileImpl::Detach() {
178 void DownloadFileImpl::Cancel() {
182 base::FilePath
DownloadFileImpl::FullPath() const {
183 return file_
.full_path();
186 bool DownloadFileImpl::InProgress() const {
187 return file_
.in_progress();
190 int64
DownloadFileImpl::CurrentSpeed() const {
191 return rate_estimator_
.GetCountPerSecond();
194 bool DownloadFileImpl::GetHash(std::string
* hash
) {
195 return file_
.GetHash(hash
);
198 std::string
DownloadFileImpl::GetHashState() {
199 return file_
.GetHashState();
202 void DownloadFileImpl::StreamActive() {
203 base::TimeTicks
start(base::TimeTicks::Now());
205 scoped_refptr
<net::IOBuffer
> incoming_data
;
206 size_t incoming_data_size
= 0;
207 size_t total_incoming_data_size
= 0;
208 size_t num_buffers
= 0;
209 ByteStreamReader::StreamState
state(ByteStreamReader::STREAM_EMPTY
);
210 DownloadInterruptReason reason
= DOWNLOAD_INTERRUPT_REASON_NONE
;
211 base::TimeDelta
delta(
212 base::TimeDelta::FromMilliseconds(kMaxTimeBlockingFileThreadMs
));
214 // Take care of any file local activity required.
216 state
= stream_reader_
->Read(&incoming_data
, &incoming_data_size
);
219 case ByteStreamReader::STREAM_EMPTY
:
221 case ByteStreamReader::STREAM_HAS_DATA
:
224 base::TimeTicks
write_start(base::TimeTicks::Now());
225 reason
= AppendDataToFile(
226 incoming_data
.get()->data(), incoming_data_size
);
227 disk_writes_time_
+= (base::TimeTicks::Now() - write_start
);
228 bytes_seen_
+= incoming_data_size
;
229 total_incoming_data_size
+= incoming_data_size
;
232 case ByteStreamReader::STREAM_COMPLETE
:
234 reason
= stream_reader_
->GetStatus();
236 base::TimeTicks
close_start(base::TimeTicks::Now());
238 base::TimeTicks
now(base::TimeTicks::Now());
239 disk_writes_time_
+= (now
- close_start
);
241 bytes_seen_
, disk_writes_time_
, now
- download_start_
);
242 update_timer_
.reset();
249 now
= base::TimeTicks::Now();
250 } while (state
== ByteStreamReader::STREAM_HAS_DATA
&&
251 reason
== DOWNLOAD_INTERRUPT_REASON_NONE
&&
252 now
- start
<= delta
);
254 // If we're stopping to yield the thread, post a task so we come back.
255 if (state
== ByteStreamReader::STREAM_HAS_DATA
&&
256 now
- start
> delta
) {
257 BrowserThread::PostTask(
258 BrowserThread::FILE, FROM_HERE
,
259 base::Bind(&DownloadFileImpl::StreamActive
,
260 weak_factory_
.GetWeakPtr()));
263 if (total_incoming_data_size
)
264 RecordFileThreadReceiveBuffers(num_buffers
);
266 RecordContiguousWriteTime(now
- start
);
268 // Take care of communication with our observer.
269 if (reason
!= DOWNLOAD_INTERRUPT_REASON_NONE
) {
270 // Error case for both upstream source and file write.
271 // Shut down processing and signal an error to our observer.
272 // Our observer will clean us up.
273 stream_reader_
->RegisterCallback(base::Closure());
274 weak_factory_
.InvalidateWeakPtrs();
275 SendUpdate(); // Make info up to date before error.
276 BrowserThread::PostTask(
277 BrowserThread::UI
, FROM_HERE
,
278 base::Bind(&DownloadDestinationObserver::DestinationError
,
280 } else if (state
== ByteStreamReader::STREAM_COMPLETE
) {
281 // Signal successful completion and shut down processing.
282 stream_reader_
->RegisterCallback(base::Closure());
283 weak_factory_
.InvalidateWeakPtrs();
285 if (!GetHash(&hash
) || file_
.IsEmptyHash(hash
))
288 BrowserThread::PostTask(
289 BrowserThread::UI
, FROM_HERE
,
291 &DownloadDestinationObserver::DestinationCompleted
,
294 if (bound_net_log_
.IsLoggingAllEvents()) {
295 bound_net_log_
.AddEvent(
296 net::NetLog::TYPE_DOWNLOAD_STREAM_DRAINED
,
297 base::Bind(&FileStreamDrainedNetLogCallback
, total_incoming_data_size
,
302 void DownloadFileImpl::SendUpdate() {
303 BrowserThread::PostTask(
304 BrowserThread::UI
, FROM_HERE
,
305 base::Bind(&DownloadDestinationObserver::DestinationUpdate
,
306 observer_
, file_
.bytes_so_far(), CurrentSpeed(),
311 int DownloadFile::GetNumberOfDownloadFiles() {
312 return number_active_objects_
;
315 } // namespace content