OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "chrome/browser/download/download_item.h" | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "base/file_util.h" | |
9 #include "base/format_macros.h" | |
10 #include "base/i18n/case_conversion.h" | |
11 #include "base/logging.h" | |
12 #include "base/metrics/histogram.h" | |
13 #include "base/stringprintf.h" | |
14 #include "base/timer.h" | |
15 #include "base/utf_string_conversions.h" | |
16 #include "net/base/net_util.h" | |
17 #include "chrome/browser/download/download_create_info.h" | |
18 #include "chrome/browser/download/download_crx_util.h" | |
19 #include "chrome/browser/download/download_extensions.h" | |
20 #include "chrome/browser/download/download_file_manager.h" | |
21 #include "chrome/browser/download/download_history.h" | |
22 #include "chrome/browser/download/download_manager.h" | |
23 #include "chrome/browser/download/download_manager_delegate.h" | |
24 #include "chrome/browser/download/download_prefs.h" | |
25 #include "chrome/browser/download/download_state_info.h" | |
26 #include "chrome/browser/download/download_util.h" | |
27 #include "chrome/browser/extensions/crx_installer.h" | |
28 #include "chrome/browser/history/download_history_info.h" | |
29 #include "chrome/browser/platform_util.h" | |
30 #include "chrome/browser/prefs/pref_service.h" | |
31 #include "chrome/browser/profiles/profile.h" | |
32 #include "chrome/common/chrome_notification_types.h" | |
33 #include "chrome/common/extensions/extension.h" | |
34 #include "chrome/common/pref_names.h" | |
35 #include "content/browser/browser_thread.h" | |
36 #include "content/common/notification_source.h" | |
37 #include "ui/base/l10n/l10n_util.h" | |
38 | |
39 // A DownloadItem normally goes through the following states: | |
40 // * Created (when download starts) | |
41 // * Made visible to consumers (e.g. Javascript) after the | |
42 // destination file has been determined. | |
43 // * Entered into the history database. | |
44 // * Made visible in the download shelf. | |
45 // * All data is saved. Note that the actual data download occurs | |
46 // in parallel with the above steps, but until those steps are | |
47 // complete, completion of the data download will be ignored. | |
48 // * Download file is renamed to its final name, and possibly | |
49 // auto-opened. | |
50 // TODO(rdsmith): This progress should be reflected in | |
51 // DownloadItem::DownloadState and a state transition table/state diagram. | |
52 // | |
53 // TODO(rdsmith): This description should be updated to reflect the cancel | |
54 // pathways. | |
55 | |
56 namespace { | |
57 | |
58 // Update frequency (milliseconds). | |
59 const int kUpdateTimeMs = 1000; | |
60 | |
61 static void DeleteDownloadedFile(const FilePath& path) { | |
62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
63 | |
64 // Make sure we only delete files. | |
65 if (!file_util::DirectoryExists(path)) | |
66 file_util::Delete(path, false); | |
67 } | |
68 | |
69 const char* DebugSafetyStateString(DownloadItem::SafetyState state) { | |
70 switch (state) { | |
71 case DownloadItem::SAFE: | |
72 return "SAFE"; | |
73 case DownloadItem::DANGEROUS: | |
74 return "DANGEROUS"; | |
75 case DownloadItem::DANGEROUS_BUT_VALIDATED: | |
76 return "DANGEROUS_BUT_VALIDATED"; | |
77 default: | |
78 NOTREACHED() << "Unknown safety state " << state; | |
79 return "unknown"; | |
80 }; | |
81 } | |
82 | |
83 const char* DebugDownloadStateString(DownloadItem::DownloadState state) { | |
84 switch (state) { | |
85 case DownloadItem::IN_PROGRESS: | |
86 return "IN_PROGRESS"; | |
87 case DownloadItem::COMPLETE: | |
88 return "COMPLETE"; | |
89 case DownloadItem::CANCELLED: | |
90 return "CANCELLED"; | |
91 case DownloadItem::REMOVING: | |
92 return "REMOVING"; | |
93 case DownloadItem::INTERRUPTED: | |
94 return "INTERRUPTED"; | |
95 default: | |
96 NOTREACHED() << "Unknown download state " << state; | |
97 return "unknown"; | |
98 }; | |
99 } | |
100 | |
101 DownloadItem::SafetyState GetSafetyState(bool dangerous_file, | |
102 bool dangerous_url) { | |
103 return (dangerous_url || dangerous_file) ? | |
104 DownloadItem::DANGEROUS : DownloadItem::SAFE; | |
105 } | |
106 | |
107 // Note: When a download has both |dangerous_file| and |dangerous_url| set, | |
108 // danger type is set to DANGEROUS_URL since the risk of dangerous URL | |
109 // overweights that of dangerous file type. | |
110 DownloadItem::DangerType GetDangerType(bool dangerous_file, | |
111 bool dangerous_url) { | |
112 if (dangerous_url) { | |
113 // dangerous URL overweights dangerous file. We check dangerous URL first. | |
114 return DownloadItem::DANGEROUS_URL; | |
115 } | |
116 return dangerous_file ? | |
117 DownloadItem::DANGEROUS_FILE : DownloadItem::NOT_DANGEROUS; | |
118 } | |
119 | |
120 } // namespace | |
121 | |
122 // Constructor for reading from the history service. | |
123 DownloadItem::DownloadItem(DownloadManager* download_manager, | |
124 const DownloadHistoryInfo& info) | |
125 : download_id_(-1), | |
126 full_path_(info.path), | |
127 url_chain_(1, info.url), | |
128 referrer_url_(info.referrer_url), | |
129 total_bytes_(info.total_bytes), | |
130 received_bytes_(info.received_bytes), | |
131 start_tick_(base::TimeTicks()), | |
132 state_(static_cast<DownloadState>(info.state)), | |
133 start_time_(info.start_time), | |
134 db_handle_(info.db_handle), | |
135 download_manager_(download_manager), | |
136 is_paused_(false), | |
137 open_when_complete_(false), | |
138 file_externally_removed_(false), | |
139 safety_state_(SAFE), | |
140 auto_opened_(false), | |
141 is_otr_(false), | |
142 is_temporary_(false), | |
143 all_data_saved_(false), | |
144 opened_(false), | |
145 open_enabled_(true) { | |
146 if (IsInProgress()) | |
147 state_ = CANCELLED; | |
148 if (IsComplete()) | |
149 all_data_saved_ = true; | |
150 Init(false /* not actively downloading */); | |
151 } | |
152 | |
153 // Constructing for a regular download: | |
154 DownloadItem::DownloadItem(DownloadManager* download_manager, | |
155 const DownloadCreateInfo& info, | |
156 bool is_otr) | |
157 : state_info_(info.original_name, info.save_info.file_path, | |
158 info.has_user_gesture, info.prompt_user_for_save_location, | |
159 info.path_uniquifier, false, false, | |
160 info.is_extension_install), | |
161 request_handle_(info.request_handle), | |
162 download_id_(info.download_id), | |
163 full_path_(info.path), | |
164 url_chain_(info.url_chain), | |
165 referrer_url_(info.referrer_url), | |
166 suggested_filename_(UTF16ToUTF8(info.save_info.suggested_name)), | |
167 content_disposition_(info.content_disposition), | |
168 mime_type_(info.mime_type), | |
169 original_mime_type_(info.original_mime_type), | |
170 referrer_charset_(info.referrer_charset), | |
171 total_bytes_(info.total_bytes), | |
172 received_bytes_(0), | |
173 last_os_error_(0), | |
174 start_tick_(base::TimeTicks::Now()), | |
175 state_(IN_PROGRESS), | |
176 start_time_(info.start_time), | |
177 db_handle_(DownloadHistory::kUninitializedHandle), | |
178 download_manager_(download_manager), | |
179 is_paused_(false), | |
180 open_when_complete_(false), | |
181 file_externally_removed_(false), | |
182 safety_state_(SAFE), | |
183 auto_opened_(false), | |
184 is_otr_(is_otr), | |
185 is_temporary_(!info.save_info.file_path.empty()), | |
186 all_data_saved_(false), | |
187 opened_(false), | |
188 open_enabled_(true) { | |
189 Init(true /* actively downloading */); | |
190 } | |
191 | |
192 // Constructing for the "Save Page As..." feature: | |
193 DownloadItem::DownloadItem(DownloadManager* download_manager, | |
194 const FilePath& path, | |
195 const GURL& url, | |
196 bool is_otr) | |
197 : download_id_(download_manager->GetNextSavePageId()), | |
198 full_path_(path), | |
199 url_chain_(1, url), | |
200 referrer_url_(GURL()), | |
201 total_bytes_(0), | |
202 received_bytes_(0), | |
203 last_os_error_(0), | |
204 start_tick_(base::TimeTicks::Now()), | |
205 state_(IN_PROGRESS), | |
206 start_time_(base::Time::Now()), | |
207 db_handle_(DownloadHistory::kUninitializedHandle), | |
208 download_manager_(download_manager), | |
209 is_paused_(false), | |
210 open_when_complete_(false), | |
211 file_externally_removed_(false), | |
212 safety_state_(SAFE), | |
213 auto_opened_(false), | |
214 is_otr_(is_otr), | |
215 is_temporary_(false), | |
216 all_data_saved_(false), | |
217 opened_(false), | |
218 open_enabled_(true) { | |
219 Init(true /* actively downloading */); | |
220 } | |
221 | |
222 DownloadItem::~DownloadItem() { | |
223 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
224 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
225 | |
226 TransitionTo(REMOVING); | |
227 download_manager_->AssertQueueStateConsistent(this); | |
228 } | |
229 | |
230 void DownloadItem::AddObserver(Observer* observer) { | |
231 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
232 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
233 | |
234 observers_.AddObserver(observer); | |
235 } | |
236 | |
237 void DownloadItem::RemoveObserver(Observer* observer) { | |
238 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
239 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
240 | |
241 observers_.RemoveObserver(observer); | |
242 } | |
243 | |
244 void DownloadItem::UpdateObservers() { | |
245 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
246 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
247 | |
248 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); | |
249 } | |
250 | |
251 bool DownloadItem::CanShowInFolder() { | |
252 return !IsCancelled() && !file_externally_removed_; | |
253 } | |
254 | |
255 bool DownloadItem::CanOpenDownload() { | |
256 return !Extension::IsExtension(state_info_.target_name) && | |
257 !file_externally_removed_; | |
258 } | |
259 | |
260 bool DownloadItem::ShouldOpenFileBasedOnExtension() { | |
261 return download_manager_->delegate()->ShouldOpenFileBasedOnExtension( | |
262 GetUserVerifiedFilePath()); | |
263 } | |
264 | |
265 void DownloadItem::OpenFilesBasedOnExtension(bool open) { | |
266 DownloadPrefs* prefs = download_manager_->download_prefs(); | |
267 if (open) | |
268 prefs->EnableAutoOpenBasedOnExtension(GetUserVerifiedFilePath()); | |
269 else | |
270 prefs->DisableAutoOpenBasedOnExtension(GetUserVerifiedFilePath()); | |
271 } | |
272 | |
273 void DownloadItem::OpenDownload() { | |
274 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
275 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
276 | |
277 if (IsPartialDownload()) { | |
278 open_when_complete_ = !open_when_complete_; | |
279 } else if (IsComplete() && !file_externally_removed_) { | |
280 // Ideally, we want to detect errors in opening and report them, but we | |
281 // don't generally have the proper interface for that to the external | |
282 // program that opens the file. So instead we spawn a check to update | |
283 // the UI if the file has been deleted in parallel with the open. | |
284 download_manager_->CheckForFileRemoval(this); | |
285 opened_ = true; | |
286 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this)); | |
287 | |
288 // For testing: If download opening is disabled on this item, | |
289 // make the rest of the routine a no-op. | |
290 if (!open_enabled_) | |
291 return; | |
292 | |
293 if (is_extension_install()) { | |
294 download_crx_util::OpenChromeExtension(download_manager_->profile(), | |
295 *this); | |
296 return; | |
297 } | |
298 #if defined(OS_MACOSX) | |
299 // Mac OS X requires opening downloads on the UI thread. | |
300 platform_util::OpenItem(full_path()); | |
301 #else | |
302 BrowserThread::PostTask( | |
303 BrowserThread::FILE, FROM_HERE, | |
304 NewRunnableFunction(&platform_util::OpenItem, full_path())); | |
305 #endif | |
306 } | |
307 } | |
308 | |
309 void DownloadItem::ShowDownloadInShell() { | |
310 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
311 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
312 | |
313 #if defined(OS_MACOSX) | |
314 // Mac needs to run this operation on the UI thread. | |
315 platform_util::ShowItemInFolder(full_path()); | |
316 #else | |
317 BrowserThread::PostTask( | |
318 BrowserThread::FILE, FROM_HERE, | |
319 NewRunnableFunction(&platform_util::ShowItemInFolder, | |
320 full_path())); | |
321 #endif | |
322 } | |
323 | |
324 void DownloadItem::DangerousDownloadValidated() { | |
325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
326 DCHECK_EQ(DANGEROUS, safety_state()); | |
327 | |
328 UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated", | |
329 GetDangerType(), | |
330 DANGEROUS_TYPE_MAX); | |
331 | |
332 safety_state_ = DANGEROUS_BUT_VALIDATED; | |
333 UpdateObservers(); | |
334 | |
335 download_manager_->MaybeCompleteDownload(this); | |
336 } | |
337 | |
338 void DownloadItem::UpdateSize(int64 bytes_so_far) { | |
339 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
340 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
341 | |
342 received_bytes_ = bytes_so_far; | |
343 | |
344 // If we've received more data than we were expecting (bad server info?), | |
345 // revert to 'unknown size mode'. | |
346 if (received_bytes_ > total_bytes_) | |
347 total_bytes_ = 0; | |
348 } | |
349 | |
350 void DownloadItem::StartProgressTimer() { | |
351 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
352 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
353 | |
354 update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateTimeMs), this, | |
355 &DownloadItem::UpdateObservers); | |
356 } | |
357 | |
358 void DownloadItem::StopProgressTimer() { | |
359 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
360 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
361 | |
362 update_timer_.Stop(); | |
363 } | |
364 | |
365 // Updates from the download thread may have been posted while this download | |
366 // was being cancelled in the UI thread, so we'll accept them unless we're | |
367 // complete. | |
368 void DownloadItem::Update(int64 bytes_so_far) { | |
369 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
370 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
371 | |
372 if (!IsInProgress()) { | |
373 NOTREACHED(); | |
374 return; | |
375 } | |
376 UpdateSize(bytes_so_far); | |
377 UpdateObservers(); | |
378 } | |
379 | |
380 // Triggered by a user action. | |
381 void DownloadItem::Cancel(bool update_history) { | |
382 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
383 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
384 | |
385 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); | |
386 if (!IsPartialDownload()) { | |
387 // Small downloads might be complete before this method has | |
388 // a chance to run. | |
389 return; | |
390 } | |
391 | |
392 download_util::RecordDownloadCount(download_util::CANCELLED_COUNT); | |
393 | |
394 TransitionTo(CANCELLED); | |
395 StopProgressTimer(); | |
396 if (update_history) | |
397 download_manager_->DownloadCancelled(download_id_); | |
398 } | |
399 | |
400 void DownloadItem::MarkAsComplete() { | |
401 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
402 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
403 | |
404 DCHECK(all_data_saved_); | |
405 TransitionTo(COMPLETE); | |
406 } | |
407 | |
408 void DownloadItem::OnAllDataSaved(int64 size) { | |
409 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
410 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
411 | |
412 DCHECK(!all_data_saved_); | |
413 all_data_saved_ = true; | |
414 UpdateSize(size); | |
415 StopProgressTimer(); | |
416 } | |
417 | |
418 void DownloadItem::OnDownloadedFileRemoved() { | |
419 file_externally_removed_ = true; | |
420 UpdateObservers(); | |
421 } | |
422 | |
423 void DownloadItem::Completed() { | |
424 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
425 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
426 | |
427 VLOG(20) << __FUNCTION__ << "() " << DebugString(false); | |
428 | |
429 DCHECK(all_data_saved_); | |
430 TransitionTo(COMPLETE); | |
431 download_manager_->DownloadCompleted(id()); | |
432 download_util::RecordDownloadCompleted(start_tick_); | |
433 | |
434 if (is_extension_install()) { | |
435 // Extensions should already have been unpacked and opened. | |
436 DCHECK(auto_opened_); | |
437 } else if (open_when_complete() || | |
438 ShouldOpenFileBasedOnExtension() || | |
439 is_temporary()) { | |
440 // If the download is temporary, like in drag-and-drop, do not open it but | |
441 // we still need to set it auto-opened so that it can be removed from the | |
442 // download shelf. | |
443 if (!is_temporary()) | |
444 OpenDownload(); | |
445 | |
446 auto_opened_ = true; | |
447 UpdateObservers(); | |
448 } | |
449 } | |
450 | |
451 void DownloadItem::StartCrxInstall() { | |
452 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
453 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
454 | |
455 DCHECK(is_extension_install()); | |
456 DCHECK(all_data_saved_); | |
457 | |
458 scoped_refptr<CrxInstaller> crx_installer = | |
459 download_crx_util::OpenChromeExtension( | |
460 download_manager_->profile(), | |
461 *this); | |
462 | |
463 // CRX_INSTALLER_DONE will fire when the install completes. Observe() | |
464 // will call Completed() on this item. If this DownloadItem is not | |
465 // around when CRX_INSTALLER_DONE fires, Complete() will not be called. | |
466 registrar_.Add(this, | |
467 chrome::NOTIFICATION_CRX_INSTALLER_DONE, | |
468 Source<CrxInstaller>(crx_installer.get())); | |
469 | |
470 // The status text and percent complete indicator will change now | |
471 // that we are installing a CRX. Update observers so that they pick | |
472 // up the change. | |
473 UpdateObservers(); | |
474 } | |
475 | |
476 void DownloadItem::TransitionTo(DownloadState new_state) { | |
477 if (state_ == new_state) | |
478 return; | |
479 | |
480 state_ = new_state; | |
481 UpdateObservers(); | |
482 } | |
483 | |
484 void DownloadItem::UpdateSafetyState() { | |
485 SafetyState updated_value( | |
486 GetSafetyState(state_info_.is_dangerous_file, | |
487 state_info_.is_dangerous_url)); | |
488 if (updated_value != safety_state_) { | |
489 safety_state_ = updated_value; | |
490 UpdateObservers(); | |
491 } | |
492 } | |
493 | |
494 void DownloadItem::UpdateTarget() { | |
495 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
496 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
497 | |
498 if (state_info_.target_name.value().empty()) | |
499 state_info_.target_name = full_path_.BaseName(); | |
500 } | |
501 | |
502 // NotificationObserver implementation. | |
503 void DownloadItem::Observe(int type, | |
504 const NotificationSource& source, | |
505 const NotificationDetails& details) { | |
506 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
507 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
508 | |
509 DCHECK(type == chrome::NOTIFICATION_CRX_INSTALLER_DONE); | |
510 | |
511 // No need to listen for CRX_INSTALLER_DONE anymore. | |
512 registrar_.Remove(this, | |
513 chrome::NOTIFICATION_CRX_INSTALLER_DONE, | |
514 source); | |
515 | |
516 auto_opened_ = true; | |
517 DCHECK(all_data_saved_); | |
518 | |
519 Completed(); | |
520 } | |
521 | |
522 void DownloadItem::Interrupted(int64 size, int os_error) { | |
523 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
524 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
525 | |
526 if (!IsInProgress()) | |
527 return; | |
528 | |
529 last_os_error_ = os_error; | |
530 UpdateSize(size); | |
531 StopProgressTimer(); | |
532 download_util::RecordDownloadInterrupted(os_error, | |
533 received_bytes_, | |
534 total_bytes_); | |
535 TransitionTo(INTERRUPTED); | |
536 } | |
537 | |
538 void DownloadItem::Delete(DeleteReason reason) { | |
539 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
540 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
541 | |
542 switch (reason) { | |
543 case DELETE_DUE_TO_USER_DISCARD: | |
544 UMA_HISTOGRAM_ENUMERATION("Download.UserDiscard", GetDangerType(), | |
545 DANGEROUS_TYPE_MAX); | |
546 break; | |
547 case DELETE_DUE_TO_BROWSER_SHUTDOWN: | |
548 UMA_HISTOGRAM_ENUMERATION("Download.Discard", GetDangerType(), | |
549 DANGEROUS_TYPE_MAX); | |
550 break; | |
551 default: | |
552 NOTREACHED(); | |
553 } | |
554 | |
555 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
556 NewRunnableFunction(&DeleteDownloadedFile, full_path_)); | |
557 Remove(); | |
558 // We have now been deleted. | |
559 } | |
560 | |
561 void DownloadItem::Remove() { | |
562 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
563 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
564 | |
565 download_manager_->AssertQueueStateConsistent(this); | |
566 Cancel(true); | |
567 download_manager_->AssertQueueStateConsistent(this); | |
568 | |
569 TransitionTo(REMOVING); | |
570 download_manager_->RemoveDownload(db_handle_); | |
571 // We have now been deleted. | |
572 } | |
573 | |
574 bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const { | |
575 if (total_bytes_ <= 0) | |
576 return false; // We never received the content_length for this download. | |
577 | |
578 int64 speed = CurrentSpeed(); | |
579 if (speed == 0) | |
580 return false; | |
581 | |
582 *remaining = base::TimeDelta::FromSeconds( | |
583 (total_bytes_ - received_bytes_) / speed); | |
584 return true; | |
585 } | |
586 | |
587 int64 DownloadItem::CurrentSpeed() const { | |
588 if (is_paused_) | |
589 return 0; | |
590 base::TimeDelta diff = base::TimeTicks::Now() - start_tick_; | |
591 int64 diff_ms = diff.InMilliseconds(); | |
592 return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms; | |
593 } | |
594 | |
595 int DownloadItem::PercentComplete() const { | |
596 // We don't have an accurate way to estimate the time to unpack a CRX. | |
597 // The slowest part is re-encoding images, and time to do this depends on | |
598 // the contents of the image. If a CRX is being unpacked, indicate that | |
599 // we do not know how close to completion we are. | |
600 if (IsCrxInstallRuning() || total_bytes_ <= 0) | |
601 return -1; | |
602 | |
603 return static_cast<int>(received_bytes_ * 100.0 / total_bytes_); | |
604 } | |
605 | |
606 void DownloadItem::OnPathDetermined(const FilePath& path) { | |
607 full_path_ = path; | |
608 UpdateTarget(); | |
609 } | |
610 | |
611 void DownloadItem::Rename(const FilePath& full_path) { | |
612 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
613 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
614 | |
615 VLOG(20) << __FUNCTION__ << "()" | |
616 << " full_path = \"" << full_path.value() << "\"" | |
617 << " " << DebugString(true); | |
618 DCHECK(!full_path.empty()); | |
619 full_path_ = full_path; | |
620 } | |
621 | |
622 void DownloadItem::TogglePause() { | |
623 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
624 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
625 | |
626 DCHECK(IsInProgress()); | |
627 if (is_paused_) | |
628 request_handle_.ResumeRequest(); | |
629 else | |
630 request_handle_.PauseRequest(); | |
631 is_paused_ = !is_paused_; | |
632 UpdateObservers(); | |
633 } | |
634 | |
635 void DownloadItem::OnDownloadCompleting(DownloadFileManager* file_manager) { | |
636 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
637 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
638 | |
639 VLOG(20) << __FUNCTION__ << "()" | |
640 << " needs rename = " << NeedsRename() | |
641 << " " << DebugString(true); | |
642 DCHECK_NE(DANGEROUS, safety_state()); | |
643 DCHECK(file_manager); | |
644 | |
645 if (NeedsRename()) { | |
646 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
647 NewRunnableMethod(file_manager, | |
648 &DownloadFileManager::RenameCompletingDownloadFile, id(), | |
649 GetTargetFilePath(), safety_state() == SAFE)); | |
650 return; | |
651 } | |
652 | |
653 DCHECK(!is_extension_install()); | |
654 Completed(); | |
655 | |
656 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
657 NewRunnableMethod(file_manager, &DownloadFileManager::CompleteDownload, | |
658 id())); | |
659 } | |
660 | |
661 void DownloadItem::OnDownloadRenamedToFinalName(const FilePath& full_path) { | |
662 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
663 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
664 | |
665 VLOG(20) << __FUNCTION__ << "()" | |
666 << " full_path = \"" << full_path.value() << "\"" | |
667 << " needed rename = " << NeedsRename() | |
668 << " " << DebugString(false); | |
669 DCHECK(NeedsRename()); | |
670 | |
671 Rename(full_path); | |
672 | |
673 if (is_extension_install()) { | |
674 StartCrxInstall(); | |
675 // Completed() will be called when the installer finishes. | |
676 return; | |
677 } | |
678 | |
679 Completed(); | |
680 } | |
681 | |
682 bool DownloadItem::MatchesQuery(const string16& query) const { | |
683 if (query.empty()) | |
684 return true; | |
685 | |
686 DCHECK_EQ(query, base::i18n::ToLower(query)); | |
687 | |
688 string16 url_raw(base::i18n::ToLower(UTF8ToUTF16(GetURL().spec()))); | |
689 if (url_raw.find(query) != string16::npos) | |
690 return true; | |
691 | |
692 // TODO(phajdan.jr): write a test case for the following code. | |
693 // A good test case would be: | |
694 // "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", | |
695 // L"/\x4f60\x597d\x4f60\x597d", | |
696 // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD" | |
697 PrefService* prefs = download_manager_->profile()->GetPrefs(); | |
698 std::string languages(prefs->GetString(prefs::kAcceptLanguages)); | |
699 string16 url_formatted( | |
700 base::i18n::ToLower(net::FormatUrl(GetURL(), languages))); | |
701 if (url_formatted.find(query) != string16::npos) | |
702 return true; | |
703 | |
704 string16 path(base::i18n::ToLower(full_path().LossyDisplayName())); | |
705 // This shouldn't just do a substring match; it is wrong for Unicode | |
706 // due to normalization and we have a fancier search-query system | |
707 // used elsewhere. | |
708 // http://code.google.com/p/chromium/issues/detail?id=71982 | |
709 return (path.find(query) != string16::npos); | |
710 } | |
711 | |
712 void DownloadItem::SetFileCheckResults(const DownloadStateInfo& state) { | |
713 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
714 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
715 | |
716 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true); | |
717 state_info_ = state; | |
718 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true); | |
719 | |
720 UpdateSafetyState(); | |
721 } | |
722 | |
723 DownloadItem::DangerType DownloadItem::GetDangerType() const { | |
724 return ::GetDangerType(state_info_.is_dangerous_file, | |
725 state_info_.is_dangerous_url); | |
726 } | |
727 | |
728 bool DownloadItem::IsDangerous() const { | |
729 return GetDangerType() != DownloadItem::NOT_DANGEROUS; | |
730 } | |
731 | |
732 void DownloadItem::MarkFileDangerous() { | |
733 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
734 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
735 | |
736 state_info_.is_dangerous_file = true; | |
737 UpdateSafetyState(); | |
738 } | |
739 | |
740 void DownloadItem::MarkUrlDangerous() { | |
741 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
742 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
743 | |
744 state_info_.is_dangerous_url = true; | |
745 UpdateSafetyState(); | |
746 } | |
747 | |
748 DownloadHistoryInfo DownloadItem::GetHistoryInfo() const { | |
749 return DownloadHistoryInfo(full_path(), | |
750 GetURL(), | |
751 referrer_url(), | |
752 start_time(), | |
753 received_bytes(), | |
754 total_bytes(), | |
755 state(), | |
756 db_handle()); | |
757 } | |
758 | |
759 FilePath DownloadItem::GetTargetFilePath() const { | |
760 return full_path_.DirName().Append(state_info_.target_name); | |
761 } | |
762 | |
763 FilePath DownloadItem::GetFileNameToReportUser() const { | |
764 if (state_info_.path_uniquifier > 0) { | |
765 FilePath name(state_info_.target_name); | |
766 download_util::AppendNumberToPath(&name, state_info_.path_uniquifier); | |
767 return name; | |
768 } | |
769 return state_info_.target_name; | |
770 } | |
771 | |
772 FilePath DownloadItem::GetUserVerifiedFilePath() const { | |
773 return (safety_state_ == DownloadItem::SAFE) ? | |
774 GetTargetFilePath() : full_path_; | |
775 } | |
776 | |
777 void DownloadItem::Init(bool active) { | |
778 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. | |
779 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
780 | |
781 UpdateTarget(); | |
782 if (active) { | |
783 StartProgressTimer(); | |
784 download_util::RecordDownloadCount(download_util::START_COUNT); | |
785 } | |
786 VLOG(20) << __FUNCTION__ << "() " << DebugString(true); | |
787 } | |
788 | |
789 // TODO(ahendrickson) -- Move |INTERRUPTED| from |IsCancelled()| to | |
790 // |IsPartialDownload()|, when resuming interrupted downloads is implemented. | |
791 bool DownloadItem::IsPartialDownload() const { | |
792 return (state_ == IN_PROGRESS); | |
793 } | |
794 | |
795 bool DownloadItem::IsInProgress() const { | |
796 return (state_ == IN_PROGRESS); | |
797 } | |
798 | |
799 bool DownloadItem::IsCancelled() const { | |
800 return (state_ == CANCELLED) || | |
801 (state_ == INTERRUPTED); | |
802 } | |
803 | |
804 bool DownloadItem::IsInterrupted() const { | |
805 return (state_ == INTERRUPTED); | |
806 } | |
807 | |
808 bool DownloadItem::IsComplete() const { | |
809 return (state_ == COMPLETE); | |
810 } | |
811 | |
812 const GURL& DownloadItem::GetURL() const { | |
813 return url_chain_.empty() ? | |
814 GURL::EmptyGURL() : url_chain_.back(); | |
815 } | |
816 | |
817 std::string DownloadItem::DebugString(bool verbose) const { | |
818 std::string description = | |
819 base::StringPrintf("{ id = %d" | |
820 " state = %s", | |
821 download_id_, | |
822 DebugDownloadStateString(state())); | |
823 | |
824 // Construct a string of the URL chain. | |
825 std::string url_list("<none>"); | |
826 if (!url_chain_.empty()) { | |
827 std::vector<GURL>::const_iterator iter = url_chain_.begin(); | |
828 std::vector<GURL>::const_iterator last = url_chain_.end(); | |
829 url_list = (*iter).spec(); | |
830 ++iter; | |
831 for ( ; verbose && (iter != last); ++iter) { | |
832 url_list += " ->\n\t"; | |
833 const GURL& next_url = *iter; | |
834 url_list += next_url.spec(); | |
835 } | |
836 } | |
837 | |
838 if (verbose) { | |
839 description += base::StringPrintf( | |
840 " db_handle = %" PRId64 | |
841 " total_bytes = %" PRId64 | |
842 " received_bytes = %" PRId64 | |
843 " is_paused = %c" | |
844 " is_extension_install = %c" | |
845 " is_otr = %c" | |
846 " safety_state = %s" | |
847 " url_chain = \n\t\"%s\"\n\t" | |
848 " target_name = \"%" PRFilePath "\"" | |
849 " full_path = \"%" PRFilePath "\"", | |
850 db_handle(), | |
851 total_bytes(), | |
852 received_bytes(), | |
853 is_paused() ? 'T' : 'F', | |
854 is_extension_install() ? 'T' : 'F', | |
855 is_otr() ? 'T' : 'F', | |
856 DebugSafetyStateString(safety_state()), | |
857 url_list.c_str(), | |
858 state_info_.target_name.value().c_str(), | |
859 full_path().value().c_str()); | |
860 } else { | |
861 description += base::StringPrintf(" url = \"%s\"", url_list.c_str()); | |
862 } | |
863 | |
864 description += " }"; | |
865 | |
866 return description; | |
867 } | |
OLD | NEW |