1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // 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.
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
24 #include "content/browser/download/download_item_impl.h"
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"
60 bool DeleteDownloadedFile(const base::FilePath& path) {
61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
63 // Make sure we only delete files.
64 if (base::DirectoryExists(path))
66 return base::DeleteFile(path, false);
69 void DeleteDownloadedFileDone(
70 base::WeakPtr<DownloadItemImpl> item,
71 const base::Callback<void(bool)>& callback,
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
74 if (success && item.get())
75 item->OnDownloadedFileRemoved();
76 callback.Run(success);
79 // Wrapper around DownloadFile::Detach and DownloadFile::Cancel that
80 // takes ownership of the DownloadFile and hence implicitly destroys it
81 // at the end of the function.
82 static base::FilePath DownloadFileDetach(
83 scoped_ptr<DownloadFile> download_file) {
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
85 base::FilePath full_path = download_file->FullPath();
86 download_file->Detach();
90 static void DownloadFileCancel(scoped_ptr<DownloadFile> download_file) {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
92 download_file->Cancel();
95 bool IsDownloadResumptionEnabled() {
96 return CommandLine::ForCurrentProcess()->HasSwitch(
97 switches::kEnableDownloadResumption);
102 const uint32 DownloadItem::kInvalidId = 0;
104 const char DownloadItem::kEmptyFileHash[] = "";
106 // The maximum number of attempts we will make to resume automatically.
107 const int DownloadItemImpl::kMaxAutoResumeAttempts = 5;
109 // Constructor for reading from the history service.
110 DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate,
112 const base::FilePath& current_path,
113 const base::FilePath& target_path,
114 const std::vector<GURL>& url_chain,
115 const GURL& referrer_url,
116 const base::Time& start_time,
117 const base::Time& end_time,
118 const std::string& etag,
119 const std::string& last_modified,
120 int64 received_bytes,
122 DownloadItem::DownloadState state,
123 DownloadDangerType danger_type,
124 DownloadInterruptReason interrupt_reason,
126 const net::BoundNetLog& bound_net_log)
127 : is_save_package_download_(false),
128 download_id_(download_id),
129 current_path_(current_path),
130 target_path_(target_path),
131 target_disposition_(TARGET_DISPOSITION_OVERWRITE),
132 url_chain_(url_chain),
133 referrer_url_(referrer_url),
134 transition_type_(PAGE_TRANSITION_LINK),
135 has_user_gesture_(false),
136 total_bytes_(total_bytes),
137 received_bytes_(received_bytes),
139 last_modified_time_(last_modified),
141 last_reason_(interrupt_reason),
142 start_tick_(base::TimeTicks()),
143 state_(ExternalToInternalState(state)),
144 danger_type_(danger_type),
145 start_time_(start_time),
149 auto_resume_count_(0),
150 open_when_complete_(false),
151 file_externally_removed_(false),
153 is_temporary_(false),
154 all_data_saved_(state == COMPLETE),
155 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
157 delegate_delayed_complete_(false),
158 bound_net_log_(bound_net_log),
159 weak_ptr_factory_(this) {
161 DCHECK_NE(IN_PROGRESS_INTERNAL, state_);
162 Init(false /* not actively downloading */, SRC_HISTORY_IMPORT);
165 // Constructing for a regular download:
166 DownloadItemImpl::DownloadItemImpl(
167 DownloadItemImplDelegate* delegate,
169 const DownloadCreateInfo& info,
170 const net::BoundNetLog& bound_net_log)
171 : is_save_package_download_(false),
172 download_id_(download_id),
174 (info.save_info->prompt_for_save_location) ?
175 TARGET_DISPOSITION_PROMPT : TARGET_DISPOSITION_OVERWRITE),
176 url_chain_(info.url_chain),
177 referrer_url_(info.referrer_url),
178 tab_url_(info.tab_url),
179 tab_referrer_url_(info.tab_referrer_url),
180 suggested_filename_(base::UTF16ToUTF8(info.save_info->suggested_name)),
181 forced_file_path_(info.save_info->file_path),
182 transition_type_(info.transition_type),
183 has_user_gesture_(info.has_user_gesture),
184 content_disposition_(info.content_disposition),
185 mime_type_(info.mime_type),
186 original_mime_type_(info.original_mime_type),
187 remote_address_(info.remote_address),
188 total_bytes_(info.total_bytes),
191 last_modified_time_(info.last_modified),
193 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE),
194 start_tick_(base::TimeTicks::Now()),
195 state_(IN_PROGRESS_INTERNAL),
196 danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
197 start_time_(info.start_time),
200 auto_resume_count_(0),
201 open_when_complete_(false),
202 file_externally_removed_(false),
204 is_temporary_(!info.save_info->file_path.empty()),
205 all_data_saved_(false),
206 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
208 delegate_delayed_complete_(false),
209 bound_net_log_(bound_net_log),
210 weak_ptr_factory_(this) {
212 Init(true /* actively downloading */, SRC_ACTIVE_DOWNLOAD);
214 // Link the event sources.
215 bound_net_log_.AddEvent(
216 net::NetLog::TYPE_DOWNLOAD_URL_REQUEST,
217 info.request_bound_net_log.source().ToEventParametersCallback());
219 info.request_bound_net_log.AddEvent(
220 net::NetLog::TYPE_DOWNLOAD_STARTED,
221 bound_net_log_.source().ToEventParametersCallback());
224 // Constructing for the "Save Page As..." feature:
225 DownloadItemImpl::DownloadItemImpl(
226 DownloadItemImplDelegate* delegate,
228 const base::FilePath& path,
230 const std::string& mime_type,
231 scoped_ptr<DownloadRequestHandleInterface> request_handle,
232 const net::BoundNetLog& bound_net_log)
233 : is_save_package_download_(true),
234 request_handle_(request_handle.Pass()),
235 download_id_(download_id),
238 target_disposition_(TARGET_DISPOSITION_OVERWRITE),
240 referrer_url_(GURL()),
241 transition_type_(PAGE_TRANSITION_LINK),
242 has_user_gesture_(false),
243 mime_type_(mime_type),
244 original_mime_type_(mime_type),
248 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE),
249 start_tick_(base::TimeTicks::Now()),
250 state_(IN_PROGRESS_INTERNAL),
251 danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
252 start_time_(base::Time::Now()),
255 auto_resume_count_(0),
256 open_when_complete_(false),
257 file_externally_removed_(false),
259 is_temporary_(false),
260 all_data_saved_(false),
261 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
263 delegate_delayed_complete_(false),
264 bound_net_log_(bound_net_log),
265 weak_ptr_factory_(this) {
267 Init(true /* actively downloading */, SRC_SAVE_PAGE_AS);
270 DownloadItemImpl::~DownloadItemImpl() {
271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
273 // Should always have been nuked before now, at worst in
274 // DownloadManager shutdown.
275 DCHECK(!download_file_.get());
277 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadDestroyed(this));
278 delegate_->AssertStateConsistent(this);
282 void DownloadItemImpl::AddObserver(Observer* observer) {
283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
285 observers_.AddObserver(observer);
288 void DownloadItemImpl::RemoveObserver(Observer* observer) {
289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
291 observers_.RemoveObserver(observer);
294 void DownloadItemImpl::UpdateObservers() {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
297 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
300 void DownloadItemImpl::ValidateDangerousDownload() {
301 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
303 DCHECK(IsDangerous());
305 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
307 if (IsDone() || !IsDangerous())
310 RecordDangerousDownloadAccept(GetDangerType(),
311 GetTargetFilePath());
313 danger_type_ = DOWNLOAD_DANGER_TYPE_USER_VALIDATED;
315 bound_net_log_.AddEvent(
316 net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED,
317 base::Bind(&ItemCheckedNetLogCallback, GetDangerType()));
321 MaybeCompleteDownload();
324 void DownloadItemImpl::StealDangerousDownload(
325 const AcquireFileCallback& callback) {
326 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328 DCHECK(IsDangerous());
329 if (download_file_) {
330 BrowserThread::PostTaskAndReplyWithResult(
333 base::Bind(&DownloadFileDetach, base::Passed(&download_file_)),
336 callback.Run(current_path_);
338 current_path_.clear();
340 // We have now been deleted.
343 void DownloadItemImpl::Pause() {
344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
346 // Ignore irrelevant states.
347 if (state_ != IN_PROGRESS_INTERNAL || is_paused_)
350 request_handle_->PauseRequest();
355 void DownloadItemImpl::Resume() {
356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358 case IN_PROGRESS_INTERNAL:
361 request_handle_->ResumeRequest();
366 case COMPLETING_INTERNAL:
367 case COMPLETE_INTERNAL:
368 case CANCELLED_INTERNAL:
369 case RESUMING_INTERNAL:
372 case INTERRUPTED_INTERNAL:
373 auto_resume_count_ = 0; // User input resets the counter.
374 ResumeInterruptedDownload();
377 case MAX_DOWNLOAD_INTERNAL_STATE:
382 void DownloadItemImpl::Cancel(bool user_cancel) {
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
385 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
386 if (state_ != IN_PROGRESS_INTERNAL &&
387 state_ != INTERRUPTED_INTERNAL &&
388 state_ != RESUMING_INTERNAL) {
389 // Small downloads might be complete before this method has a chance to run.
394 RecordDangerousDownloadDiscard(
395 user_cancel ? DOWNLOAD_DISCARD_DUE_TO_USER_ACTION
396 : DOWNLOAD_DISCARD_DUE_TO_SHUTDOWN,
398 GetTargetFilePath());
401 last_reason_ = user_cancel ? DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
402 : DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN;
404 RecordDownloadCount(CANCELLED_COUNT);
406 // TODO(rdsmith/benjhayden): Remove condition as part of
407 // |SavePackage| integration.
408 // |download_file_| can be NULL if Interrupt() is called after the
409 // download file has been released.
410 if (!is_save_package_download_ && download_file_)
411 ReleaseDownloadFile(true);
413 if (state_ == IN_PROGRESS_INTERNAL) {
414 // Cancel the originating URL request unless it's already been cancelled
416 request_handle_->CancelRequest();
419 // Remove the intermediate file if we are cancelling an interrupted download.
420 // Continuable interruptions leave the intermediate file around.
421 if ((state_ == INTERRUPTED_INTERNAL || state_ == RESUMING_INTERNAL) &&
422 !current_path_.empty()) {
423 BrowserThread::PostTask(
424 BrowserThread::FILE, FROM_HERE,
425 base::Bind(base::IgnoreResult(&DeleteDownloadedFile), current_path_));
426 current_path_.clear();
429 TransitionTo(CANCELLED_INTERNAL, UPDATE_OBSERVERS);
432 void DownloadItemImpl::Remove() {
433 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
434 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
436 delegate_->AssertStateConsistent(this);
438 delegate_->AssertStateConsistent(this);
441 delegate_->DownloadRemoved(this);
442 // We have now been deleted.
445 void DownloadItemImpl::OpenDownload() {
446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449 // We don't honor the open_when_complete_ flag for temporary
450 // downloads. Don't set it because it shows up in the UI.
452 open_when_complete_ = !open_when_complete_;
456 if (state_ != COMPLETE_INTERNAL || file_externally_removed_)
459 // Ideally, we want to detect errors in opening and report them, but we
460 // don't generally have the proper interface for that to the external
461 // program that opens the file. So instead we spawn a check to update
462 // the UI if the file has been deleted in parallel with the open.
463 delegate_->CheckForFileRemoval(this);
464 RecordOpen(GetEndTime(), !GetOpened());
466 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this));
467 delegate_->OpenDownload(this);
470 void DownloadItemImpl::ShowDownloadInShell() {
471 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
473 delegate_->ShowDownloadInShell(this);
476 uint32 DownloadItemImpl::GetId() const {
480 DownloadItem::DownloadState DownloadItemImpl::GetState() const {
481 return InternalToExternalState(state_);
484 DownloadInterruptReason DownloadItemImpl::GetLastReason() const {
488 bool DownloadItemImpl::IsPaused() const {
492 bool DownloadItemImpl::IsTemporary() const {
493 return is_temporary_;
496 bool DownloadItemImpl::CanResume() const {
497 if ((GetState() == IN_PROGRESS) && IsPaused())
500 if (state_ != INTERRUPTED_INTERNAL)
503 // Downloads that don't have a WebContents should still be resumable, but this
504 // isn't currently the case. See ResumeInterruptedDownload().
505 if (!GetWebContents())
508 ResumeMode resume_mode = GetResumeMode();
509 return IsDownloadResumptionEnabled() &&
510 (resume_mode == RESUME_MODE_USER_RESTART ||
511 resume_mode == RESUME_MODE_USER_CONTINUE);
514 bool DownloadItemImpl::IsDone() const {
516 case IN_PROGRESS_INTERNAL:
517 case COMPLETING_INTERNAL:
520 case COMPLETE_INTERNAL:
521 case CANCELLED_INTERNAL:
524 case INTERRUPTED_INTERNAL:
527 case RESUMING_INTERNAL:
530 case MAX_DOWNLOAD_INTERNAL_STATE:
537 const GURL& DownloadItemImpl::GetURL() const {
538 return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.back();
541 const std::vector<GURL>& DownloadItemImpl::GetUrlChain() const {
545 const GURL& DownloadItemImpl::GetOriginalUrl() const {
546 // Be careful about taking the front() of possibly-empty vectors!
547 // http://crbug.com/190096
548 return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.front();
551 const GURL& DownloadItemImpl::GetReferrerUrl() const {
552 return referrer_url_;
555 const GURL& DownloadItemImpl::GetTabUrl() const {
559 const GURL& DownloadItemImpl::GetTabReferrerUrl() const {
560 return tab_referrer_url_;
563 std::string DownloadItemImpl::GetSuggestedFilename() const {
564 return suggested_filename_;
567 std::string DownloadItemImpl::GetContentDisposition() const {
568 return content_disposition_;
571 std::string DownloadItemImpl::GetMimeType() const {
575 std::string DownloadItemImpl::GetOriginalMimeType() const {
576 return original_mime_type_;
579 std::string DownloadItemImpl::GetRemoteAddress() const {
580 return remote_address_;
583 bool DownloadItemImpl::HasUserGesture() const {
584 return has_user_gesture_;
587 PageTransition DownloadItemImpl::GetTransitionType() const {
588 return transition_type_;
591 const std::string& DownloadItemImpl::GetLastModifiedTime() const {
592 return last_modified_time_;
595 const std::string& DownloadItemImpl::GetETag() const {
599 bool DownloadItemImpl::IsSavePackageDownload() const {
600 return is_save_package_download_;
603 const base::FilePath& DownloadItemImpl::GetFullPath() const {
604 return current_path_;
607 const base::FilePath& DownloadItemImpl::GetTargetFilePath() const {
611 const base::FilePath& DownloadItemImpl::GetForcedFilePath() const {
612 // TODO(asanka): Get rid of GetForcedFilePath(). We should instead just
613 // require that clients respect GetTargetFilePath() if it is already set.
614 return forced_file_path_;
617 base::FilePath DownloadItemImpl::GetFileNameToReportUser() const {
618 if (!display_name_.empty())
619 return display_name_;
620 return target_path_.BaseName();
623 DownloadItem::TargetDisposition DownloadItemImpl::GetTargetDisposition() const {
624 return target_disposition_;
627 const std::string& DownloadItemImpl::GetHash() const {
631 const std::string& DownloadItemImpl::GetHashState() const {
635 bool DownloadItemImpl::GetFileExternallyRemoved() const {
636 return file_externally_removed_;
639 void DownloadItemImpl::DeleteFile(const base::Callback<void(bool)>& callback) {
640 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
641 if (GetState() != DownloadItem::COMPLETE) {
642 // Pass a null WeakPtr so it doesn't call OnDownloadedFileRemoved.
643 BrowserThread::PostTask(
644 BrowserThread::UI, FROM_HERE,
645 base::Bind(&DeleteDownloadedFileDone,
646 base::WeakPtr<DownloadItemImpl>(), callback, false));
649 if (current_path_.empty() || file_externally_removed_) {
650 // Pass a null WeakPtr so it doesn't call OnDownloadedFileRemoved.
651 BrowserThread::PostTask(
652 BrowserThread::UI, FROM_HERE,
653 base::Bind(&DeleteDownloadedFileDone,
654 base::WeakPtr<DownloadItemImpl>(), callback, true));
657 BrowserThread::PostTaskAndReplyWithResult(
658 BrowserThread::FILE, FROM_HERE,
659 base::Bind(&DeleteDownloadedFile, current_path_),
660 base::Bind(&DeleteDownloadedFileDone,
661 weak_ptr_factory_.GetWeakPtr(), callback));
664 bool DownloadItemImpl::IsDangerous() const {
666 // TODO(noelutz): At this point only the windows views UI supports
667 // warnings based on dangerous content.
668 return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
669 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
670 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
671 danger_type_ == DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
672 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
673 danger_type_ == DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED);
675 return (danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
676 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL);
680 DownloadDangerType DownloadItemImpl::GetDangerType() const {
684 bool DownloadItemImpl::TimeRemaining(base::TimeDelta* remaining) const {
685 if (total_bytes_ <= 0)
686 return false; // We never received the content_length for this download.
688 int64 speed = CurrentSpeed();
692 *remaining = base::TimeDelta::FromSeconds(
693 (total_bytes_ - received_bytes_) / speed);
697 int64 DownloadItemImpl::CurrentSpeed() const {
700 return bytes_per_sec_;
703 int DownloadItemImpl::PercentComplete() const {
704 // If the delegate is delaying completion of the download, then we have no
705 // idea how long it will take.
706 if (delegate_delayed_complete_ || total_bytes_ <= 0)
709 return static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
712 bool DownloadItemImpl::AllDataSaved() const {
713 return all_data_saved_;
716 int64 DownloadItemImpl::GetTotalBytes() const {
720 int64 DownloadItemImpl::GetReceivedBytes() const {
721 return received_bytes_;
724 base::Time DownloadItemImpl::GetStartTime() const {
728 base::Time DownloadItemImpl::GetEndTime() const {
732 bool DownloadItemImpl::CanShowInFolder() {
733 // A download can be shown in the folder if the downloaded file is in a known
735 return CanOpenDownload() && !GetFullPath().empty();
738 bool DownloadItemImpl::CanOpenDownload() {
739 // We can open the file or mark it for opening on completion if the download
740 // is expected to complete successfully. Exclude temporary downloads, since
741 // they aren't owned by the download system.
742 const bool is_complete = GetState() == DownloadItem::COMPLETE;
743 return (!IsDone() || is_complete) && !IsTemporary() &&
744 !file_externally_removed_;
747 bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() {
748 return delegate_->ShouldOpenFileBasedOnExtension(GetTargetFilePath());
751 bool DownloadItemImpl::GetOpenWhenComplete() const {
752 return open_when_complete_;
755 bool DownloadItemImpl::GetAutoOpened() {
759 bool DownloadItemImpl::GetOpened() const {
763 BrowserContext* DownloadItemImpl::GetBrowserContext() const {
764 return delegate_->GetBrowserContext();
767 WebContents* DownloadItemImpl::GetWebContents() const {
768 // TODO(rdsmith): Remove null check after removing GetWebContents() from
769 // paths that might be used by DownloadItems created from history import.
770 // Currently such items have null request_handle_s, where other items
771 // (regular and SavePackage downloads) have actual objects off the pointer.
773 return request_handle_->GetWebContents();
777 void DownloadItemImpl::OnContentCheckCompleted(DownloadDangerType danger_type) {
778 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
779 DCHECK(AllDataSaved());
780 VLOG(20) << __FUNCTION__ << " danger_type=" << danger_type
781 << " download=" << DebugString(true);
782 SetDangerType(danger_type);
786 void DownloadItemImpl::SetOpenWhenComplete(bool open) {
787 open_when_complete_ = open;
790 void DownloadItemImpl::SetIsTemporary(bool temporary) {
791 is_temporary_ = temporary;
794 void DownloadItemImpl::SetOpened(bool opened) {
798 void DownloadItemImpl::SetDisplayName(const base::FilePath& name) {
799 display_name_ = name;
802 std::string DownloadItemImpl::DebugString(bool verbose) const {
803 std::string description =
804 base::StringPrintf("{ id = %d"
807 DebugDownloadStateString(state_));
809 // Construct a string of the URL chain.
810 std::string url_list("<none>");
811 if (!url_chain_.empty()) {
812 std::vector<GURL>::const_iterator iter = url_chain_.begin();
813 std::vector<GURL>::const_iterator last = url_chain_.end();
814 url_list = (*iter).is_valid() ? (*iter).spec() : "<invalid>";
816 for ( ; verbose && (iter != last); ++iter) {
817 url_list += " ->\n\t";
818 const GURL& next_url = *iter;
819 url_list += next_url.is_valid() ? next_url.spec() : "<invalid>";
824 description += base::StringPrintf(
826 " received = %" PRId64
830 " auto_resume_count = %d"
832 " all_data_saved = %c"
833 " last_modified = '%s'"
835 " has_download_file = %s"
836 " url_chain = \n\t\"%s\"\n\t"
837 " full_path = \"%" PRFilePath "\"\n\t"
838 " target_path = \"%" PRFilePath "\"",
841 DownloadInterruptReasonToString(last_reason_).c_str(),
842 IsPaused() ? 'T' : 'F',
843 DebugResumeModeString(GetResumeMode()),
846 AllDataSaved() ? 'T' : 'F',
847 GetLastModifiedTime().c_str(),
849 download_file_.get() ? "true" : "false",
851 GetFullPath().value().c_str(),
852 GetTargetFilePath().value().c_str());
854 description += base::StringPrintf(" url = \"%s\"", url_list.c_str());
862 DownloadItemImpl::ResumeMode DownloadItemImpl::GetResumeMode() const {
863 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
864 // We can't continue without a handle on the intermediate file.
865 // We also can't continue if we don't have some verifier to make sure
866 // we're getting the same file.
867 const bool force_restart =
868 (current_path_.empty() || (etag_.empty() && last_modified_time_.empty()));
870 // We won't auto-restart if we've used up our attempts or the
871 // download has been paused by user action.
872 const bool force_user =
873 (auto_resume_count_ >= kMaxAutoResumeAttempts || is_paused_);
875 ResumeMode mode = RESUME_MODE_INVALID;
877 switch(last_reason_) {
878 case DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR:
879 case DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT:
880 if (force_restart && force_user)
881 mode = RESUME_MODE_USER_RESTART;
882 else if (force_restart)
883 mode = RESUME_MODE_IMMEDIATE_RESTART;
885 mode = RESUME_MODE_USER_CONTINUE;
887 mode = RESUME_MODE_IMMEDIATE_CONTINUE;
890 case DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION:
891 case DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE:
892 case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT:
894 mode = RESUME_MODE_USER_RESTART;
896 mode = RESUME_MODE_IMMEDIATE_RESTART;
899 case DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED:
900 case DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED:
901 case DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN:
902 case DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST:
903 case DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED:
904 case DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN:
905 case DOWNLOAD_INTERRUPT_REASON_CRASH:
907 mode = RESUME_MODE_USER_RESTART;
909 mode = RESUME_MODE_USER_CONTINUE;
912 case DOWNLOAD_INTERRUPT_REASON_FILE_FAILED:
913 case DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
914 case DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
915 case DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG:
916 case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
917 mode = RESUME_MODE_USER_RESTART;
920 case DOWNLOAD_INTERRUPT_REASON_NONE:
921 case DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED:
922 case DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT:
923 case DOWNLOAD_INTERRUPT_REASON_USER_CANCELED:
924 case DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED:
925 case DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED:
926 mode = RESUME_MODE_INVALID;
933 void DownloadItemImpl::MergeOriginInfoOnResume(
934 const DownloadCreateInfo& new_create_info) {
935 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
936 DCHECK_EQ(RESUMING_INTERNAL, state_);
937 DCHECK(!new_create_info.url_chain.empty());
939 // We are going to tack on any new redirects to our list of redirects.
940 // When a download is resumed, the URL used for the resumption request is the
941 // one at the end of the previous redirect chain. Tacking additional redirects
942 // to the end of this chain ensures that:
943 // - If the download needs to be resumed again, the ETag/Last-Modified headers
944 // will be used with the last server that sent them to us.
945 // - The redirect chain contains all the servers that were involved in this
946 // download since the initial request, in order.
947 std::vector<GURL>::const_iterator chain_iter =
948 new_create_info.url_chain.begin();
949 if (*chain_iter == url_chain_.back())
952 // Record some stats. If the precondition failed (the server returned
953 // HTTP_PRECONDITION_FAILED), then the download will automatically retried as
954 // a full request rather than a partial. Full restarts clobber validators.
955 int origin_state = 0;
956 if (chain_iter != new_create_info.url_chain.end())
957 origin_state |= ORIGIN_STATE_ON_RESUMPTION_ADDITIONAL_REDIRECTS;
958 if (etag_ != new_create_info.etag ||
959 last_modified_time_ != new_create_info.last_modified)
960 origin_state |= ORIGIN_STATE_ON_RESUMPTION_VALIDATORS_CHANGED;
961 if (content_disposition_ != new_create_info.content_disposition)
962 origin_state |= ORIGIN_STATE_ON_RESUMPTION_CONTENT_DISPOSITION_CHANGED;
963 RecordOriginStateOnResumption(new_create_info.save_info->offset != 0,
967 url_chain_.end(), chain_iter, new_create_info.url_chain.end());
968 etag_ = new_create_info.etag;
969 last_modified_time_ = new_create_info.last_modified;
970 content_disposition_ = new_create_info.content_disposition;
972 // Don't update observers. This method is expected to be called just before a
973 // DownloadFile is created and Start() is called. The observers will be
974 // notified when the download transitions to the IN_PROGRESS state.
977 void DownloadItemImpl::NotifyRemoved() {
978 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadRemoved(this));
981 void DownloadItemImpl::OnDownloadedFileRemoved() {
982 file_externally_removed_ = true;
983 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
987 base::WeakPtr<DownloadDestinationObserver>
988 DownloadItemImpl::DestinationObserverAsWeakPtr() {
989 return weak_ptr_factory_.GetWeakPtr();
992 const net::BoundNetLog& DownloadItemImpl::GetBoundNetLog() const {
993 return bound_net_log_;
996 void DownloadItemImpl::SetTotalBytes(int64 total_bytes) {
997 total_bytes_ = total_bytes;
1000 void DownloadItemImpl::OnAllDataSaved(const std::string& final_hash) {
1001 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1003 DCHECK_EQ(IN_PROGRESS_INTERNAL, state_);
1004 DCHECK(!all_data_saved_);
1005 all_data_saved_ = true;
1006 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
1008 // Store final hash and null out intermediate serialized hash state.
1015 void DownloadItemImpl::MarkAsComplete() {
1016 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1018 DCHECK(all_data_saved_);
1019 end_time_ = base::Time::Now();
1020 TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS);
1023 void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far,
1024 int64 bytes_per_sec,
1025 const std::string& hash_state) {
1026 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1027 VLOG(20) << __FUNCTION__ << " so_far=" << bytes_so_far
1028 << " per_sec=" << bytes_per_sec << " download=" << DebugString(true);
1030 if (GetState() != IN_PROGRESS) {
1031 // Ignore if we're no longer in-progress. This can happen if we race a
1032 // Cancel on the UI thread with an update on the FILE thread.
1034 // TODO(rdsmith): Arguably we should let this go through, as this means
1035 // the download really did get further than we know before it was
1036 // cancelled. But the gain isn't very large, and the code is more
1037 // fragile if it has to support in progress updates in a non-in-progress
1038 // state. This issue should be readdressed when we revamp performance
1042 bytes_per_sec_ = bytes_per_sec;
1043 hash_state_ = hash_state;
1044 received_bytes_ = bytes_so_far;
1046 // If we've received more data than we were expecting (bad server info?),
1047 // revert to 'unknown size mode'.
1048 if (received_bytes_ > total_bytes_)
1051 if (bound_net_log_.IsLogging()) {
1052 bound_net_log_.AddEvent(
1053 net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED,
1054 net::NetLog::Int64Callback("bytes_so_far", received_bytes_));
1060 void DownloadItemImpl::DestinationError(DownloadInterruptReason reason) {
1061 // Postpone recognition of this error until after file name determination
1062 // has completed and the intermediate file has been renamed to simplify
1063 // resumption conditions.
1064 if (current_path_.empty() || target_path_.empty())
1065 destination_error_ = reason;
1070 void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) {
1071 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
1072 if (GetState() != IN_PROGRESS)
1074 OnAllDataSaved(final_hash);
1075 MaybeCompleteDownload();
1078 // **** Download progression cascade
1080 void DownloadItemImpl::Init(bool active,
1081 DownloadType download_type) {
1082 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1085 RecordDownloadCount(START_COUNT);
1087 std::string file_name;
1088 if (download_type == SRC_HISTORY_IMPORT) {
1089 // target_path_ works for History and Save As versions.
1090 file_name = target_path_.AsUTF8Unsafe();
1092 // See if it's set programmatically.
1093 file_name = forced_file_path_.AsUTF8Unsafe();
1094 // Possibly has a 'download' attribute for the anchor.
1095 if (file_name.empty())
1096 file_name = suggested_filename_;
1097 // From the URL file name.
1098 if (file_name.empty())
1099 file_name = GetURL().ExtractFileName();
1102 base::Callback<base::Value*(net::NetLog::LogLevel)> active_data = base::Bind(
1103 &ItemActivatedNetLogCallback, this, download_type, &file_name);
1105 bound_net_log_.BeginEvent(
1106 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data);
1108 bound_net_log_.AddEvent(
1109 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE, active_data);
1112 VLOG(20) << __FUNCTION__ << "() " << DebugString(true);
1115 // We're starting the download.
1116 void DownloadItemImpl::Start(
1117 scoped_ptr<DownloadFile> file,
1118 scoped_ptr<DownloadRequestHandleInterface> req_handle) {
1119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1120 DCHECK(!download_file_.get());
1122 DCHECK(req_handle.get());
1124 download_file_ = file.Pass();
1125 request_handle_ = req_handle.Pass();
1127 if (GetState() == CANCELLED) {
1128 // The download was in the process of resuming when it was cancelled. Don't
1130 ReleaseDownloadFile(true);
1131 request_handle_->CancelRequest();
1135 TransitionTo(IN_PROGRESS_INTERNAL, UPDATE_OBSERVERS);
1137 BrowserThread::PostTask(
1138 BrowserThread::FILE, FROM_HERE,
1139 base::Bind(&DownloadFile::Initialize,
1140 // Safe because we control download file lifetime.
1141 base::Unretained(download_file_.get()),
1142 base::Bind(&DownloadItemImpl::OnDownloadFileInitialized,
1143 weak_ptr_factory_.GetWeakPtr())));
1146 void DownloadItemImpl::OnDownloadFileInitialized(
1147 DownloadInterruptReason result) {
1148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1149 if (result != DOWNLOAD_INTERRUPT_REASON_NONE) {
1151 // TODO(rdsmith/asanka): Arguably we should show this in the UI, but
1152 // it's not at all clear what to show--we haven't done filename
1153 // determination, so we don't know what name to display. OTOH,
1154 // the failure mode of not showing the DI if the file initialization
1155 // fails isn't a good one. Can we hack up a name based on the
1156 // URLRequest? We'll need to make sure that initialization happens
1157 // properly. Possibly the right thing is to have the UI handle
1158 // this case specially.
1162 delegate_->DetermineDownloadTarget(
1163 this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined,
1164 weak_ptr_factory_.GetWeakPtr()));
1167 // Called by delegate_ when the download target path has been
1169 void DownloadItemImpl::OnDownloadTargetDetermined(
1170 const base::FilePath& target_path,
1171 TargetDisposition disposition,
1172 DownloadDangerType danger_type,
1173 const base::FilePath& intermediate_path) {
1174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1176 // If the |target_path| is empty, then we consider this download to be
1178 if (target_path.empty()) {
1183 // TODO(rdsmith,asanka): We are ignoring the possibility that the download
1184 // has been interrupted at this point until we finish the intermediate
1185 // rename and set the full path. That's dangerous, because we might race
1186 // with resumption, either manual (because the interrupt is visible to the
1187 // UI) or automatic. If we keep the "ignore an error on download until file
1188 // name determination complete" semantics, we need to make sure that the
1189 // error is kept completely invisible until that point.
1191 VLOG(20) << __FUNCTION__ << " " << target_path.value() << " " << disposition
1192 << " " << danger_type << " " << DebugString(true);
1194 target_path_ = target_path;
1195 target_disposition_ = disposition;
1196 SetDangerType(danger_type);
1198 // We want the intermediate and target paths to refer to the same directory so
1199 // that they are both on the same device and subject to same
1200 // space/permission/availability constraints.
1201 DCHECK(intermediate_path.DirName() == target_path.DirName());
1203 // During resumption, we may choose to proceed with the same intermediate
1204 // file. No rename is necessary if our intermediate file already has the
1207 // The intermediate name may change from its original value during filename
1208 // determination on resumption, for example if the reason for the interruption
1209 // was the download target running out space, resulting in a user prompt.
1210 if (intermediate_path == current_path_) {
1211 OnDownloadRenamedToIntermediateName(DOWNLOAD_INTERRUPT_REASON_NONE,
1216 // Rename to intermediate name.
1217 // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a
1218 // spurious rename when we can just rename to the final
1219 // filename. Unnecessary renames may cause bugs like
1220 // http://crbug.com/74187.
1221 DCHECK(!is_save_package_download_);
1222 DCHECK(download_file_.get());
1223 DownloadFile::RenameCompletionCallback callback =
1224 base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName,
1225 weak_ptr_factory_.GetWeakPtr());
1226 BrowserThread::PostTask(
1227 BrowserThread::FILE, FROM_HERE,
1228 base::Bind(&DownloadFile::RenameAndUniquify,
1229 // Safe because we control download file lifetime.
1230 base::Unretained(download_file_.get()),
1231 intermediate_path, callback));
1234 void DownloadItemImpl::OnDownloadRenamedToIntermediateName(
1235 DownloadInterruptReason reason,
1236 const base::FilePath& full_path) {
1237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1238 VLOG(20) << __FUNCTION__ << " download=" << DebugString(true);
1240 if (DOWNLOAD_INTERRUPT_REASON_NONE != destination_error_) {
1241 // Process destination error. If both |reason| and |destination_error_|
1242 // refer to actual errors, we want to use the |destination_error_| as the
1243 // argument to the Interrupt() routine, as it happened first.
1244 if (reason == DOWNLOAD_INTERRUPT_REASON_NONE)
1245 SetFullPath(full_path);
1246 Interrupt(destination_error_);
1247 destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE;
1248 } else if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) {
1250 // All file errors result in file deletion above; no need to cleanup. The
1251 // current_path_ should be empty. Resuming this download will force a
1252 // restart and a re-doing of filename determination.
1253 DCHECK(current_path_.empty());
1255 SetFullPath(full_path);
1257 MaybeCompleteDownload();
1261 // When SavePackage downloads MHTML to GData (see
1262 // SavePackageFilePickerChromeOS), GData calls MaybeCompleteDownload() like it
1263 // does for non-SavePackage downloads, but SavePackage downloads never satisfy
1264 // IsDownloadReadyForCompletion(). GDataDownloadObserver manually calls
1265 // DownloadItem::UpdateObservers() when the upload completes so that SavePackage
1266 // notices that the upload has completed and runs its normal Finish() pathway.
1267 // MaybeCompleteDownload() is never the mechanism by which SavePackage completes
1268 // downloads. SavePackage always uses its own Finish() to mark downloads
1270 void DownloadItemImpl::MaybeCompleteDownload() {
1271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1272 DCHECK(!is_save_package_download_);
1274 if (!IsDownloadReadyForCompletion(
1275 base::Bind(&DownloadItemImpl::MaybeCompleteDownload,
1276 weak_ptr_factory_.GetWeakPtr())))
1279 // TODO(rdsmith): DCHECK that we only pass through this point
1280 // once per download. The natural way to do this is by a state
1281 // transition on the DownloadItem.
1283 // Confirm we're in the proper set of states to be here;
1284 // have all data, have a history handle, (validated or safe).
1285 DCHECK_EQ(IN_PROGRESS_INTERNAL, state_);
1286 DCHECK(!IsDangerous());
1287 DCHECK(all_data_saved_);
1289 OnDownloadCompleting();
1292 // Called by MaybeCompleteDownload() when it has determined that the download
1293 // is ready for completion.
1294 void DownloadItemImpl::OnDownloadCompleting() {
1295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1297 if (state_ != IN_PROGRESS_INTERNAL)
1300 VLOG(20) << __FUNCTION__ << "()"
1301 << " " << DebugString(true);
1302 DCHECK(!GetTargetFilePath().empty());
1303 DCHECK(!IsDangerous());
1305 // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration.
1306 if (is_save_package_download_) {
1307 // Avoid doing anything on the file thread; there's nothing we control
1309 // Strictly speaking, this skips giving the embedder a chance to open
1310 // the download. But on a save package download, there's no real
1311 // concept of opening.
1316 DCHECK(download_file_.get());
1317 // Unilaterally rename; even if it already has the right name,
1318 // we need theannotation.
1319 DownloadFile::RenameCompletionCallback callback =
1320 base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName,
1321 weak_ptr_factory_.GetWeakPtr());
1322 BrowserThread::PostTask(
1323 BrowserThread::FILE, FROM_HERE,
1324 base::Bind(&DownloadFile::RenameAndAnnotate,
1325 base::Unretained(download_file_.get()),
1326 GetTargetFilePath(), callback));
1329 void DownloadItemImpl::OnDownloadRenamedToFinalName(
1330 DownloadInterruptReason reason,
1331 const base::FilePath& full_path) {
1332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1333 DCHECK(!is_save_package_download_);
1335 // If a cancel or interrupt hit, we'll cancel the DownloadFile, which
1336 // will result in deleting the file on the file thread. So we don't
1337 // care about the name having been changed.
1338 if (state_ != IN_PROGRESS_INTERNAL)
1341 VLOG(20) << __FUNCTION__ << "()"
1342 << " full_path = \"" << full_path.value() << "\""
1343 << " " << DebugString(false);
1345 if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) {
1348 // All file errors should have resulted in in file deletion above. On
1349 // resumption we will need to re-do filename determination.
1350 DCHECK(current_path_.empty());
1354 DCHECK(target_path_ == full_path);
1356 if (full_path != current_path_) {
1357 // full_path is now the current and target file path.
1358 DCHECK(!full_path.empty());
1359 SetFullPath(full_path);
1362 // Complete the download and release the DownloadFile.
1363 DCHECK(download_file_.get());
1364 ReleaseDownloadFile(false);
1366 // We're not completely done with the download item yet, but at this
1367 // point we're committed to complete the download. Cancels (or Interrupts,
1368 // though it's not clear how they could happen) after this point will be
1370 TransitionTo(COMPLETING_INTERNAL, DONT_UPDATE_OBSERVERS);
1372 if (delegate_->ShouldOpenDownload(
1373 this, base::Bind(&DownloadItemImpl::DelayedDownloadOpened,
1374 weak_ptr_factory_.GetWeakPtr()))) {
1377 delegate_delayed_complete_ = true;
1382 void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) {
1383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1385 auto_opened_ = auto_opened;
1389 void DownloadItemImpl::Completed() {
1390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1392 VLOG(20) << __FUNCTION__ << "() " << DebugString(false);
1394 DCHECK(all_data_saved_);
1395 end_time_ = base::Time::Now();
1396 TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS);
1397 RecordDownloadCompleted(start_tick_, received_bytes_);
1400 // If it was already handled by the delegate, do nothing.
1401 } else if (GetOpenWhenComplete() ||
1402 ShouldOpenFileBasedOnExtension() ||
1404 // If the download is temporary, like in drag-and-drop, do not open it but
1405 // we still need to set it auto-opened so that it can be removed from the
1410 auto_opened_ = true;
1415 void DownloadItemImpl::OnResumeRequestStarted(
1417 DownloadInterruptReason interrupt_reason) {
1418 // If |item| is not NULL, then Start() has been called already, and nothing
1419 // more needs to be done here.
1421 DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
1422 DCHECK_EQ(static_cast<DownloadItem*>(this), item);
1425 // Otherwise, the request failed without passing through
1426 // DownloadResourceHandler::OnResponseStarted.
1427 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
1428 Interrupt(interrupt_reason);
1431 // **** End of Download progression cascade
1433 // An error occurred somewhere.
1434 void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) {
1435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1436 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason);
1438 // Somewhat counter-intuitively, it is possible for us to receive an
1439 // interrupt after we've already been interrupted. The generation of
1440 // interrupts from the file thread Renames and the generation of
1441 // interrupts from disk writes go through two different mechanisms (driven
1442 // by rename requests from UI thread and by write requests from IO thread,
1443 // respectively), and since we choose not to keep state on the File thread,
1444 // this is the place where the races collide. It's also possible for
1445 // interrupts to race with cancels.
1447 // Whatever happens, the first one to hit the UI thread wins.
1448 if (state_ != IN_PROGRESS_INTERNAL && state_ != RESUMING_INTERNAL)
1451 last_reason_ = reason;
1453 ResumeMode resume_mode = GetResumeMode();
1455 if (state_ == IN_PROGRESS_INTERNAL) {
1456 // Cancel (delete file) if:
1457 // 1) we're going to restart.
1458 // 2) Resumption isn't possible (download was cancelled or blocked due to
1459 // security restrictions).
1460 // 3) Resumption isn't enabled.
1461 // No point in leaving data around we aren't going to use.
1462 ReleaseDownloadFile(resume_mode == RESUME_MODE_IMMEDIATE_RESTART ||
1463 resume_mode == RESUME_MODE_USER_RESTART ||
1464 resume_mode == RESUME_MODE_INVALID ||
1465 !IsDownloadResumptionEnabled());
1467 // Cancel the originating URL request.
1468 request_handle_->CancelRequest();
1470 DCHECK(!download_file_.get());
1473 // Reset all data saved, as even if we did save all the data we're going
1474 // to go through another round of downloading when we resume.
1475 // There's a potential problem here in the abstract, as if we did download
1476 // all the data and then run into a continuable error, on resumption we
1477 // won't download any more data. However, a) there are currently no
1478 // continuable errors that can occur after we download all the data, and
1479 // b) if there were, that would probably simply result in a null range
1480 // request, which would generate a DestinationCompleted() notification
1481 // from the DownloadFile, which would behave properly with setting
1482 // all_data_saved_ to false here.
1483 all_data_saved_ = false;
1485 TransitionTo(INTERRUPTED_INTERNAL, DONT_UPDATE_OBSERVERS);
1486 RecordDownloadInterrupted(reason, received_bytes_, total_bytes_);
1487 if (!GetWebContents())
1488 RecordDownloadCount(INTERRUPTED_WITHOUT_WEBCONTENTS);
1490 AutoResumeIfValid();
1494 void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) {
1495 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1498 BrowserThread::PostTask(
1499 BrowserThread::FILE, FROM_HERE,
1500 // Will be deleted at end of task execution.
1501 base::Bind(&DownloadFileCancel, base::Passed(&download_file_)));
1502 // Avoid attempting to reuse the intermediate file by clearing out
1504 current_path_.clear();
1506 BrowserThread::PostTask(
1507 BrowserThread::FILE,
1509 base::Bind(base::IgnoreResult(&DownloadFileDetach),
1510 // Will be deleted at end of task execution.
1511 base::Passed(&download_file_)));
1513 // Don't accept any more messages from the DownloadFile, and null
1514 // out any previous "all data received". This also breaks links to
1515 // other entities we've given out weak pointers to.
1516 weak_ptr_factory_.InvalidateWeakPtrs();
1519 bool DownloadItemImpl::IsDownloadReadyForCompletion(
1520 const base::Closure& state_change_notification) {
1521 // If we don't have all the data, the download is not ready for
1523 if (!AllDataSaved())
1526 // If the download is dangerous, but not yet validated, it's not ready for
1531 // If the download isn't active (e.g. has been cancelled) it's not
1532 // ready for completion.
1533 if (state_ != IN_PROGRESS_INTERNAL)
1536 // If the target filename hasn't been determined, then it's not ready for
1537 // completion. This is checked in ReadyForDownloadCompletionDone().
1538 if (GetTargetFilePath().empty())
1541 // This is checked in NeedsRename(). Without this conditional,
1542 // browser_tests:DownloadTest.DownloadMimeType fails the DCHECK.
1543 if (target_path_.DirName() != current_path_.DirName())
1546 // Give the delegate a chance to hold up a stop sign. It'll call
1547 // use back through the passed callback if it does and that state changes.
1548 if (!delegate_->ShouldCompleteDownload(this, state_change_notification))
1554 void DownloadItemImpl::TransitionTo(DownloadInternalState new_state,
1555 ShouldUpdateObservers notify_action) {
1556 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1558 if (state_ == new_state)
1561 DownloadInternalState old_state = state_;
1565 case COMPLETING_INTERNAL:
1566 bound_net_log_.AddEvent(
1567 net::NetLog::TYPE_DOWNLOAD_ITEM_COMPLETING,
1568 base::Bind(&ItemCompletingNetLogCallback, received_bytes_, &hash_));
1570 case COMPLETE_INTERNAL:
1571 bound_net_log_.AddEvent(
1572 net::NetLog::TYPE_DOWNLOAD_ITEM_FINISHED,
1573 base::Bind(&ItemFinishedNetLogCallback, auto_opened_));
1575 case INTERRUPTED_INTERNAL:
1576 bound_net_log_.AddEvent(
1577 net::NetLog::TYPE_DOWNLOAD_ITEM_INTERRUPTED,
1578 base::Bind(&ItemInterruptedNetLogCallback, last_reason_,
1579 received_bytes_, &hash_state_));
1581 case IN_PROGRESS_INTERNAL:
1582 if (old_state == INTERRUPTED_INTERNAL) {
1583 bound_net_log_.AddEvent(
1584 net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED,
1585 base::Bind(&ItemResumingNetLogCallback,
1586 false, last_reason_, received_bytes_, &hash_state_));
1589 case CANCELLED_INTERNAL:
1590 bound_net_log_.AddEvent(
1591 net::NetLog::TYPE_DOWNLOAD_ITEM_CANCELED,
1592 base::Bind(&ItemCanceledNetLogCallback, received_bytes_,
1599 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true)
1600 << " " << InternalToExternalState(old_state)
1601 << " " << InternalToExternalState(state_);
1603 bool is_done = (state_ != IN_PROGRESS_INTERNAL &&
1604 state_ != COMPLETING_INTERNAL);
1605 bool was_done = (old_state != IN_PROGRESS_INTERNAL &&
1606 old_state != COMPLETING_INTERNAL);
1608 if (is_done && !was_done)
1609 bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE);
1612 if (was_done && !is_done) {
1613 std::string file_name(target_path_.BaseName().AsUTF8Unsafe());
1614 bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE,
1615 base::Bind(&ItemActivatedNetLogCallback,
1616 this, SRC_ACTIVE_DOWNLOAD,
1620 if (notify_action == UPDATE_OBSERVERS)
1624 void DownloadItemImpl::SetDangerType(DownloadDangerType danger_type) {
1625 if (danger_type != danger_type_) {
1626 bound_net_log_.AddEvent(
1627 net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED,
1628 base::Bind(&ItemCheckedNetLogCallback, danger_type));
1630 // Only record the Malicious UMA stat if it's going from {not malicious} ->
1632 if ((danger_type_ == DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS ||
1633 danger_type_ == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
1634 danger_type_ == DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
1635 danger_type_ == DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT) &&
1636 (danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
1637 danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
1638 danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
1639 danger_type == DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED)) {
1640 RecordMaliciousDownloadClassified(danger_type);
1642 danger_type_ = danger_type;
1645 void DownloadItemImpl::SetFullPath(const base::FilePath& new_path) {
1646 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1647 VLOG(20) << __FUNCTION__ << "()"
1648 << " new_path = \"" << new_path.value() << "\""
1649 << " " << DebugString(true);
1650 DCHECK(!new_path.empty());
1652 bound_net_log_.AddEvent(
1653 net::NetLog::TYPE_DOWNLOAD_ITEM_RENAMED,
1654 base::Bind(&ItemRenamedNetLogCallback, ¤t_path_, &new_path));
1656 current_path_ = new_path;
1659 void DownloadItemImpl::AutoResumeIfValid() {
1660 DVLOG(20) << __FUNCTION__ << "() " << DebugString(true);
1661 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1662 ResumeMode mode = GetResumeMode();
1664 if (mode != RESUME_MODE_IMMEDIATE_RESTART &&
1665 mode != RESUME_MODE_IMMEDIATE_CONTINUE) {
1669 auto_resume_count_++;
1671 ResumeInterruptedDownload();
1674 void DownloadItemImpl::ResumeInterruptedDownload() {
1675 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1677 // If the flag for downloads resumption isn't enabled, ignore
1679 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
1680 if (!command_line.HasSwitch(switches::kEnableDownloadResumption))
1683 // If we're not interrupted, ignore the request; our caller is drunk.
1684 if (state_ != INTERRUPTED_INTERNAL)
1687 // If we can't get a web contents, we can't resume the download.
1688 // TODO(rdsmith): Find some alternative web contents to use--this
1689 // means we can't restart a download if it's a download imported
1690 // from the history.
1691 if (!GetWebContents())
1694 // Reset the appropriate state if restarting.
1695 ResumeMode mode = GetResumeMode();
1696 if (mode == RESUME_MODE_IMMEDIATE_RESTART ||
1697 mode == RESUME_MODE_USER_RESTART) {
1698 received_bytes_ = 0;
1700 last_modified_time_ = "";
1704 scoped_ptr<DownloadUrlParameters> download_params(
1705 DownloadUrlParameters::FromWebContents(GetWebContents(),
1708 download_params->set_file_path(GetFullPath());
1709 download_params->set_offset(GetReceivedBytes());
1710 download_params->set_hash_state(GetHashState());
1711 download_params->set_last_modified(GetLastModifiedTime());
1712 download_params->set_etag(GetETag());
1713 download_params->set_callback(
1714 base::Bind(&DownloadItemImpl::OnResumeRequestStarted,
1715 weak_ptr_factory_.GetWeakPtr()));
1717 delegate_->ResumeInterruptedDownload(download_params.Pass(), GetId());
1718 // Just in case we were interrupted while paused.
1721 TransitionTo(RESUMING_INTERNAL, DONT_UPDATE_OBSERVERS);
1725 DownloadItem::DownloadState DownloadItemImpl::InternalToExternalState(
1726 DownloadInternalState internal_state) {
1727 switch (internal_state) {
1728 case IN_PROGRESS_INTERNAL:
1730 case COMPLETING_INTERNAL:
1732 case COMPLETE_INTERNAL:
1734 case CANCELLED_INTERNAL:
1736 case INTERRUPTED_INTERNAL:
1738 case RESUMING_INTERNAL:
1740 case MAX_DOWNLOAD_INTERNAL_STATE:
1744 return MAX_DOWNLOAD_STATE;
1748 DownloadItemImpl::DownloadInternalState
1749 DownloadItemImpl::ExternalToInternalState(
1750 DownloadState external_state) {
1751 switch (external_state) {
1753 return IN_PROGRESS_INTERNAL;
1755 return COMPLETE_INTERNAL;
1757 return CANCELLED_INTERNAL;
1759 return INTERRUPTED_INTERNAL;
1763 return MAX_DOWNLOAD_INTERNAL_STATE;
1766 const char* DownloadItemImpl::DebugDownloadStateString(
1767 DownloadInternalState state) {
1769 case IN_PROGRESS_INTERNAL:
1770 return "IN_PROGRESS";
1771 case COMPLETING_INTERNAL:
1772 return "COMPLETING";
1773 case COMPLETE_INTERNAL:
1775 case CANCELLED_INTERNAL:
1777 case INTERRUPTED_INTERNAL:
1778 return "INTERRUPTED";
1779 case RESUMING_INTERNAL:
1781 case MAX_DOWNLOAD_INTERNAL_STATE:
1784 NOTREACHED() << "Unknown download state " << state;
1788 const char* DownloadItemImpl::DebugResumeModeString(ResumeMode mode) {
1790 case RESUME_MODE_INVALID:
1792 case RESUME_MODE_IMMEDIATE_CONTINUE:
1793 return "IMMEDIATE_CONTINUE";
1794 case RESUME_MODE_IMMEDIATE_RESTART:
1795 return "IMMEDIATE_RESTART";
1796 case RESUME_MODE_USER_CONTINUE:
1797 return "USER_CONTINUE";
1798 case RESUME_MODE_USER_RESTART:
1799 return "USER_RESTART";
1801 NOTREACHED() << "Unknown resume mode " << mode;
1805 } // namespace content