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 // File method ordering: Methods in this file are in the same order as
6 // in download_item_impl.h, with the following exception: The public
7 // interface Start is placed in chronological order with the other
8 // (private) routines that together define a DownloadItem's state
9 // transitions as the download progresses. See "Download progression
10 // cascade" later in this file.
11
12 // A regular DownloadItem (created for a download in this session of the
13 // browser) normally goes through the following states:
14 // * Created (when download starts)
15 // * Destination filename determined
16 // * Entered into the history database.
17 // * Made visible in the download shelf.
18 // * All the data is saved. Note that the actual data download occurs
19 // in parallel with the above steps, but until those steps are
20 // complete, the state of the data save will be ignored.
21 // * Download file is renamed to its final name, and possibly
22 // auto-opened.
23
24 #include "content/browser/download/download_item_impl.h"
25
26 #include <vector>
27
28 #include "base/basictypes.h"
29 #include "base/bind.h"
30 #include "base/command_line.h"
31 #include "base/file_util.h"
32 #include "base/format_macros.h"
33 #include "base/logging.h"
34 #include "base/metrics/histogram.h"
35 #include "base/stl_util.h"
36 #include "base/strings/stringprintf.h"
37 #include "base/strings/utf_string_conversions.h"
38 #include "content/browser/download/download_create_info.h"
39 #include "content/browser/download/download_file.h"
40 #include "content/browser/download/download_interrupt_reasons_impl.h"
41 #include "content/browser/download/download_item_impl_delegate.h"
42 #include "content/browser/download/download_request_handle.h"
43 #include "content/browser/download/download_stats.h"
44 #include "content/browser/renderer_host/render_view_host_impl.h"
45 #include "content/browser/web_contents/web_contents_impl.h"
46 #include "content/public/browser/browser_context.h"
47 #include "content/public/browser/browser_thread.h"
48 #include "content/public/browser/content_browser_client.h"
49 #include "content/public/browser/download_danger_type.h"
50 #include "content/public/browser/download_interrupt_reasons.h"
51 #include "content/public/browser/download_url_parameters.h"
52 #include "content/public/common/content_switches.h"
53 #include "content/public/common/referrer.h"
54 #include "net/base/net_util.h"
55
56 namespace content {
57
58 namespace {
59
DeleteDownloadedFile(const base::FilePath & path)60 void DeleteDownloadedFile(const base::FilePath& path) {
61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
62
63 // Make sure we only delete files.
64 if (!base::DirectoryExists(path))
65 base::DeleteFile(path, false);
66 }
67
68 // Wrapper around DownloadFile::Detach and DownloadFile::Cancel that
69 // takes ownership of the DownloadFile and hence implicitly destroys it
70 // at the end of the function.
DownloadFileDetach(scoped_ptr<DownloadFile> download_file)71 static base::FilePath DownloadFileDetach(
72 scoped_ptr<DownloadFile> download_file) {
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
74 base::FilePath full_path = download_file->FullPath();
75 download_file->Detach();
76 return full_path;
77 }
78
DownloadFileCancel(scoped_ptr<DownloadFile> download_file)79 static void DownloadFileCancel(scoped_ptr<DownloadFile> download_file) {
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
81 download_file->Cancel();
82 }
83
IsDownloadResumptionEnabled()84 bool IsDownloadResumptionEnabled() {
85 return CommandLine::ForCurrentProcess()->HasSwitch(
86 switches::kEnableDownloadResumption);
87 }
88
89 } // namespace
90
91 const uint32 DownloadItem::kInvalidId = 0;
92
93 const char DownloadItem::kEmptyFileHash[] = "";
94
95 // The maximum number of attempts we will make to resume automatically.
96 const int DownloadItemImpl::kMaxAutoResumeAttempts = 5;
97
98 // Constructor for reading from the history service.
DownloadItemImpl(DownloadItemImplDelegate * delegate,uint32 download_id,const base::FilePath & current_path,const base::FilePath & target_path,const std::vector<GURL> & url_chain,const GURL & referrer_url,const base::Time & start_time,const base::Time & end_time,const std::string & etag,const std::string & last_modified,int64 received_bytes,int64 total_bytes,DownloadItem::DownloadState state,DownloadDangerType danger_type,DownloadInterruptReason interrupt_reason,bool opened,const net::BoundNetLog & bound_net_log)99 DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
100 uint32 download_id,
101 const base::FilePath& current_path,
102 const base::FilePath& target_path,
103 const std::vector<GURL>& url_chain,
104 const GURL& referrer_url,
105 const base::Time& start_time,
106 const base::Time& end_time,
107 const std::string& etag,
108 const std::string& last_modified,
109 int64 received_bytes,
110 int64 total_bytes,
111 DownloadItem::DownloadState state,
112 DownloadDangerType danger_type,
113 DownloadInterruptReason interrupt_reason,
114 bool opened,
115 const net::BoundNetLog& bound_net_log)
116 : is_save_package_download_(false),
117 download_id_(download_id),
118 current_path_(current_path),
119 target_path_(target_path),
120 target_disposition_(TARGET_DISPOSITION_OVERWRITE),
121 url_chain_(url_chain),
122 referrer_url_(referrer_url),
123 transition_type_(PAGE_TRANSITION_LINK),
124 has_user_gesture_(false),
125 total_bytes_(total_bytes),
126 received_bytes_(received_bytes),
127 bytes_per_sec_(0),
128 last_modified_time_(last_modified),
129 etag_(etag),
130 last_reason_(interrupt_reason),
131 start_tick_(base::TimeTicks()),
132 state_(ExternalToInternalState(state)),
133 danger_type_(danger_type),
134 start_time_(start_time),
135 end_time_(end_time),
136 delegate_(delegate),
137 is_paused_(false),
138 auto_resume_count_(0),
139 open_when_complete_(false),
140 file_externally_removed_(false),
141 auto_opened_(false),
142 is_temporary_(false),
143 all_data_saved_(state == COMPLETE),
144 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
145 opened_(opened),
146 delegate_delayed_complete_(false),
147 bound_net_log_(bound_net_log),
148 weak_ptr_factory_(this) {
149 delegate_->Attach();
150 DCHECK_NE(IN_PROGRESS_INTERNAL, state_);
151 Init(false /* not actively downloading */, SRC_HISTORY_IMPORT);
152 }
153
154 // Constructing for a regular download:
DownloadItemImpl(DownloadItemImplDelegate * delegate,uint32 download_id,const DownloadCreateInfo & info,const net::BoundNetLog & bound_net_log)155 DownloadItemImpl::DownloadItemImpl(
156 DownloadItemImplDelegate* delegate,
157 uint32 download_id,
158 const DownloadCreateInfo& info,
159 const net::BoundNetLog& bound_net_log)
160 : is_save_package_download_(false),
161 download_id_(download_id),
162 target_disposition_(
163 (info.save_info->prompt_for_save_location) ?
164 TARGET_DISPOSITION_PROMPT : TARGET_DISPOSITION_OVERWRITE),
165 url_chain_(info.url_chain),
166 referrer_url_(info.referrer_url),
167 suggested_filename_(UTF16ToUTF8(info.save_info->suggested_name)),
168 forced_file_path_(info.save_info->file_path),
169 transition_type_(info.transition_type),
170 has_user_gesture_(info.has_user_gesture),
171 content_disposition_(info.content_disposition),
172 mime_type_(info.mime_type),
173 original_mime_type_(info.original_mime_type),
174 remote_address_(info.remote_address),
175 total_bytes_(info.total_bytes),
176 received_bytes_(0),
177 bytes_per_sec_(0),
178 last_modified_time_(info.last_modified),
179 etag_(info.etag),
180 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE),
181 start_tick_(base::TimeTicks::Now()),
182 state_(IN_PROGRESS_INTERNAL),
183 danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
184 start_time_(info.start_time),
185 delegate_(delegate),
186 is_paused_(false),
187 auto_resume_count_(0),
188 open_when_complete_(false),
189 file_externally_removed_(false),
190 auto_opened_(false),
191 is_temporary_(!info.save_info->file_path.empty()),
192 all_data_saved_(false),
193 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
194 opened_(false),
195 delegate_delayed_complete_(false),
196 bound_net_log_(bound_net_log),
197 weak_ptr_factory_(this) {
198 delegate_->Attach();
199 Init(true /* actively downloading */, SRC_ACTIVE_DOWNLOAD);
200
201 // Link the event sources.
202 bound_net_log_.AddEvent(
203 net::NetLog::TYPE_DOWNLOAD_URL_REQUEST,
204 info.request_bound_net_log.source().ToEventParametersCallback());
205
206 info.request_bound_net_log.AddEvent(
207 net::NetLog::TYPE_DOWNLOAD_STARTED,
208 bound_net_log_.source().ToEventParametersCallback());
209 }
210
211 // Constructing for the "Save Page As..." feature:
DownloadItemImpl(DownloadItemImplDelegate * delegate,uint32 download_id,const base::FilePath & path,const GURL & url,const std::string & mime_type,scoped_ptr<DownloadRequestHandleInterface> request_handle,const net::BoundNetLog & bound_net_log)212 DownloadItemImpl::DownloadItemImpl(
213 DownloadItemImplDelegate* delegate,
214 uint32 download_id,
215 const base::FilePath& path,
216 const GURL& url,
217 const std::string& mime_type,
218 scoped_ptr<DownloadRequestHandleInterface> request_handle,
219 const net::BoundNetLog& bound_net_log)
220 : is_save_package_download_(true),
221 request_handle_(request_handle.Pass()),
222 download_id_(download_id),
223 current_path_(path),
224 target_path_(path),
225 target_disposition_(TARGET_DISPOSITION_OVERWRITE),
226 url_chain_(1, url),
227 referrer_url_(GURL()),
228 transition_type_(PAGE_TRANSITION_LINK),
229 has_user_gesture_(false),
230 mime_type_(mime_type),
231 original_mime_type_(mime_type),
232 total_bytes_(0),
233 received_bytes_(0),
234 bytes_per_sec_(0),
235 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE),
236 start_tick_(base::TimeTicks::Now()),
237 state_(IN_PROGRESS_INTERNAL),
238 danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
239 start_time_(base::Time::Now()),
240 delegate_(delegate),
241 is_paused_(false),
242 auto_resume_count_(0),
243 open_when_complete_(false),
244 file_externally_removed_(false),
245 auto_opened_(false),
246 is_temporary_(false),
247 all_data_saved_(false),
248 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
249 opened_(false),
250 delegate_delayed_complete_(false),
251 bound_net_log_(bound_net_log),
252 weak_ptr_factory_(this) {
253 delegate_->Attach();
254 Init(true /* actively downloading */, SRC_SAVE_PAGE_AS);
255 }
256
~DownloadItemImpl()257 DownloadItemImpl::~DownloadItemImpl() {
258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
259
260 // Should always have been nuked before now, at worst in
261 // DownloadManager shutdown.
262 DCHECK(!download_file_.get());
263
264 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadDestroyed(this));
265 delegate_->AssertStateConsistent(this);
266 delegate_->Detach();
267 }
268
AddObserver(Observer * observer)269 void DownloadItemImpl::AddObserver(Observer* observer) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271
272 observers_.AddObserver(observer);
273 }
274
RemoveObserver(Observer * observer)275 void DownloadItemImpl::RemoveObserver(Observer* observer) {
276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
277
278 observers_.RemoveObserver(observer);
279 }
280
UpdateObservers()281 void DownloadItemImpl::UpdateObservers() {
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
283
284 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
285 }
286
ValidateDangerousDownload()287 void DownloadItemImpl::ValidateDangerousDownload() {
288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
289 DCHECK(!IsDone());
290 DCHECK(IsDangerous());
291
292 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
293
294 if (IsDone() || !IsDangerous())
295 return;
296
297 RecordDangerousDownloadAccept(GetDangerType(),
298 GetTargetFilePath());
299
300 danger_type_ = DOWNLOAD_DANGER_TYPE_USER_VALIDATED;
301
302 bound_net_log_.AddEvent(
303 net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED,
304 base::Bind(&ItemCheckedNetLogCallback, GetDangerType()));
305
306 UpdateObservers();
307
308 MaybeCompleteDownload();
309 }
310
StealDangerousDownload(const AcquireFileCallback & callback)311 void DownloadItemImpl::StealDangerousDownload(
312 const AcquireFileCallback& callback) {
313 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
315 DCHECK(IsDangerous());
316 if (download_file_) {
317 BrowserThread::PostTaskAndReplyWithResult(
318 BrowserThread::FILE,
319 FROM_HERE,
320 base::Bind(&DownloadFileDetach, base::Passed(&download_file_)),
321 callback);
322 } else {
323 callback.Run(current_path_);
324 }
325 current_path_.clear();
326 Remove();
327 // We have now been deleted.
328 }
329
Pause()330 void DownloadItemImpl::Pause() {
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
332
333 // Ignore irrelevant states.
334 if (state_ != IN_PROGRESS_INTERNAL || is_paused_)
335 return;
336
337 request_handle_->PauseRequest();
338 is_paused_ = true;
339 UpdateObservers();
340 }
341
Resume()342 void DownloadItemImpl::Resume() {
343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
344 switch (state_) {
345 case IN_PROGRESS_INTERNAL:
346 if (!is_paused_)
347 return;
348 request_handle_->ResumeRequest();
349 is_paused_ = false;
350 UpdateObservers();
351 return;
352
353 case COMPLETING_INTERNAL:
354 case COMPLETE_INTERNAL:
355 case CANCELLED_INTERNAL:
356 case RESUMING_INTERNAL:
357 return;
358
359 case INTERRUPTED_INTERNAL:
360 auto_resume_count_ = 0; // User input resets the counter.
361 ResumeInterruptedDownload();
362 return;
363
364 case MAX_DOWNLOAD_INTERNAL_STATE:
365 NOTREACHED();
366 }
367 }
368
Cancel(bool user_cancel)369 void DownloadItemImpl::Cancel(bool user_cancel) {
370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
371
372 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
373 if (state_ != IN_PROGRESS_INTERNAL &&
374 state_ != INTERRUPTED_INTERNAL &&
375 state_ != RESUMING_INTERNAL) {
376 // Small downloads might be complete before this method has a chance to run.
377 return;
378 }
379
380 if (IsDangerous()) {
381 RecordDangerousDownloadDiscard(
382 user_cancel ? DOWNLOAD_DISCARD_DUE_TO_USER_ACTION
383 : DOWNLOAD_DISCARD_DUE_TO_SHUTDOWN,
384 GetDangerType(),
385 GetTargetFilePath());
386 }
387
388 last_reason_ = user_cancel ? DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
389 : DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN;
390
391 RecordDownloadCount(CANCELLED_COUNT);
392
393 // TODO(rdsmith/benjhayden): Remove condition as part of
394 // |SavePackage| integration.
395 // |download_file_| can be NULL if Interrupt() is called after the
396 // download file has been released.
397 if (!is_save_package_download_ && download_file_)
398 ReleaseDownloadFile(true);
399
400 if (state_ == IN_PROGRESS_INTERNAL) {
401 // Cancel the originating URL request unless it's already been cancelled
402 // by interrupt.
403 request_handle_->CancelRequest();
404 }
405
406 // Remove the intermediate file if we are cancelling an interrupted download.
407 // Continuable interruptions leave the intermediate file around.
408 if ((state_ == INTERRUPTED_INTERNAL || state_ == RESUMING_INTERNAL) &&
409 !current_path_.empty()) {
410 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
411 base::Bind(&DeleteDownloadedFile, current_path_));
412 current_path_.clear();
413 }
414
415 TransitionTo(CANCELLED_INTERNAL, UPDATE_OBSERVERS);
416 }
417
Remove()418 void DownloadItemImpl::Remove() {
419 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
421
422 delegate_->AssertStateConsistent(this);
423 Cancel(true);
424 delegate_->AssertStateConsistent(this);
425
426 NotifyRemoved();
427 delegate_->DownloadRemoved(this);
428 // We have now been deleted.
429 }
430
OpenDownload()431 void DownloadItemImpl::OpenDownload() {
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
433
434 if (!IsDone()) {
435 // We don't honor the open_when_complete_ flag for temporary
436 // downloads. Don't set it because it shows up in the UI.
437 if (!IsTemporary())
438 open_when_complete_ = !open_when_complete_;
439 return;
440 }
441
442 if (state_ != COMPLETE_INTERNAL || file_externally_removed_)
443 return;
444
445 // Ideally, we want to detect errors in opening and report them, but we
446 // don't generally have the proper interface for that to the external
447 // program that opens the file. So instead we spawn a check to update
448 // the UI if the file has been deleted in parallel with the open.
449 delegate_->CheckForFileRemoval(this);
450 RecordOpen(GetEndTime(), !GetOpened());
451 opened_ = true;
452 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this));
453 delegate_->OpenDownload(this);
454 }
455
ShowDownloadInShell()456 void DownloadItemImpl::ShowDownloadInShell() {
457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
458
459 delegate_->ShowDownloadInShell(this);
460 }
461
GetId() const462 uint32 DownloadItemImpl::GetId() const {
463 return download_id_;
464 }
465
GetState() const466 DownloadItem::DownloadState DownloadItemImpl::GetState() const {
467 return InternalToExternalState(state_);
468 }
469
GetLastReason() const470 DownloadInterruptReason DownloadItemImpl::GetLastReason() const {
471 return last_reason_;
472 }
473
IsPaused() const474 bool DownloadItemImpl::IsPaused() const {
475 return is_paused_;
476 }
477
IsTemporary() const478 bool DownloadItemImpl::IsTemporary() const {
479 return is_temporary_;
480 }
481
CanResume() const482 bool DownloadItemImpl::CanResume() const {
483 if ((GetState() == IN_PROGRESS) && IsPaused())
484 return true;
485
486 if (state_ != INTERRUPTED_INTERNAL)
487 return false;
488
489 // Downloads that don't have a WebContents should still be resumable, but this
490 // isn't currently the case. See ResumeInterruptedDownload().
491 if (!GetWebContents())
492 return false;
493
494 ResumeMode resume_mode = GetResumeMode();
495 return IsDownloadResumptionEnabled() &&
496 (resume_mode == RESUME_MODE_USER_RESTART ||
497 resume_mode == RESUME_MODE_USER_CONTINUE);
498 }
499
IsDone() const500 bool DownloadItemImpl::IsDone() const {
501 switch (state_) {
502 case IN_PROGRESS_INTERNAL:
503 case COMPLETING_INTERNAL:
504 return false;
505
506 case COMPLETE_INTERNAL:
507 case CANCELLED_INTERNAL:
508 return true;
509
510 case INTERRUPTED_INTERNAL:
511 return !CanResume();
512
513 case RESUMING_INTERNAL:
514 return false;
515
516 case MAX_DOWNLOAD_INTERNAL_STATE:
517 break;
518 }
519 NOTREACHED();
520 return true;
521 }
522
GetURL() const523 const GURL& DownloadItemImpl::GetURL() const {
524 return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.back();
525 }
526
GetUrlChain() const527 const std::vector<GURL>& DownloadItemImpl::GetUrlChain() const {
528 return url_chain_;
529 }
530
GetOriginalUrl() const531 const GURL& DownloadItemImpl::GetOriginalUrl() const {
532 // Be careful about taking the front() of possibly-empty vectors!
533 // http://crbug.com/190096
534 return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.front();
535 }
536
GetReferrerUrl() const537 const GURL& DownloadItemImpl::GetReferrerUrl() const {
538 return referrer_url_;
539 }
540
GetSuggestedFilename() const541 std::string DownloadItemImpl::GetSuggestedFilename() const {
542 return suggested_filename_;
543 }
544
GetContentDisposition() const545 std::string DownloadItemImpl::GetContentDisposition() const {
546 return content_disposition_;
547 }
548
GetMimeType() const549 std::string DownloadItemImpl::GetMimeType() const {
550 return mime_type_;
551 }
552
GetOriginalMimeType() const553 std::string DownloadItemImpl::GetOriginalMimeType() const {
554 return original_mime_type_;
555 }
556
GetRemoteAddress() const557 std::string DownloadItemImpl::GetRemoteAddress() const {
558 return remote_address_;
559 }
560
HasUserGesture() const561 bool DownloadItemImpl::HasUserGesture() const {
562 return has_user_gesture_;
563 };
564
GetTransitionType() const565 PageTransition DownloadItemImpl::GetTransitionType() const {
566 return transition_type_;
567 };
568
GetLastModifiedTime() const569 const std::string& DownloadItemImpl::GetLastModifiedTime() const {
570 return last_modified_time_;
571 }
572
GetETag() const573 const std::string& DownloadItemImpl::GetETag() const {
574 return etag_;
575 }
576
IsSavePackageDownload() const577 bool DownloadItemImpl::IsSavePackageDownload() const {
578 return is_save_package_download_;
579 }
580
GetFullPath() const581 const base::FilePath& DownloadItemImpl::GetFullPath() const {
582 return current_path_;
583 }
584
GetTargetFilePath() const585 const base::FilePath& DownloadItemImpl::GetTargetFilePath() const {
586 return target_path_;
587 }
588
GetForcedFilePath() const589 const base::FilePath& DownloadItemImpl::GetForcedFilePath() const {
590 // TODO(asanka): Get rid of GetForcedFilePath(). We should instead just
591 // require that clients respect GetTargetFilePath() if it is already set.
592 return forced_file_path_;
593 }
594
GetFileNameToReportUser() const595 base::FilePath DownloadItemImpl::GetFileNameToReportUser() const {
596 if (!display_name_.empty())
597 return display_name_;
598 return target_path_.BaseName();
599 }
600
GetTargetDisposition() const601 DownloadItem::TargetDisposition DownloadItemImpl::GetTargetDisposition() const {
602 return target_disposition_;
603 }
604
GetHash() const605 const std::string& DownloadItemImpl::GetHash() const {
606 return hash_;
607 }
608
GetHashState() const609 const std::string& DownloadItemImpl::GetHashState() const {
610 return hash_state_;
611 }
612
GetFileExternallyRemoved() const613 bool DownloadItemImpl::GetFileExternallyRemoved() const {
614 return file_externally_removed_;
615 }
616
DeleteFile()617 void DownloadItemImpl::DeleteFile() {
618 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
619 if ((GetState() != DownloadItem::COMPLETE) ||
620 file_externally_removed_) {
621 return;
622 }
623 BrowserThread::PostTaskAndReply(
624 BrowserThread::FILE, FROM_HERE,
625 base::Bind(&DeleteDownloadedFile, current_path_),
626 base::Bind(&DownloadItemImpl::OnDownloadedFileRemoved,
627 weak_ptr_factory_.GetWeakPtr()));
628 current_path_.clear();
629 }
630
IsDangerous() const631 bool DownloadItemImpl::IsDangerous() const {
632 #if defined(OS_WIN)
633 // TODO(noelutz): At this point only the windows views UI supports
634 // warnings based on dangerous content.
635 return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
636 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
637 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
638 danger_type_ == DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
639 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
640 danger_type_ == DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED);
641 #else
642 return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
643 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL);
644 #endif
645 }
646
GetDangerType() const647 DownloadDangerType DownloadItemImpl::GetDangerType() const {
648 return danger_type_;
649 }
650
TimeRemaining(base::TimeDelta * remaining) const651 bool DownloadItemImpl::TimeRemaining(base::TimeDelta* remaining) const {
652 if (total_bytes_ <= 0)
653 return false; // We never received the content_length for this download.
654
655 int64 speed = CurrentSpeed();
656 if (speed == 0)
657 return false;
658
659 *remaining = base::TimeDelta::FromSeconds(
660 (total_bytes_ - received_bytes_) / speed);
661 return true;
662 }
663
CurrentSpeed() const664 int64 DownloadItemImpl::CurrentSpeed() const {
665 if (is_paused_)
666 return 0;
667 return bytes_per_sec_;
668 }
669
PercentComplete() const670 int DownloadItemImpl::PercentComplete() const {
671 // If the delegate is delaying completion of the download, then we have no
672 // idea how long it will take.
673 if (delegate_delayed_complete_ || total_bytes_ <= 0)
674 return -1;
675
676 return static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
677 }
678
AllDataSaved() const679 bool DownloadItemImpl::AllDataSaved() const {
680 return all_data_saved_;
681 }
682
GetTotalBytes() const683 int64 DownloadItemImpl::GetTotalBytes() const {
684 return total_bytes_;
685 }
686
GetReceivedBytes() const687 int64 DownloadItemImpl::GetReceivedBytes() const {
688 return received_bytes_;
689 }
690
GetStartTime() const691 base::Time DownloadItemImpl::GetStartTime() const {
692 return start_time_;
693 }
694
GetEndTime() const695 base::Time DownloadItemImpl::GetEndTime() const {
696 return end_time_;
697 }
698
CanShowInFolder()699 bool DownloadItemImpl::CanShowInFolder() {
700 // A download can be shown in the folder if the downloaded file is in a known
701 // location.
702 return CanOpenDownload() && !GetFullPath().empty();
703 }
704
CanOpenDownload()705 bool DownloadItemImpl::CanOpenDownload() {
706 // We can open the file or mark it for opening on completion if the download
707 // is expected to complete successfully. Exclude temporary downloads, since
708 // they aren't owned by the download system.
709 const bool is_complete = GetState() == DownloadItem::COMPLETE;
710 return (!IsDone() || is_complete) && !IsTemporary() &&
711 !file_externally_removed_;
712 }
713
ShouldOpenFileBasedOnExtension()714 bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() {
715 return delegate_->ShouldOpenFileBasedOnExtension(GetTargetFilePath());
716 }
717
GetOpenWhenComplete() const718 bool DownloadItemImpl::GetOpenWhenComplete() const {
719 return open_when_complete_;
720 }
721
GetAutoOpened()722 bool DownloadItemImpl::GetAutoOpened() {
723 return auto_opened_;
724 }
725
GetOpened() const726 bool DownloadItemImpl::GetOpened() const {
727 return opened_;
728 }
729
GetBrowserContext() const730 BrowserContext* DownloadItemImpl::GetBrowserContext() const {
731 return delegate_->GetBrowserContext();
732 }
733
GetWebContents() const734 WebContents* DownloadItemImpl::GetWebContents() const {
735 // TODO(rdsmith): Remove null check after removing GetWebContents() from
736 // paths that might be used by DownloadItems created from history import.
737 // Currently such items have null request_handle_s, where other items
738 // (regular and SavePackage downloads) have actual objects off the pointer.
739 if (request_handle_)
740 return request_handle_->GetWebContents();
741 return NULL;
742 }
743
OnContentCheckCompleted(DownloadDangerType danger_type)744 void DownloadItemImpl::OnContentCheckCompleted(DownloadDangerType danger_type) {
745 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
746 DCHECK(AllDataSaved());
747 VLOG(20) << __FUNCTION__ << " danger_type=" << danger_type
748 << " download=" << DebugString(true);
749 SetDangerType(danger_type);
750 UpdateObservers();
751 }
752
SetOpenWhenComplete(bool open)753 void DownloadItemImpl::SetOpenWhenComplete(bool open) {
754 open_when_complete_ = open;
755 }
756
SetIsTemporary(bool temporary)757 void DownloadItemImpl::SetIsTemporary(bool temporary) {
758 is_temporary_ = temporary;
759 }
760
SetOpened(bool opened)761 void DownloadItemImpl::SetOpened(bool opened) {
762 opened_ = opened;
763 }
764
SetDisplayName(const base::FilePath & name)765 void DownloadItemImpl::SetDisplayName(const base::FilePath& name) {
766 display_name_ = name;
767 }
768
DebugString(bool verbose) const769 std::string DownloadItemImpl::DebugString(bool verbose) const {
770 std::string description =
771 base::StringPrintf("{ id = %d"
772 " state = %s",
773 download_id_,
774 DebugDownloadStateString(state_));
775
776 // Construct a string of the URL chain.
777 std::string url_list("<none>");
778 if (!url_chain_.empty()) {
779 std::vector<GURL>::const_iterator iter = url_chain_.begin();
780 std::vector<GURL>::const_iterator last = url_chain_.end();
781 url_list = (*iter).is_valid() ? (*iter).spec() : "<invalid>";
782 ++iter;
783 for ( ; verbose && (iter != last); ++iter) {
784 url_list += " ->\n\t";
785 const GURL& next_url = *iter;
786 url_list += next_url.is_valid() ? next_url.spec() : "<invalid>";
787 }
788 }
789
790 if (verbose) {
791 description += base::StringPrintf(
792 " total = %" PRId64
793 " received = %" PRId64
794 " reason = %s"
795 " paused = %c"
796 " resume_mode = %s"
797 " auto_resume_count = %d"
798 " danger = %d"
799 " all_data_saved = %c"
800 " last_modified = '%s'"
801 " etag = '%s'"
802 " has_download_file = %s"
803 " url_chain = \n\t\"%s\"\n\t"
804 " full_path = \"%" PRFilePath "\"\n\t"
805 " target_path = \"%" PRFilePath "\"",
806 GetTotalBytes(),
807 GetReceivedBytes(),
808 InterruptReasonDebugString(last_reason_).c_str(),
809 IsPaused() ? 'T' : 'F',
810 DebugResumeModeString(GetResumeMode()),
811 auto_resume_count_,
812 GetDangerType(),
813 AllDataSaved() ? 'T' : 'F',
814 GetLastModifiedTime().c_str(),
815 GetETag().c_str(),
816 download_file_.get() ? "true" : "false",
817 url_list.c_str(),
818 GetFullPath().value().c_str(),
819 GetTargetFilePath().value().c_str());
820 } else {
821 description += base::StringPrintf(" url = \"%s\"", url_list.c_str());
822 }
823
824 description += " }";
825
826 return description;
827 }
828
GetResumeMode() const829 DownloadItemImpl::ResumeMode DownloadItemImpl::GetResumeMode() const {
830 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
831 // We can't continue without a handle on the intermediate file.
832 // We also can't continue if we don't have some verifier to make sure
833 // we're getting the same file.
834 const bool force_restart =
835 (current_path_.empty() || (etag_.empty() && last_modified_time_.empty()));
836
837 // We won't auto-restart if we've used up our attempts or the
838 // download has been paused by user action.
839 const bool force_user =
840 (auto_resume_count_ >= kMaxAutoResumeAttempts || is_paused_);
841
842 ResumeMode mode = RESUME_MODE_INVALID;
843
844 switch(last_reason_) {
845 case DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR:
846 case DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT:
847 if (force_restart && force_user)
848 mode = RESUME_MODE_USER_RESTART;
849 else if (force_restart)
850 mode = RESUME_MODE_IMMEDIATE_RESTART;
851 else if (force_user)
852 mode = RESUME_MODE_USER_CONTINUE;
853 else
854 mode = RESUME_MODE_IMMEDIATE_CONTINUE;
855 break;
856
857 case DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION:
858 case DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE:
859 case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT:
860 if (force_user)
861 mode = RESUME_MODE_USER_RESTART;
862 else
863 mode = RESUME_MODE_IMMEDIATE_RESTART;
864 break;
865
866 case DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED:
867 case DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED:
868 case DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN:
869 case DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED:
870 case DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN:
871 case DOWNLOAD_INTERRUPT_REASON_CRASH:
872 if (force_restart)
873 mode = RESUME_MODE_USER_RESTART;
874 else
875 mode = RESUME_MODE_USER_CONTINUE;
876 break;
877
878 case DOWNLOAD_INTERRUPT_REASON_FILE_FAILED:
879 case DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
880 case DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
881 case DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG:
882 case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
883 mode = RESUME_MODE_USER_RESTART;
884 break;
885
886 case DOWNLOAD_INTERRUPT_REASON_NONE:
887 case DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED:
888 case DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT:
889 case DOWNLOAD_INTERRUPT_REASON_USER_CANCELED:
890 case DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED:
891 case DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED:
892 mode = RESUME_MODE_INVALID;
893 break;
894 }
895
896 return mode;
897 }
898
MergeOriginInfoOnResume(const DownloadCreateInfo & new_create_info)899 void DownloadItemImpl::MergeOriginInfoOnResume(
900 const DownloadCreateInfo& new_create_info) {
901 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
902 DCHECK_EQ(RESUMING_INTERNAL, state_);
903 DCHECK(!new_create_info.url_chain.empty());
904
905 // We are going to tack on any new redirects to our list of redirects.
906 // When a download is resumed, the URL used for the resumption request is the
907 // one at the end of the previous redirect chain. Tacking additional redirects
908 // to the end of this chain ensures that:
909 // - If the download needs to be resumed again, the ETag/Last-Modified headers
910 // will be used with the last server that sent them to us.
911 // - The redirect chain contains all the servers that were involved in this
912 // download since the initial request, in order.
913 std::vector<GURL>::const_iterator chain_iter =
914 new_create_info.url_chain.begin();
915 if (*chain_iter == url_chain_.back())
916 ++chain_iter;
917
918 // Record some stats. If the precondition failed (the server returned
919 // HTTP_PRECONDITION_FAILED), then the download will automatically retried as
920 // a full request rather than a partial. Full restarts clobber validators.
921 int origin_state = 0;
922 if (chain_iter != new_create_info.url_chain.end())
923 origin_state |= ORIGIN_STATE_ON_RESUMPTION_ADDITIONAL_REDIRECTS;
924 if (etag_ != new_create_info.etag ||
925 last_modified_time_ != new_create_info.last_modified)
926 origin_state |= ORIGIN_STATE_ON_RESUMPTION_VALIDATORS_CHANGED;
927 if (content_disposition_ != new_create_info.content_disposition)
928 origin_state |= ORIGIN_STATE_ON_RESUMPTION_CONTENT_DISPOSITION_CHANGED;
929 RecordOriginStateOnResumption(new_create_info.save_info->offset != 0,
930 origin_state);
931
932 url_chain_.insert(
933 url_chain_.end(), chain_iter, new_create_info.url_chain.end());
934 etag_ = new_create_info.etag;
935 last_modified_time_ = new_create_info.last_modified;
936 content_disposition_ = new_create_info.content_disposition;
937
938 // Don't update observers. This method is expected to be called just before a
939 // DownloadFile is created and Start() is called. The observers will be
940 // notified when the download transitions to the IN_PROGRESS state.
941 }
942
NotifyRemoved()943 void DownloadItemImpl::NotifyRemoved() {
944 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadRemoved(this));
945 }
946
OnDownloadedFileRemoved()947 void DownloadItemImpl::OnDownloadedFileRemoved() {
948 file_externally_removed_ = true;
949 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
950 UpdateObservers();
951 }
952
953 base::WeakPtr<DownloadDestinationObserver>
DestinationObserverAsWeakPtr()954 DownloadItemImpl::DestinationObserverAsWeakPtr() {
955 return weak_ptr_factory_.GetWeakPtr();
956 }
957
GetBoundNetLog() const958 const net::BoundNetLog& DownloadItemImpl::GetBoundNetLog() const {
959 return bound_net_log_;
960 }
961
SetTotalBytes(int64 total_bytes)962 void DownloadItemImpl::SetTotalBytes(int64 total_bytes) {
963 total_bytes_ = total_bytes;
964 }
965
OnAllDataSaved(const std::string & final_hash)966 void DownloadItemImpl::OnAllDataSaved(const std::string& final_hash) {
967 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
968
969 DCHECK_EQ(IN_PROGRESS_INTERNAL, state_);
970 DCHECK(!all_data_saved_);
971 all_data_saved_ = true;
972 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
973
974 // Store final hash and null out intermediate serialized hash state.
975 hash_ = final_hash;
976 hash_state_ = "";
977
978 UpdateObservers();
979 }
980
MarkAsComplete()981 void DownloadItemImpl::MarkAsComplete() {
982 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
983
984 DCHECK(all_data_saved_);
985 end_time_ = base::Time::Now();
986 TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS);
987 }
988
DestinationUpdate(int64 bytes_so_far,int64 bytes_per_sec,const std::string & hash_state)989 void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far,
990 int64 bytes_per_sec,
991 const std::string& hash_state) {
992 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
993 VLOG(20) << __FUNCTION__ << " so_far=" << bytes_so_far
994 << " per_sec=" << bytes_per_sec << " download=" << DebugString(true);
995
996 if (GetState() != IN_PROGRESS) {
997 // Ignore if we're no longer in-progress. This can happen if we race a
998 // Cancel on the UI thread with an update on the FILE thread.
999 //
1000 // TODO(rdsmith): Arguably we should let this go through, as this means
1001 // the download really did get further than we know before it was
1002 // cancelled. But the gain isn't very large, and the code is more
1003 // fragile if it has to support in progress updates in a non-in-progress
1004 // state. This issue should be readdressed when we revamp performance
1005 // reporting.
1006 return;
1007 }
1008 bytes_per_sec_ = bytes_per_sec;
1009 hash_state_ = hash_state;
1010 received_bytes_ = bytes_so_far;
1011
1012 // If we've received more data than we were expecting (bad server info?),
1013 // revert to 'unknown size mode'.
1014 if (received_bytes_ > total_bytes_)
1015 total_bytes_ = 0;
1016
1017 if (bound_net_log_.IsLoggingAllEvents()) {
1018 bound_net_log_.AddEvent(
1019 net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED,
1020 net::NetLog::Int64Callback("bytes_so_far", received_bytes_));
1021 }
1022
1023 UpdateObservers();
1024 }
1025
DestinationError(DownloadInterruptReason reason)1026 void DownloadItemImpl::DestinationError(DownloadInterruptReason reason) {
1027 // Postpone recognition of this error until after file name determination
1028 // has completed and the intermediate file has been renamed to simplify
1029 // resumption conditions.
1030 if (current_path_.empty() || target_path_.empty())
1031 destination_error_ = reason;
1032 else
1033 Interrupt(reason);
1034 }
1035
DestinationCompleted(const std::string & final_hash)1036 void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) {
1037 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
1038 if (GetState() != IN_PROGRESS)
1039 return;
1040 OnAllDataSaved(final_hash);
1041 MaybeCompleteDownload();
1042 }
1043
1044 // **** Download progression cascade
1045
Init(bool active,DownloadType download_type)1046 void DownloadItemImpl::Init(bool active,
1047 DownloadType download_type) {
1048 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1049
1050 if (active)
1051 RecordDownloadCount(START_COUNT);
1052
1053 std::string file_name;
1054 if (download_type == SRC_HISTORY_IMPORT) {
1055 // target_path_ works for History and Save As versions.
1056 file_name = target_path_.AsUTF8Unsafe();
1057 } else {
1058 // See if it's set programmatically.
1059 file_name = forced_file_path_.AsUTF8Unsafe();
1060 // Possibly has a 'download' attribute for the anchor.
1061 if (file_name.empty())
1062 file_name = suggested_filename_;
1063 // From the URL file name.
1064 if (file_name.empty())
1065 file_name = GetURL().ExtractFileName();
1066 }
1067
1068 base::Callback<base::Value*(net::NetLog::LogLevel)> active_data = base::Bind(
1069 &ItemActivatedNetLogCallback, this, download_type, &file_name);
1070 if (active) {
1071 bound_net_log_.BeginEvent(
1072 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data);
1073 } else {
1074 bound_net_log_.AddEvent(
1075 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data);
1076 }
1077
1078 VLOG(20) << __FUNCTION__ << "() " << DebugString(true);
1079 }
1080
1081 // We're starting the download.
Start(scoped_ptr<DownloadFile> file,scoped_ptr<DownloadRequestHandleInterface> req_handle)1082 void DownloadItemImpl::Start(
1083 scoped_ptr<DownloadFile> file,
1084 scoped_ptr<DownloadRequestHandleInterface> req_handle) {
1085 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1086 DCHECK(!download_file_.get());
1087 DCHECK(file.get());
1088 DCHECK(req_handle.get());
1089
1090 download_file_ = file.Pass();
1091 request_handle_ = req_handle.Pass();
1092
1093 if (GetState() == CANCELLED) {
1094 // The download was in the process of resuming when it was cancelled. Don't
1095 // proceed.
1096 ReleaseDownloadFile(true);
1097 request_handle_->CancelRequest();
1098 return;
1099 }
1100
1101 TransitionTo(IN_PROGRESS_INTERNAL, UPDATE_OBSERVERS);
1102
1103 BrowserThread::PostTask(
1104 BrowserThread::FILE, FROM_HERE,
1105 base::Bind(&DownloadFile::Initialize,
1106 // Safe because we control download file lifetime.
1107 base::Unretained(download_file_.get()),
1108 base::Bind(&DownloadItemImpl::OnDownloadFileInitialized,
1109 weak_ptr_factory_.GetWeakPtr())));
1110 }
1111
OnDownloadFileInitialized(DownloadInterruptReason result)1112 void DownloadItemImpl::OnDownloadFileInitialized(
1113 DownloadInterruptReason result) {
1114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1115 if (result != DOWNLOAD_INTERRUPT_REASON_NONE) {
1116 Interrupt(result);
1117 // TODO(rdsmith/asanka): Arguably we should show this in the UI, but
1118 // it's not at all clear what to show--we haven't done filename
1119 // determination, so we don't know what name to display. OTOH,
1120 // the failure mode of not showing the DI if the file initialization
1121 // fails isn't a good one. Can we hack up a name based on the
1122 // URLRequest? We'll need to make sure that initialization happens
1123 // properly. Possibly the right thing is to have the UI handle
1124 // this case specially.
1125 return;
1126 }
1127
1128 delegate_->DetermineDownloadTarget(
1129 this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined,
1130 weak_ptr_factory_.GetWeakPtr()));
1131 }
1132
1133 // Called by delegate_ when the download target path has been
1134 // determined.
OnDownloadTargetDetermined(const base::FilePath & target_path,TargetDisposition disposition,DownloadDangerType danger_type,const base::FilePath & intermediate_path)1135 void DownloadItemImpl::OnDownloadTargetDetermined(
1136 const base::FilePath& target_path,
1137 TargetDisposition disposition,
1138 DownloadDangerType danger_type,
1139 const base::FilePath& intermediate_path) {
1140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1141
1142 // If the |target_path| is empty, then we consider this download to be
1143 // canceled.
1144 if (target_path.empty()) {
1145 Cancel(true);
1146 return;
1147 }
1148
1149 // TODO(rdsmith,asanka): We are ignoring the possibility that the download
1150 // has been interrupted at this point until we finish the intermediate
1151 // rename and set the full path. That's dangerous, because we might race
1152 // with resumption, either manual (because the interrupt is visible to the
1153 // UI) or automatic. If we keep the "ignore an error on download until file
1154 // name determination complete" semantics, we need to make sure that the
1155 // error is kept completely invisible until that point.
1156
1157 VLOG(20) << __FUNCTION__ << " " << target_path.value() << " " << disposition
1158 << " " << danger_type << " " << DebugString(true);
1159
1160 target_path_ = target_path;
1161 target_disposition_ = disposition;
1162 SetDangerType(danger_type);
1163
1164 // We want the intermediate and target paths to refer to the same directory so
1165 // that they are both on the same device and subject to same
1166 // space/permission/availability constraints.
1167 DCHECK(intermediate_path.DirName() == target_path.DirName());
1168
1169 // During resumption, we may choose to proceed with the same intermediate
1170 // file. No rename is necessary if our intermediate file already has the
1171 // correct name.
1172 //
1173 // The intermediate name may change from its original value during filename
1174 // determination on resumption, for example if the reason for the interruption
1175 // was the download target running out space, resulting in a user prompt.
1176 if (intermediate_path == current_path_) {
1177 OnDownloadRenamedToIntermediateName(DOWNLOAD_INTERRUPT_REASON_NONE,
1178 intermediate_path);
1179 return;
1180 }
1181
1182 // Rename to intermediate name.
1183 // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a
1184 // spurious rename when we can just rename to the final
1185 // filename. Unnecessary renames may cause bugs like
1186 // http://crbug.com/74187.
1187 DCHECK(!is_save_package_download_);
1188 DCHECK(download_file_.get());
1189 DownloadFile::RenameCompletionCallback callback =
1190 base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName,
1191 weak_ptr_factory_.GetWeakPtr());
1192 BrowserThread::PostTask(
1193 BrowserThread::FILE, FROM_HERE,
1194 base::Bind(&DownloadFile::RenameAndUniquify,
1195 // Safe because we control download file lifetime.
1196 base::Unretained(download_file_.get()),
1197 intermediate_path, callback));
1198 }
1199
OnDownloadRenamedToIntermediateName(DownloadInterruptReason reason,const base::FilePath & full_path)1200 void DownloadItemImpl::OnDownloadRenamedToIntermediateName(
1201 DownloadInterruptReason reason,
1202 const base::FilePath& full_path) {
1203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1204 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
1205
1206 if (DOWNLOAD_INTERRUPT_REASON_NONE != destination_error_) {
1207 // Process destination error. If both |reason| and |destination_error_|
1208 // refer to actual errors, we want to use the |destination_error_| as the
1209 // argument to the Interrupt() routine, as it happened first.
1210 if (reason == DOWNLOAD_INTERRUPT_REASON_NONE)
1211 SetFullPath(full_path);
1212 Interrupt(destination_error_);
1213 destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE;
1214 } else if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) {
1215 Interrupt(reason);
1216 // All file errors result in file deletion above; no need to cleanup. The
1217 // current_path_ should be empty. Resuming this download will force a
1218 // restart and a re-doing of filename determination.
1219 DCHECK(current_path_.empty());
1220 } else {
1221 SetFullPath(full_path);
1222 UpdateObservers();
1223 MaybeCompleteDownload();
1224 }
1225 }
1226
1227 // When SavePackage downloads MHTML to GData (see
1228 // SavePackageFilePickerChromeOS), GData calls MaybeCompleteDownload() like it
1229 // does for non-SavePackage downloads, but SavePackage downloads never satisfy
1230 // IsDownloadReadyForCompletion(). GDataDownloadObserver manually calls
1231 // DownloadItem::UpdateObservers() when the upload completes so that SavePackage
1232 // notices that the upload has completed and runs its normal Finish() pathway.
1233 // MaybeCompleteDownload() is never the mechanism by which SavePackage completes
1234 // downloads. SavePackage always uses its own Finish() to mark downloads
1235 // complete.
MaybeCompleteDownload()1236 void DownloadItemImpl::MaybeCompleteDownload() {
1237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1238 DCHECK(!is_save_package_download_);
1239
1240 if (!IsDownloadReadyForCompletion(
1241 base::Bind(&DownloadItemImpl::MaybeCompleteDownload,
1242 weak_ptr_factory_.GetWeakPtr())))
1243 return;
1244
1245 // TODO(rdsmith): DCHECK that we only pass through this point
1246 // once per download. The natural way to do this is by a state
1247 // transition on the DownloadItem.
1248
1249 // Confirm we're in the proper set of states to be here;
1250 // have all data, have a history handle, (validated or safe).
1251 DCHECK_EQ(IN_PROGRESS_INTERNAL, state_);
1252 DCHECK(!IsDangerous());
1253 DCHECK(all_data_saved_);
1254
1255 OnDownloadCompleting();
1256 }
1257
1258 // Called by MaybeCompleteDownload() when it has determined that the download
1259 // is ready for completion.
OnDownloadCompleting()1260 void DownloadItemImpl::OnDownloadCompleting() {
1261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1262
1263 if (state_ != IN_PROGRESS_INTERNAL)
1264 return;
1265
1266 VLOG(20) << __FUNCTION__ << "()"
1267 << " " << DebugString(true);
1268 DCHECK(!GetTargetFilePath().empty());
1269 DCHECK(!IsDangerous());
1270
1271 // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration.
1272 if (is_save_package_download_) {
1273 // Avoid doing anything on the file thread; there's nothing we control
1274 // there.
1275 // Strictly speaking, this skips giving the embedder a chance to open
1276 // the download. But on a save package download, there's no real
1277 // concept of opening.
1278 Completed();
1279 return;
1280 }
1281
1282 DCHECK(download_file_.get());
1283 // Unilaterally rename; even if it already has the right name,
1284 // we need theannotation.
1285 DownloadFile::RenameCompletionCallback callback =
1286 base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName,
1287 weak_ptr_factory_.GetWeakPtr());
1288 BrowserThread::PostTask(
1289 BrowserThread::FILE, FROM_HERE,
1290 base::Bind(&DownloadFile::RenameAndAnnotate,
1291 base::Unretained(download_file_.get()),
1292 GetTargetFilePath(), callback));
1293 }
1294
OnDownloadRenamedToFinalName(DownloadInterruptReason reason,const base::FilePath & full_path)1295 void DownloadItemImpl::OnDownloadRenamedToFinalName(
1296 DownloadInterruptReason reason,
1297 const base::FilePath& full_path) {
1298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1299 DCHECK(!is_save_package_download_);
1300
1301 // If a cancel or interrupt hit, we'll cancel the DownloadFile, which
1302 // will result in deleting the file on the file thread. So we don't
1303 // care about the name having been changed.
1304 if (state_ != IN_PROGRESS_INTERNAL)
1305 return;
1306
1307 VLOG(20) << __FUNCTION__ << "()"
1308 << " full_path = \"" << full_path.value() << "\""
1309 << " " << DebugString(false);
1310
1311 if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) {
1312 Interrupt(reason);
1313
1314 // All file errors should have resulted in in file deletion above. On
1315 // resumption we will need to re-do filename determination.
1316 DCHECK(current_path_.empty());
1317 return;
1318 }
1319
1320 DCHECK(target_path_ == full_path);
1321
1322 if (full_path != current_path_) {
1323 // full_path is now the current and target file path.
1324 DCHECK(!full_path.empty());
1325 SetFullPath(full_path);
1326 }
1327
1328 // Complete the download and release the DownloadFile.
1329 DCHECK(download_file_.get());
1330 ReleaseDownloadFile(false);
1331
1332 // We're not completely done with the download item yet, but at this
1333 // point we're committed to complete the download. Cancels (or Interrupts,
1334 // though it's not clear how they could happen) after this point will be
1335 // ignored.
1336 TransitionTo(COMPLETING_INTERNAL, DONT_UPDATE_OBSERVERS);
1337
1338 if (delegate_->ShouldOpenDownload(
1339 this, base::Bind(&DownloadItemImpl::DelayedDownloadOpened,
1340 weak_ptr_factory_.GetWeakPtr()))) {
1341 Completed();
1342 } else {
1343 delegate_delayed_complete_ = true;
1344 UpdateObservers();
1345 }
1346 }
1347
DelayedDownloadOpened(bool auto_opened)1348 void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) {
1349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1350
1351 auto_opened_ = auto_opened;
1352 Completed();
1353 }
1354
Completed()1355 void DownloadItemImpl::Completed() {
1356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1357
1358 VLOG(20) << __FUNCTION__ << "() " << DebugString(false);
1359
1360 DCHECK(all_data_saved_);
1361 end_time_ = base::Time::Now();
1362 TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS);
1363 RecordDownloadCompleted(start_tick_, received_bytes_);
1364
1365 if (auto_opened_) {
1366 // If it was already handled by the delegate, do nothing.
1367 } else if (GetOpenWhenComplete() ||
1368 ShouldOpenFileBasedOnExtension() ||
1369 IsTemporary()) {
1370 // If the download is temporary, like in drag-and-drop, do not open it but
1371 // we still need to set it auto-opened so that it can be removed from the
1372 // download shelf.
1373 if (!IsTemporary())
1374 OpenDownload();
1375
1376 auto_opened_ = true;
1377 UpdateObservers();
1378 }
1379 }
1380
OnResumeRequestStarted(DownloadItem * item,net::Error error)1381 void DownloadItemImpl::OnResumeRequestStarted(DownloadItem* item,
1382 net::Error error) {
1383 // If |item| is not NULL, then Start() has been called already, and nothing
1384 // more needs to be done here.
1385 if (item) {
1386 DCHECK_EQ(net::OK, error);
1387 DCHECK_EQ(static_cast<DownloadItem*>(this), item);
1388 return;
1389 }
1390 // Otherwise, the request failed without passing through
1391 // DownloadResourceHandler::OnResponseStarted.
1392 if (error == net::OK)
1393 error = net::ERR_FAILED;
1394 DownloadInterruptReason reason =
1395 ConvertNetErrorToInterruptReason(error, DOWNLOAD_INTERRUPT_FROM_NETWORK);
1396 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason);
1397 Interrupt(reason);
1398 }
1399
1400 // **** End of Download progression cascade
1401
1402 // An error occurred somewhere.
Interrupt(DownloadInterruptReason reason)1403 void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) {
1404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1405
1406 // Somewhat counter-intuitively, it is possible for us to receive an
1407 // interrupt after we've already been interrupted. The generation of
1408 // interrupts from the file thread Renames and the generation of
1409 // interrupts from disk writes go through two different mechanisms (driven
1410 // by rename requests from UI thread and by write requests from IO thread,
1411 // respectively), and since we choose not to keep state on the File thread,
1412 // this is the place where the races collide. It's also possible for
1413 // interrupts to race with cancels.
1414
1415 // Whatever happens, the first one to hit the UI thread wins.
1416 if (state_ != IN_PROGRESS_INTERNAL && state_ != RESUMING_INTERNAL)
1417 return;
1418
1419 last_reason_ = reason;
1420
1421 ResumeMode resume_mode = GetResumeMode();
1422
1423 if (state_ == IN_PROGRESS_INTERNAL) {
1424 // Cancel (delete file) if we're going to restart; no point in leaving
1425 // data around we aren't going to use. Also cancel if resumption isn't
1426 // enabled for the same reason.
1427 ReleaseDownloadFile(resume_mode == RESUME_MODE_IMMEDIATE_RESTART ||
1428 resume_mode == RESUME_MODE_USER_RESTART ||
1429 !IsDownloadResumptionEnabled());
1430
1431 // Cancel the originating URL request.
1432 request_handle_->CancelRequest();
1433 } else {
1434 DCHECK(!download_file_.get());
1435 }
1436
1437 // Reset all data saved, as even if we did save all the data we're going
1438 // to go through another round of downloading when we resume.
1439 // There's a potential problem here in the abstract, as if we did download
1440 // all the data and then run into a continuable error, on resumption we
1441 // won't download any more data. However, a) there are currently no
1442 // continuable errors that can occur after we download all the data, and
1443 // b) if there were, that would probably simply result in a null range
1444 // request, which would generate a DestinationCompleted() notification
1445 // from the DownloadFile, which would behave properly with setting
1446 // all_data_saved_ to false here.
1447 all_data_saved_ = false;
1448
1449 TransitionTo(INTERRUPTED_INTERNAL, DONT_UPDATE_OBSERVERS);
1450 RecordDownloadInterrupted(reason, received_bytes_, total_bytes_);
1451 if (!GetWebContents())
1452 RecordDownloadCount(INTERRUPTED_WITHOUT_WEBCONTENTS);
1453
1454 AutoResumeIfValid();
1455 UpdateObservers();
1456 }
1457
ReleaseDownloadFile(bool destroy_file)1458 void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) {
1459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1460
1461 if (destroy_file) {
1462 BrowserThread::PostTask(
1463 BrowserThread::FILE, FROM_HERE,
1464 // Will be deleted at end of task execution.
1465 base::Bind(&DownloadFileCancel, base::Passed(&download_file_)));
1466 // Avoid attempting to reuse the intermediate file by clearing out
1467 // current_path_.
1468 current_path_.clear();
1469 } else {
1470 BrowserThread::PostTask(
1471 BrowserThread::FILE,
1472 FROM_HERE,
1473 base::Bind(base::IgnoreResult(&DownloadFileDetach),
1474 // Will be deleted at end of task execution.
1475 base::Passed(&download_file_)));
1476 }
1477 // Don't accept any more messages from the DownloadFile, and null
1478 // out any previous "all data received". This also breaks links to
1479 // other entities we've given out weak pointers to.
1480 weak_ptr_factory_.InvalidateWeakPtrs();
1481 }
1482
IsDownloadReadyForCompletion(const base::Closure & state_change_notification)1483 bool DownloadItemImpl::IsDownloadReadyForCompletion(
1484 const base::Closure& state_change_notification) {
1485 // If we don't have all the data, the download is not ready for
1486 // completion.
1487 if (!AllDataSaved())
1488 return false;
1489
1490 // If the download is dangerous, but not yet validated, it's not ready for
1491 // completion.
1492 if (IsDangerous())
1493 return false;
1494
1495 // If the download isn't active (e.g. has been cancelled) it's not
1496 // ready for completion.
1497 if (state_ != IN_PROGRESS_INTERNAL)
1498 return false;
1499
1500 // If the target filename hasn't been determined, then it's not ready for
1501 // completion. This is checked in ReadyForDownloadCompletionDone().
1502 if (GetTargetFilePath().empty())
1503 return false;
1504
1505 // This is checked in NeedsRename(). Without this conditional,
1506 // browser_tests:DownloadTest.DownloadMimeType fails the DCHECK.
1507 if (target_path_.DirName() != current_path_.DirName())
1508 return false;
1509
1510 // Give the delegate a chance to hold up a stop sign. It'll call
1511 // use back through the passed callback if it does and that state changes.
1512 if (!delegate_->ShouldCompleteDownload(this, state_change_notification))
1513 return false;
1514
1515 return true;
1516 }
1517
TransitionTo(DownloadInternalState new_state,ShouldUpdateObservers notify_action)1518 void DownloadItemImpl::TransitionTo(DownloadInternalState new_state,
1519 ShouldUpdateObservers notify_action) {
1520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1521
1522 if (state_ == new_state)
1523 return;
1524
1525 DownloadInternalState old_state = state_;
1526 state_ = new_state;
1527
1528 switch (state_) {
1529 case COMPLETING_INTERNAL:
1530 bound_net_log_.AddEvent(
1531 net::NetLog::TYPE_DOWNLOAD_ITEM_COMPLETING,
1532 base::Bind(&ItemCompletingNetLogCallback, received_bytes_, &hash_));
1533 break;
1534 case COMPLETE_INTERNAL:
1535 bound_net_log_.AddEvent(
1536 net::NetLog::TYPE_DOWNLOAD_ITEM_FINISHED,
1537 base::Bind(&ItemFinishedNetLogCallback, auto_opened_));
1538 break;
1539 case INTERRUPTED_INTERNAL:
1540 bound_net_log_.AddEvent(
1541 net::NetLog::TYPE_DOWNLOAD_ITEM_INTERRUPTED,
1542 base::Bind(&ItemInterruptedNetLogCallback, last_reason_,
1543 received_bytes_, &hash_state_));
1544 break;
1545 case IN_PROGRESS_INTERNAL:
1546 if (old_state == INTERRUPTED_INTERNAL) {
1547 bound_net_log_.AddEvent(
1548 net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED,
1549 base::Bind(&ItemResumingNetLogCallback,
1550 false, last_reason_, received_bytes_, &hash_state_));
1551 }
1552 break;
1553 case CANCELLED_INTERNAL:
1554 bound_net_log_.AddEvent(
1555 net::NetLog::TYPE_DOWNLOAD_ITEM_CANCELED,
1556 base::Bind(&ItemCanceledNetLogCallback, received_bytes_,
1557 &hash_state_));
1558 break;
1559 default:
1560 break;
1561 }
1562
1563 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true)
1564 << " " << InternalToExternalState(old_state)
1565 << " " << InternalToExternalState(state_);
1566
1567 bool is_done = (state_ != IN_PROGRESS_INTERNAL &&
1568 state_ != COMPLETING_INTERNAL);
1569 bool was_done = (old_state != IN_PROGRESS_INTERNAL &&
1570 old_state != COMPLETING_INTERNAL);
1571 // Termination
1572 if (is_done && !was_done)
1573 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE);
1574
1575 // Resumption
1576 if (was_done && !is_done) {
1577 std::string file_name(target_path_.BaseName().AsUTF8Unsafe());
1578 bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE,
1579 base::Bind(&ItemActivatedNetLogCallback,
1580 this, SRC_ACTIVE_DOWNLOAD,
1581 &file_name));
1582 }
1583
1584 if (notify_action == UPDATE_OBSERVERS)
1585 UpdateObservers();
1586 }
1587
SetDangerType(DownloadDangerType danger_type)1588 void DownloadItemImpl::SetDangerType(DownloadDangerType danger_type) {
1589 if (danger_type != danger_type_) {
1590 bound_net_log_.AddEvent(
1591 net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED,
1592 base::Bind(&ItemCheckedNetLogCallback, danger_type));
1593 }
1594 // Only record the Malicious UMA stat if it's going from {not malicious} ->
1595 // {malicious}.
1596 if ((danger_type_ == DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS ||
1597 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
1598 danger_type_ == DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
1599 danger_type_ == DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT) &&
1600 (danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
1601 danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
1602 danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
1603 danger_type == DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED)) {
1604 RecordMaliciousDownloadClassified(danger_type);
1605 }
1606 danger_type_ = danger_type;
1607 }
1608
SetFullPath(const base::FilePath & new_path)1609 void DownloadItemImpl::SetFullPath(const base::FilePath& new_path) {
1610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1611 VLOG(20) << __FUNCTION__ << "()"
1612 << " new_path = \"" << new_path.value() << "\""
1613 << " " << DebugString(true);
1614 DCHECK(!new_path.empty());
1615
1616 bound_net_log_.AddEvent(
1617 net::NetLog::TYPE_DOWNLOAD_ITEM_RENAMED,
1618 base::Bind(&ItemRenamedNetLogCallback, ¤t_path_, &new_path));
1619
1620 current_path_ = new_path;
1621 }
1622
AutoResumeIfValid()1623 void DownloadItemImpl::AutoResumeIfValid() {
1624 DVLOG(20) << __FUNCTION__ << "() " << DebugString(true);
1625 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1626 ResumeMode mode = GetResumeMode();
1627
1628 if (mode != RESUME_MODE_IMMEDIATE_RESTART &&
1629 mode != RESUME_MODE_IMMEDIATE_CONTINUE) {
1630 return;
1631 }
1632
1633 auto_resume_count_++;
1634
1635 ResumeInterruptedDownload();
1636 }
1637
ResumeInterruptedDownload()1638 void DownloadItemImpl::ResumeInterruptedDownload() {
1639 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1640
1641 // If the flag for downloads resumption isn't enabled, ignore
1642 // this request.
1643 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
1644 if (!command_line.HasSwitch(switches::kEnableDownloadResumption))
1645 return;
1646
1647 // If we're not interrupted, ignore the request; our caller is drunk.
1648 if (state_ != INTERRUPTED_INTERNAL)
1649 return;
1650
1651 // If we can't get a web contents, we can't resume the download.
1652 // TODO(rdsmith): Find some alternative web contents to use--this
1653 // means we can't restart a download if it's a download imported
1654 // from the history.
1655 if (!GetWebContents())
1656 return;
1657
1658 // Reset the appropriate state if restarting.
1659 ResumeMode mode = GetResumeMode();
1660 if (mode == RESUME_MODE_IMMEDIATE_RESTART ||
1661 mode == RESUME_MODE_USER_RESTART) {
1662 received_bytes_ = 0;
1663 hash_state_ = "";
1664 last_modified_time_ = "";
1665 etag_ = "";
1666 }
1667
1668 scoped_ptr<DownloadUrlParameters> download_params(
1669 DownloadUrlParameters::FromWebContents(GetWebContents(),
1670 GetOriginalUrl()));
1671
1672 download_params->set_file_path(GetFullPath());
1673 download_params->set_offset(GetReceivedBytes());
1674 download_params->set_hash_state(GetHashState());
1675 download_params->set_last_modified(GetLastModifiedTime());
1676 download_params->set_etag(GetETag());
1677 download_params->set_callback(
1678 base::Bind(&DownloadItemImpl::OnResumeRequestStarted,
1679 weak_ptr_factory_.GetWeakPtr()));
1680
1681 delegate_->ResumeInterruptedDownload(download_params.Pass(), GetId());
1682 // Just in case we were interrupted while paused.
1683 is_paused_ = false;
1684
1685 TransitionTo(RESUMING_INTERNAL, DONT_UPDATE_OBSERVERS);
1686 }
1687
1688 // static
InternalToExternalState(DownloadInternalState internal_state)1689 DownloadItem::DownloadState DownloadItemImpl::InternalToExternalState(
1690 DownloadInternalState internal_state) {
1691 switch (internal_state) {
1692 case IN_PROGRESS_INTERNAL:
1693 return IN_PROGRESS;
1694 case COMPLETING_INTERNAL:
1695 return IN_PROGRESS;
1696 case COMPLETE_INTERNAL:
1697 return COMPLETE;
1698 case CANCELLED_INTERNAL:
1699 return CANCELLED;
1700 case INTERRUPTED_INTERNAL:
1701 return INTERRUPTED;
1702 case RESUMING_INTERNAL:
1703 return INTERRUPTED;
1704 case MAX_DOWNLOAD_INTERNAL_STATE:
1705 break;
1706 }
1707 NOTREACHED();
1708 return MAX_DOWNLOAD_STATE;
1709 }
1710
1711 // static
1712 DownloadItemImpl::DownloadInternalState
ExternalToInternalState(DownloadState external_state)1713 DownloadItemImpl::ExternalToInternalState(
1714 DownloadState external_state) {
1715 switch (external_state) {
1716 case IN_PROGRESS:
1717 return IN_PROGRESS_INTERNAL;
1718 case COMPLETE:
1719 return COMPLETE_INTERNAL;
1720 case CANCELLED:
1721 return CANCELLED_INTERNAL;
1722 case INTERRUPTED:
1723 return INTERRUPTED_INTERNAL;
1724 default:
1725 NOTREACHED();
1726 }
1727 return MAX_DOWNLOAD_INTERNAL_STATE;
1728 }
1729
DebugDownloadStateString(DownloadInternalState state)1730 const char* DownloadItemImpl::DebugDownloadStateString(
1731 DownloadInternalState state) {
1732 switch (state) {
1733 case IN_PROGRESS_INTERNAL:
1734 return "IN_PROGRESS";
1735 case COMPLETING_INTERNAL:
1736 return "COMPLETING";
1737 case COMPLETE_INTERNAL:
1738 return "COMPLETE";
1739 case CANCELLED_INTERNAL:
1740 return "CANCELLED";
1741 case INTERRUPTED_INTERNAL:
1742 return "INTERRUPTED";
1743 case RESUMING_INTERNAL:
1744 return "RESUMING";
1745 case MAX_DOWNLOAD_INTERNAL_STATE:
1746 break;
1747 };
1748 NOTREACHED() << "Unknown download state " << state;
1749 return "unknown";
1750 }
1751
DebugResumeModeString(ResumeMode mode)1752 const char* DownloadItemImpl::DebugResumeModeString(ResumeMode mode) {
1753 switch (mode) {
1754 case RESUME_MODE_INVALID:
1755 return "INVALID";
1756 case RESUME_MODE_IMMEDIATE_CONTINUE:
1757 return "IMMEDIATE_CONTINUE";
1758 case RESUME_MODE_IMMEDIATE_RESTART:
1759 return "IMMEDIATE_RESTART";
1760 case RESUME_MODE_USER_CONTINUE:
1761 return "USER_CONTINUE";
1762 case RESUME_MODE_USER_RESTART:
1763 return "USER_RESTART";
1764 }
1765 NOTREACHED() << "Unknown resume mode " << mode;
1766 return "unknown";
1767 }
1768
1769 } // namespace content
1770