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/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"
26 const int kUpdatePeriodMs
= 500;
27 const int kMaxTimeBlockingFileThreadMs
= 1000;
29 int DownloadFile::number_active_objects_
= 0;
31 DownloadFileImpl::DownloadFileImpl(
32 scoped_ptr
<DownloadSaveInfo
> save_info
,
33 const base::FilePath
& default_download_directory
,
35 const GURL
& referrer_url
,
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
,
46 save_info
->hash_state
,
47 save_info
->file_stream
.Pass(),
49 default_download_directory_(default_download_directory
),
50 stream_reader_(stream
.Pass()),
52 bound_net_log_(bound_net_log
),
55 power_save_blocker_(power_save_blocker
.Pass()) {
58 DownloadFileImpl::~DownloadFileImpl() {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
60 --number_active_objects_
;
63 void DownloadFileImpl::Initialize(const InitializeCallback
& callback
) {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
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
));
75 stream_reader_
->RegisterCallback(
76 base::Bind(&DownloadFileImpl::StreamActive
, weak_factory_
.GetWeakPtr()));
78 download_start_
= base::TimeTicks::Now();
80 // Primarily to make reset to zero in restart visible to owner.
83 // Initial pull from the straw.
86 BrowserThread::PostTask(
87 BrowserThread::UI
, FROM_HERE
, base::Bind(
88 callback
, DOWNLOAD_INTERRUPT_REASON_NONE
));
90 ++number_active_objects_
;
93 DownloadInterruptReason
DownloadFileImpl::AppendDataToFile(
94 const char* data
, size_t data_len
) {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
97 if (!update_timer_
->IsRunning()) {
98 update_timer_
->Start(FROM_HERE
,
99 base::TimeDelta::FromMilliseconds(kUpdatePeriodMs
),
100 this, &DownloadFileImpl::SendUpdate
);
102 rate_estimator_
.Increment(data_len
);
103 return file_
.AppendDataToFile(data
, data_len
);
106 void DownloadFileImpl::RenameAndUniquify(
107 const base::FilePath
& full_path
,
108 const RenameCompletionCallback
& callback
) {
109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
111 base::FilePath
new_path(full_path
);
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
));
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
126 // Null out callback so that we don't do any more stream processing.
127 stream_reader_
->RegisterCallback(base::Closure());
132 BrowserThread::PostTask(
133 BrowserThread::UI
, FROM_HERE
,
134 base::Bind(callback
, reason
, new_path
));
137 void DownloadFileImpl::RenameAndAnnotate(
138 const base::FilePath
& full_path
,
139 const RenameCompletionCallback
& callback
) {
140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
142 base::FilePath
new_path(full_path
);
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
);
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
156 reason
= file_
.AnnotateWithSourceInformation();
159 if (reason
!= DOWNLOAD_INTERRUPT_REASON_NONE
) {
160 // Make sure our information is updated, since we're about to
164 // Null out callback so that we don't do any more stream processing.
165 stream_reader_
->RegisterCallback(base::Closure());
170 BrowserThread::PostTask(
171 BrowserThread::UI
, FROM_HERE
,
172 base::Bind(callback
, reason
, new_path
));
175 void DownloadFileImpl::Detach() {
179 void DownloadFileImpl::Cancel() {
183 base::FilePath
DownloadFileImpl::FullPath() const {
184 return file_
.full_path();
187 bool DownloadFileImpl::InProgress() const {
188 return file_
.in_progress();
191 int64
DownloadFileImpl::CurrentSpeed() const {
192 return rate_estimator_
.GetCountPerSecond();
195 bool DownloadFileImpl::GetHash(std::string
* hash
) {
196 return file_
.GetHash(hash
);
199 std::string
DownloadFileImpl::GetHashState() {
200 return file_
.GetHashState();
203 void DownloadFileImpl::SetClientGuid(const std::string
& guid
) {
204 file_
.SetClientGuid(guid
);
207 void DownloadFileImpl::StreamActive() {
208 base::TimeTicks
start(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
));
219 // Take care of any file local activity required.
221 state
= stream_reader_
->Read(&incoming_data
, &incoming_data_size
);
224 case ByteStreamReader::STREAM_EMPTY
:
226 case ByteStreamReader::STREAM_HAS_DATA
:
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
;
237 case ByteStreamReader::STREAM_COMPLETE
:
239 reason
= static_cast<DownloadInterruptReason
>(
240 stream_reader_
->GetStatus());
242 base::TimeTicks
close_start(base::TimeTicks::Now());
244 base::TimeTicks
now(base::TimeTicks::Now());
245 disk_writes_time_
+= (now
- close_start
);
247 bytes_seen_
, disk_writes_time_
, now
- download_start_
);
248 update_timer_
.reset();
255 now
= base::TimeTicks::Now();
256 } while (state
== ByteStreamReader::STREAM_HAS_DATA
&&
257 reason
== DOWNLOAD_INTERRUPT_REASON_NONE
&&
258 now
- start
<= delta
);
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()));
269 if (total_incoming_data_size
)
270 RecordFileThreadReceiveBuffers(num_buffers
);
272 RecordContiguousWriteTime(now
- start
);
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
,
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();
291 if (!GetHash(&hash
) || file_
.IsEmptyHash(hash
))
294 BrowserThread::PostTask(
295 BrowserThread::UI
, FROM_HERE
,
297 &DownloadDestinationObserver::DestinationCompleted
,
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
,
308 void DownloadFileImpl::SendUpdate() {
309 BrowserThread::PostTask(
310 BrowserThread::UI
, FROM_HERE
,
311 base::Bind(&DownloadDestinationObserver::DestinationUpdate
,
312 observer_
, file_
.bytes_so_far(), CurrentSpeed(),
317 int DownloadFile::GetNumberOfDownloadFiles() {
318 return number_active_objects_
;
321 } // namespace content