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/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/stringprintf.h"
13 #include "base/timer.h"
14 #include "base/utf_string_conversions.h"
15 #include "net/base/net_util.h"
16 #include "chrome/browser/download/download_extensions.h"
17 #include "chrome/browser/download/download_file_manager.h"
18 #include "chrome/browser/download/download_history.h"
19 #include "chrome/browser/download/download_manager.h"
20 #include "chrome/browser/download/download_prefs.h"
21 #include "chrome/browser/download/download_util.h"
22 #include "chrome/browser/history/download_create_info.h"
23 #include "chrome/browser/platform_util.h"
24 #include "chrome/browser/prefs/pref_service.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/extensions/extension.h"
27 #include "chrome/common/pref_names.h"
28 #include "content/browser/browser_thread.h"
29 #include "ui/base/l10n/l10n_util.h"
30
31 // A DownloadItem normally goes through the following states:
32 // * Created (when download starts)
33 // * Made visible to consumers (e.g. Javascript) after the
34 // destination file has been determined.
35 // * Entered into the history database.
36 // * Made visible in the download shelf.
37 // * All data is saved. Note that the actual data download occurs
38 // in parallel with the above steps, but until those steps are
39 // complete, completion of the data download will be ignored.
40 // * Download file is renamed to its final name, and possibly
41 // auto-opened.
42 // TODO(rdsmith): This progress should be reflected in
43 // DownloadItem::DownloadState and a state transition table/state diagram.
44 //
45 // TODO(rdsmith): This description should be updated to reflect the cancel
46 // pathways.
47
48 namespace {
49
50 // Update frequency (milliseconds).
51 const int kUpdateTimeMs = 1000;
52
DeleteDownloadedFile(const FilePath & path)53 void DeleteDownloadedFile(const FilePath& path) {
54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
55
56 // Make sure we only delete files.
57 if (!file_util::DirectoryExists(path))
58 file_util::Delete(path, false);
59 }
60
DebugSafetyStateString(DownloadItem::SafetyState state)61 const char* DebugSafetyStateString(DownloadItem::SafetyState state) {
62 switch (state) {
63 case DownloadItem::SAFE:
64 return "SAFE";
65 case DownloadItem::DANGEROUS:
66 return "DANGEROUS";
67 case DownloadItem::DANGEROUS_BUT_VALIDATED:
68 return "DANGEROUS_BUT_VALIDATED";
69 default:
70 NOTREACHED() << "Unknown safety state " << state;
71 return "unknown";
72 };
73 }
74
DebugDownloadStateString(DownloadItem::DownloadState state)75 const char* DebugDownloadStateString(DownloadItem::DownloadState state) {
76 switch (state) {
77 case DownloadItem::IN_PROGRESS:
78 return "IN_PROGRESS";
79 case DownloadItem::COMPLETE:
80 return "COMPLETE";
81 case DownloadItem::CANCELLED:
82 return "CANCELLED";
83 case DownloadItem::REMOVING:
84 return "REMOVING";
85 case DownloadItem::INTERRUPTED:
86 return "INTERRUPTED";
87 default:
88 NOTREACHED() << "Unknown download state " << state;
89 return "unknown";
90 };
91 }
92
GetSafetyState(bool dangerous_file,bool dangerous_url)93 DownloadItem::SafetyState GetSafetyState(bool dangerous_file,
94 bool dangerous_url) {
95 return (dangerous_url || dangerous_file) ?
96 DownloadItem::DANGEROUS : DownloadItem::SAFE;
97 }
98
99 // Note: When a download has both |dangerous_file| and |dangerous_url| set,
100 // danger type is set to DANGEROUS_URL since the risk of dangerous URL
101 // overweights that of dangerous file type.
GetDangerType(bool dangerous_file,bool dangerous_url)102 DownloadItem::DangerType GetDangerType(bool dangerous_file,
103 bool dangerous_url) {
104 if (dangerous_url) {
105 // dangerous URL overweights dangerous file. We check dangerous URL first.
106 return DownloadItem::DANGEROUS_URL;
107 } else if (dangerous_file) {
108 return DownloadItem::DANGEROUS_FILE;
109 }
110 return DownloadItem::NOT_DANGEROUS;
111 }
112
113 } // namespace
114
115 // Constructor for reading from the history service.
DownloadItem(DownloadManager * download_manager,const DownloadCreateInfo & info)116 DownloadItem::DownloadItem(DownloadManager* download_manager,
117 const DownloadCreateInfo& info)
118 : id_(-1),
119 full_path_(info.path),
120 path_uniquifier_(0),
121 url_chain_(info.url_chain),
122 referrer_url_(info.referrer_url),
123 mime_type_(info.mime_type),
124 original_mime_type_(info.original_mime_type),
125 total_bytes_(info.total_bytes),
126 received_bytes_(info.received_bytes),
127 start_tick_(base::TimeTicks()),
128 state_(static_cast<DownloadState>(info.state)),
129 start_time_(info.start_time),
130 db_handle_(info.db_handle),
131 download_manager_(download_manager),
132 is_paused_(false),
133 open_when_complete_(false),
134 safety_state_(SAFE),
135 danger_type_(NOT_DANGEROUS),
136 auto_opened_(false),
137 target_name_(info.original_name),
138 render_process_id_(-1),
139 request_id_(-1),
140 save_as_(false),
141 is_otr_(false),
142 is_extension_install_(info.is_extension_install),
143 name_finalized_(false),
144 is_temporary_(false),
145 all_data_saved_(false),
146 opened_(false) {
147 if (IsInProgress())
148 state_ = CANCELLED;
149 if (IsComplete())
150 all_data_saved_ = true;
151 Init(false /* don't start progress timer */);
152 }
153
154 // Constructing for a regular download:
DownloadItem(DownloadManager * download_manager,const DownloadCreateInfo & info,bool is_otr)155 DownloadItem::DownloadItem(DownloadManager* download_manager,
156 const DownloadCreateInfo& info,
157 bool is_otr)
158 : id_(info.download_id),
159 full_path_(info.path),
160 path_uniquifier_(info.path_uniquifier),
161 url_chain_(info.url_chain),
162 referrer_url_(info.referrer_url),
163 mime_type_(info.mime_type),
164 original_mime_type_(info.original_mime_type),
165 total_bytes_(info.total_bytes),
166 received_bytes_(0),
167 last_os_error_(0),
168 start_tick_(base::TimeTicks::Now()),
169 state_(IN_PROGRESS),
170 start_time_(info.start_time),
171 db_handle_(DownloadHistory::kUninitializedHandle),
172 download_manager_(download_manager),
173 is_paused_(false),
174 open_when_complete_(false),
175 safety_state_(GetSafetyState(info.is_dangerous_file,
176 info.is_dangerous_url)),
177 danger_type_(GetDangerType(info.is_dangerous_file,
178 info.is_dangerous_url)),
179 auto_opened_(false),
180 target_name_(info.original_name),
181 render_process_id_(info.child_id),
182 request_id_(info.request_id),
183 save_as_(info.prompt_user_for_save_location),
184 is_otr_(is_otr),
185 is_extension_install_(info.is_extension_install),
186 name_finalized_(false),
187 is_temporary_(!info.save_info.file_path.empty()),
188 all_data_saved_(false),
189 opened_(false) {
190 Init(true /* start progress timer */);
191 }
192
193 // Constructing for the "Save Page As..." feature:
DownloadItem(DownloadManager * download_manager,const FilePath & path,const GURL & url,bool is_otr)194 DownloadItem::DownloadItem(DownloadManager* download_manager,
195 const FilePath& path,
196 const GURL& url,
197 bool is_otr)
198 : id_(1),
199 full_path_(path),
200 path_uniquifier_(0),
201 url_chain_(1, url),
202 referrer_url_(GURL()),
203 mime_type_(std::string()),
204 original_mime_type_(std::string()),
205 total_bytes_(0),
206 received_bytes_(0),
207 last_os_error_(0),
208 start_tick_(base::TimeTicks::Now()),
209 state_(IN_PROGRESS),
210 start_time_(base::Time::Now()),
211 db_handle_(DownloadHistory::kUninitializedHandle),
212 download_manager_(download_manager),
213 is_paused_(false),
214 open_when_complete_(false),
215 safety_state_(SAFE),
216 danger_type_(NOT_DANGEROUS),
217 auto_opened_(false),
218 render_process_id_(-1),
219 request_id_(-1),
220 save_as_(false),
221 is_otr_(is_otr),
222 is_extension_install_(false),
223 name_finalized_(false),
224 is_temporary_(false),
225 all_data_saved_(false),
226 opened_(false) {
227 Init(true /* start progress timer */);
228 }
229
~DownloadItem()230 DownloadItem::~DownloadItem() {
231 state_ = REMOVING;
232 UpdateObservers();
233 }
234
AddObserver(Observer * observer)235 void DownloadItem::AddObserver(Observer* observer) {
236 observers_.AddObserver(observer);
237 }
238
RemoveObserver(Observer * observer)239 void DownloadItem::RemoveObserver(Observer* observer) {
240 observers_.RemoveObserver(observer);
241 }
242
UpdateObservers()243 void DownloadItem::UpdateObservers() {
244 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
245 }
246
CanOpenDownload()247 bool DownloadItem::CanOpenDownload() {
248 return !Extension::IsExtension(target_name_);
249 }
250
ShouldOpenFileBasedOnExtension()251 bool DownloadItem::ShouldOpenFileBasedOnExtension() {
252 return download_manager_->ShouldOpenFileBasedOnExtension(
253 GetUserVerifiedFilePath());
254 }
255
OpenFilesBasedOnExtension(bool open)256 void DownloadItem::OpenFilesBasedOnExtension(bool open) {
257 DownloadPrefs* prefs = download_manager_->download_prefs();
258 if (open)
259 prefs->EnableAutoOpenBasedOnExtension(GetUserVerifiedFilePath());
260 else
261 prefs->DisableAutoOpenBasedOnExtension(GetUserVerifiedFilePath());
262 }
263
OpenDownload()264 void DownloadItem::OpenDownload() {
265 if (IsPartialDownload()) {
266 open_when_complete_ = !open_when_complete_;
267 } else if (IsComplete()) {
268 opened_ = true;
269 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this));
270 if (is_extension_install()) {
271 download_util::OpenChromeExtension(download_manager_->profile(),
272 download_manager_,
273 *this);
274 return;
275 }
276 #if defined(OS_MACOSX)
277 // Mac OS X requires opening downloads on the UI thread.
278 platform_util::OpenItem(full_path());
279 #else
280 BrowserThread::PostTask(
281 BrowserThread::FILE, FROM_HERE,
282 NewRunnableFunction(&platform_util::OpenItem, full_path()));
283 #endif
284 }
285 }
286
ShowDownloadInShell()287 void DownloadItem::ShowDownloadInShell() {
288 #if defined(OS_MACOSX)
289 // Mac needs to run this operation on the UI thread.
290 platform_util::ShowItemInFolder(full_path());
291 #else
292 BrowserThread::PostTask(
293 BrowserThread::FILE, FROM_HERE,
294 NewRunnableFunction(&platform_util::ShowItemInFolder,
295 full_path()));
296 #endif
297 }
298
DangerousDownloadValidated()299 void DownloadItem::DangerousDownloadValidated() {
300 UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated",
301 danger_type_,
302 DANGEROUS_TYPE_MAX);
303 download_manager_->DangerousDownloadValidated(this);
304 }
305
UpdateSize(int64 bytes_so_far)306 void DownloadItem::UpdateSize(int64 bytes_so_far) {
307 received_bytes_ = bytes_so_far;
308
309 // If we've received more data than we were expecting (bad server info?),
310 // revert to 'unknown size mode'.
311 if (received_bytes_ > total_bytes_)
312 total_bytes_ = 0;
313 }
314
StartProgressTimer()315 void DownloadItem::StartProgressTimer() {
316 update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateTimeMs), this,
317 &DownloadItem::UpdateObservers);
318 }
319
StopProgressTimer()320 void DownloadItem::StopProgressTimer() {
321 update_timer_.Stop();
322 }
323
324 // Updates from the download thread may have been posted while this download
325 // was being cancelled in the UI thread, so we'll accept them unless we're
326 // complete.
Update(int64 bytes_so_far)327 void DownloadItem::Update(int64 bytes_so_far) {
328 if (!IsInProgress()) {
329 NOTREACHED();
330 return;
331 }
332 UpdateSize(bytes_so_far);
333 UpdateObservers();
334 }
335
336 // Triggered by a user action.
Cancel(bool update_history)337 void DownloadItem::Cancel(bool update_history) {
338 VLOG(20) << __FUNCTION__ << "()" << " download = " << DebugString(true);
339 if (!IsPartialDownload()) {
340 // Small downloads might be complete before this method has
341 // a chance to run.
342 return;
343 }
344
345 download_util::RecordDownloadCount(download_util::CANCELLED_COUNT);
346
347 state_ = CANCELLED;
348 UpdateObservers();
349 StopProgressTimer();
350 if (update_history)
351 download_manager_->DownloadCancelled(id_);
352 }
353
MarkAsComplete()354 void DownloadItem::MarkAsComplete() {
355 DCHECK(all_data_saved_);
356 state_ = COMPLETE;
357 UpdateObservers();
358 }
359
OnAllDataSaved(int64 size)360 void DownloadItem::OnAllDataSaved(int64 size) {
361 DCHECK(!all_data_saved_);
362 all_data_saved_ = true;
363 UpdateSize(size);
364 StopProgressTimer();
365 }
366
Completed()367 void DownloadItem::Completed() {
368 VLOG(20) << " " << __FUNCTION__ << "() "
369 << DebugString(false);
370
371 download_util::RecordDownloadCount(download_util::COMPLETED_COUNT);
372
373 // Handle chrome extensions explicitly and skip the shell execute.
374 if (is_extension_install()) {
375 download_util::OpenChromeExtension(download_manager_->profile(),
376 download_manager_,
377 *this);
378 auto_opened_ = true;
379 } else if (open_when_complete() ||
380 download_manager_->ShouldOpenFileBasedOnExtension(
381 GetUserVerifiedFilePath()) ||
382 is_temporary()) {
383 // If the download is temporary, like in drag-and-drop, do not open it but
384 // we still need to set it auto-opened so that it can be removed from the
385 // download shelf.
386 if (!is_temporary())
387 OpenDownload();
388 auto_opened_ = true;
389 }
390
391 DCHECK(all_data_saved_);
392 state_ = COMPLETE;
393 UpdateObservers();
394 download_manager_->DownloadCompleted(id());
395 }
396
Interrupted(int64 size,int os_error)397 void DownloadItem::Interrupted(int64 size, int os_error) {
398 if (!IsInProgress())
399 return;
400 state_ = INTERRUPTED;
401 last_os_error_ = os_error;
402 UpdateSize(size);
403 StopProgressTimer();
404 UpdateObservers();
405 }
406
Delete(DeleteReason reason)407 void DownloadItem::Delete(DeleteReason reason) {
408 switch (reason) {
409 case DELETE_DUE_TO_USER_DISCARD:
410 UMA_HISTOGRAM_ENUMERATION("Download.UserDiscard",
411 danger_type_,
412 DANGEROUS_TYPE_MAX);
413 break;
414 case DELETE_DUE_TO_BROWSER_SHUTDOWN:
415 UMA_HISTOGRAM_ENUMERATION("Download.Discard",
416 danger_type_,
417 DANGEROUS_TYPE_MAX);
418 break;
419 default:
420 NOTREACHED();
421 }
422
423 BrowserThread::PostTask(
424 BrowserThread::FILE, FROM_HERE,
425 NewRunnableFunction(&DeleteDownloadedFile, full_path_));
426 Remove();
427 // We have now been deleted.
428 }
429
Remove()430 void DownloadItem::Remove() {
431 Cancel(true);
432 state_ = REMOVING;
433 download_manager_->RemoveDownload(db_handle_);
434 // We have now been deleted.
435 }
436
TimeRemaining(base::TimeDelta * remaining) const437 bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const {
438 if (total_bytes_ <= 0)
439 return false; // We never received the content_length for this download.
440
441 int64 speed = CurrentSpeed();
442 if (speed == 0)
443 return false;
444
445 *remaining =
446 base::TimeDelta::FromSeconds((total_bytes_ - received_bytes_) / speed);
447 return true;
448 }
449
CurrentSpeed() const450 int64 DownloadItem::CurrentSpeed() const {
451 if (is_paused_)
452 return 0;
453 base::TimeDelta diff = base::TimeTicks::Now() - start_tick_;
454 int64 diff_ms = diff.InMilliseconds();
455 return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms;
456 }
457
PercentComplete() const458 int DownloadItem::PercentComplete() const {
459 int percent = -1;
460 if (total_bytes_ > 0)
461 percent = static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
462 return percent;
463 }
464
Rename(const FilePath & full_path)465 void DownloadItem::Rename(const FilePath& full_path) {
466 VLOG(20) << " " << __FUNCTION__ << "()"
467 << " full_path = \"" << full_path.value() << "\""
468 << DebugString(true);
469 DCHECK(!full_path.empty());
470 full_path_ = full_path;
471 }
472
TogglePause()473 void DownloadItem::TogglePause() {
474 DCHECK(IsInProgress());
475 download_manager_->PauseDownload(id_, !is_paused_);
476 is_paused_ = !is_paused_;
477 UpdateObservers();
478 }
479
OnNameFinalized()480 void DownloadItem::OnNameFinalized() {
481 VLOG(20) << " " << __FUNCTION__ << "() "
482 << DebugString(true);
483 name_finalized_ = true;
484
485 // We can't reach this point in the code without having received all the
486 // data, so it's safe to move to the COMPLETE state.
487 DCHECK(all_data_saved_);
488 state_ = COMPLETE;
489 UpdateObservers();
490 download_manager_->DownloadCompleted(id());
491 }
492
OnDownloadCompleting(DownloadFileManager * file_manager)493 void DownloadItem::OnDownloadCompleting(DownloadFileManager* file_manager) {
494 VLOG(20) << " " << __FUNCTION__ << "() "
495 << " needs rename = " << NeedsRename()
496 << " " << DebugString(true);
497 DCHECK_NE(DANGEROUS, safety_state());
498 DCHECK(file_manager);
499
500 if (NeedsRename()) {
501 BrowserThread::PostTask(
502 BrowserThread::FILE, FROM_HERE,
503 NewRunnableMethod(
504 file_manager, &DownloadFileManager::RenameCompletingDownloadFile,
505 id(), GetTargetFilePath(), safety_state() == SAFE));
506 return;
507 } else {
508 name_finalized_ = true;
509 }
510
511 Completed();
512
513 BrowserThread::PostTask(
514 BrowserThread::FILE, FROM_HERE,
515 NewRunnableMethod(
516 file_manager, &DownloadFileManager::CompleteDownload, id()));
517 }
518
OnDownloadRenamedToFinalName(const FilePath & full_path)519 void DownloadItem::OnDownloadRenamedToFinalName(const FilePath& full_path) {
520 VLOG(20) << " " << __FUNCTION__ << "()"
521 << " full_path = " << full_path.value()
522 << " needed rename = " << NeedsRename()
523 << " " << DebugString(false);
524 DCHECK(NeedsRename());
525
526 Rename(full_path);
527 OnNameFinalized();
528
529 Completed();
530 }
531
MatchesQuery(const string16 & query) const532 bool DownloadItem::MatchesQuery(const string16& query) const {
533 if (query.empty())
534 return true;
535
536 DCHECK_EQ(query, l10n_util::ToLower(query));
537
538 string16 url_raw(l10n_util::ToLower(UTF8ToUTF16(url().spec())));
539 if (url_raw.find(query) != string16::npos)
540 return true;
541
542 // TODO(phajdan.jr): write a test case for the following code.
543 // A good test case would be:
544 // "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd",
545 // L"/\x4f60\x597d\x4f60\x597d",
546 // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD"
547 PrefService* prefs = download_manager_->profile()->GetPrefs();
548 std::string languages(prefs->GetString(prefs::kAcceptLanguages));
549 string16 url_formatted(l10n_util::ToLower(net::FormatUrl(url(), languages)));
550 if (url_formatted.find(query) != string16::npos)
551 return true;
552
553 string16 path(l10n_util::ToLower(full_path().LossyDisplayName()));
554 // This shouldn't just do a substring match; it is wrong for Unicode
555 // due to normalization and we have a fancier search-query system
556 // used elsewhere.
557 // http://code.google.com/p/chromium/issues/detail?id=71982
558 if (path.find(query) != string16::npos)
559 return true;
560
561 return false;
562 }
563
SetFileCheckResults(const FilePath & path,bool is_dangerous_file,bool is_dangerous_url,int path_uniquifier,bool prompt,bool is_extension_install,const FilePath & original_name)564 void DownloadItem::SetFileCheckResults(const FilePath& path,
565 bool is_dangerous_file,
566 bool is_dangerous_url,
567 int path_uniquifier,
568 bool prompt,
569 bool is_extension_install,
570 const FilePath& original_name) {
571 VLOG(20) << " " << __FUNCTION__ << "()"
572 << " path = \"" << path.value() << "\""
573 << " is_dangerous_file = " << is_dangerous_file
574 << " is_dangerous_url = " << is_dangerous_url
575 << " path_uniquifier = " << path_uniquifier
576 << " prompt = " << prompt
577 << " is_extension_install = " << is_extension_install
578 << " path = \"" << path.value() << "\""
579 << " original_name = \"" << original_name.value() << "\""
580 << " " << DebugString(true);
581 // Make sure the initial file name is set only once.
582 DCHECK(full_path_.empty());
583 DCHECK(!path.empty());
584
585 full_path_ = path;
586 safety_state_ = GetSafetyState(is_dangerous_file, is_dangerous_url);
587 danger_type_ = GetDangerType(is_dangerous_file, is_dangerous_url);
588 path_uniquifier_ = path_uniquifier;
589 save_as_ = prompt;
590 is_extension_install_ = is_extension_install;
591 target_name_ = original_name;
592
593 if (target_name_.value().empty())
594 target_name_ = full_path_.BaseName();
595 }
596
GetTargetFilePath() const597 FilePath DownloadItem::GetTargetFilePath() const {
598 return full_path_.DirName().Append(target_name_);
599 }
600
GetFileNameToReportUser() const601 FilePath DownloadItem::GetFileNameToReportUser() const {
602 if (path_uniquifier_ > 0) {
603 FilePath name(target_name_);
604 download_util::AppendNumberToPath(&name, path_uniquifier_);
605 return name;
606 }
607 return target_name_;
608 }
609
GetUserVerifiedFilePath() const610 FilePath DownloadItem::GetUserVerifiedFilePath() const {
611 if (safety_state_ == DownloadItem::SAFE)
612 return GetTargetFilePath();
613 return full_path_;
614 }
615
Init(bool start_timer)616 void DownloadItem::Init(bool start_timer) {
617 if (target_name_.value().empty())
618 target_name_ = full_path_.BaseName();
619 if (start_timer)
620 StartProgressTimer();
621 VLOG(20) << " " << __FUNCTION__ << "() " << DebugString(true);
622 }
623
624 // TODO(ahendrickson) -- Move |INTERRUPTED| from |IsCancelled()| to
625 // |IsPartialDownload()|, when resuming interrupted downloads is implemented.
IsPartialDownload() const626 bool DownloadItem::IsPartialDownload() const {
627 return (state_ == IN_PROGRESS);
628 }
629
IsInProgress() const630 bool DownloadItem::IsInProgress() const {
631 return (state_ == IN_PROGRESS);
632 }
633
IsCancelled() const634 bool DownloadItem::IsCancelled() const {
635 return (state_ == CANCELLED) || (state_ == INTERRUPTED);
636 }
637
IsInterrupted() const638 bool DownloadItem::IsInterrupted() const {
639 return (state_ == INTERRUPTED);
640 }
641
IsComplete() const642 bool DownloadItem::IsComplete() const {
643 return state() == COMPLETE;
644 }
645
DebugString(bool verbose) const646 std::string DownloadItem::DebugString(bool verbose) const {
647 std::string description =
648 base::StringPrintf("{ id_ = %d"
649 " state = %s",
650 id_,
651 DebugDownloadStateString(state()));
652
653 if (verbose) {
654 description += base::StringPrintf(
655 " db_handle = %" PRId64
656 " total_bytes = %" PRId64
657 " is_paused = " "%c"
658 " is_extension_install = " "%c"
659 " is_otr = " "%c"
660 " safety_state = " "%s"
661 " url = " "\"%s\""
662 " target_name_ = \"%" PRFilePath "\""
663 " full_path = \"%" PRFilePath "\"",
664 db_handle(),
665 total_bytes(),
666 is_paused() ? 'T' : 'F',
667 is_extension_install() ? 'T' : 'F',
668 is_otr() ? 'T' : 'F',
669 DebugSafetyStateString(safety_state()),
670 url().spec().c_str(),
671 target_name_.value().c_str(),
672 full_path().value().c_str());
673 } else {
674 description += base::StringPrintf(" url = \"%s\"", url().spec().c_str());
675 }
676
677 description += " }";
678
679 return description;
680 }
681