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 base::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 std::string
& mime_type
,
117 const std::string
& original_mime_type
,
118 const base::Time
& start_time
,
119 const base::Time
& end_time
,
120 const std::string
& etag
,
121 const std::string
& last_modified
,
122 int64 received_bytes
,
124 DownloadItem::DownloadState state
,
125 DownloadDangerType danger_type
,
126 DownloadInterruptReason interrupt_reason
,
128 const net::BoundNetLog
& bound_net_log
)
129 : is_save_package_download_(false),
130 download_id_(download_id
),
131 current_path_(current_path
),
132 target_path_(target_path
),
133 target_disposition_(TARGET_DISPOSITION_OVERWRITE
),
134 url_chain_(url_chain
),
135 referrer_url_(referrer_url
),
136 transition_type_(PAGE_TRANSITION_LINK
),
137 has_user_gesture_(false),
138 mime_type_(mime_type
),
139 original_mime_type_(original_mime_type
),
140 total_bytes_(total_bytes
),
141 received_bytes_(received_bytes
),
143 last_modified_time_(last_modified
),
145 last_reason_(interrupt_reason
),
146 start_tick_(base::TimeTicks()),
147 state_(ExternalToInternalState(state
)),
148 danger_type_(danger_type
),
149 start_time_(start_time
),
153 auto_resume_count_(0),
154 open_when_complete_(false),
155 file_externally_removed_(false),
157 is_temporary_(false),
158 all_data_saved_(state
== COMPLETE
),
159 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE
),
161 delegate_delayed_complete_(false),
162 bound_net_log_(bound_net_log
),
163 weak_ptr_factory_(this) {
165 DCHECK_NE(IN_PROGRESS_INTERNAL
, state_
);
166 Init(false /* not actively downloading */, SRC_HISTORY_IMPORT
);
169 // Constructing for a regular download:
170 DownloadItemImpl::DownloadItemImpl(
171 DownloadItemImplDelegate
* delegate
,
173 const DownloadCreateInfo
& info
,
174 const net::BoundNetLog
& bound_net_log
)
175 : is_save_package_download_(false),
176 download_id_(download_id
),
178 (info
.save_info
->prompt_for_save_location
) ?
179 TARGET_DISPOSITION_PROMPT
: TARGET_DISPOSITION_OVERWRITE
),
180 url_chain_(info
.url_chain
),
181 referrer_url_(info
.referrer_url
),
182 tab_url_(info
.tab_url
),
183 tab_referrer_url_(info
.tab_referrer_url
),
184 suggested_filename_(base::UTF16ToUTF8(info
.save_info
->suggested_name
)),
185 forced_file_path_(info
.save_info
->file_path
),
186 transition_type_(info
.transition_type
),
187 has_user_gesture_(info
.has_user_gesture
),
188 content_disposition_(info
.content_disposition
),
189 mime_type_(info
.mime_type
),
190 original_mime_type_(info
.original_mime_type
),
191 remote_address_(info
.remote_address
),
192 total_bytes_(info
.total_bytes
),
195 last_modified_time_(info
.last_modified
),
197 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE
),
198 start_tick_(base::TimeTicks::Now()),
199 state_(IN_PROGRESS_INTERNAL
),
200 danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
),
201 start_time_(info
.start_time
),
204 auto_resume_count_(0),
205 open_when_complete_(false),
206 file_externally_removed_(false),
208 is_temporary_(!info
.save_info
->file_path
.empty()),
209 all_data_saved_(false),
210 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE
),
212 delegate_delayed_complete_(false),
213 bound_net_log_(bound_net_log
),
214 weak_ptr_factory_(this) {
216 Init(true /* actively downloading */, SRC_ACTIVE_DOWNLOAD
);
218 // Link the event sources.
219 bound_net_log_
.AddEvent(
220 net::NetLog::TYPE_DOWNLOAD_URL_REQUEST
,
221 info
.request_bound_net_log
.source().ToEventParametersCallback());
223 info
.request_bound_net_log
.AddEvent(
224 net::NetLog::TYPE_DOWNLOAD_STARTED
,
225 bound_net_log_
.source().ToEventParametersCallback());
228 // Constructing for the "Save Page As..." feature:
229 DownloadItemImpl::DownloadItemImpl(
230 DownloadItemImplDelegate
* delegate
,
232 const base::FilePath
& path
,
234 const std::string
& mime_type
,
235 scoped_ptr
<DownloadRequestHandleInterface
> request_handle
,
236 const net::BoundNetLog
& bound_net_log
)
237 : is_save_package_download_(true),
238 request_handle_(request_handle
.Pass()),
239 download_id_(download_id
),
242 target_disposition_(TARGET_DISPOSITION_OVERWRITE
),
244 referrer_url_(GURL()),
245 transition_type_(PAGE_TRANSITION_LINK
),
246 has_user_gesture_(false),
247 mime_type_(mime_type
),
248 original_mime_type_(mime_type
),
252 last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE
),
253 start_tick_(base::TimeTicks::Now()),
254 state_(IN_PROGRESS_INTERNAL
),
255 danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
),
256 start_time_(base::Time::Now()),
259 auto_resume_count_(0),
260 open_when_complete_(false),
261 file_externally_removed_(false),
263 is_temporary_(false),
264 all_data_saved_(false),
265 destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE
),
267 delegate_delayed_complete_(false),
268 bound_net_log_(bound_net_log
),
269 weak_ptr_factory_(this) {
271 Init(true /* actively downloading */, SRC_SAVE_PAGE_AS
);
274 DownloadItemImpl::~DownloadItemImpl() {
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
277 // Should always have been nuked before now, at worst in
278 // DownloadManager shutdown.
279 DCHECK(!download_file_
.get());
281 FOR_EACH_OBSERVER(Observer
, observers_
, OnDownloadDestroyed(this));
282 delegate_
->AssertStateConsistent(this);
286 void DownloadItemImpl::AddObserver(Observer
* observer
) {
287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
289 observers_
.AddObserver(observer
);
292 void DownloadItemImpl::RemoveObserver(Observer
* observer
) {
293 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
295 observers_
.RemoveObserver(observer
);
298 void DownloadItemImpl::UpdateObservers() {
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
301 FOR_EACH_OBSERVER(Observer
, observers_
, OnDownloadUpdated(this));
304 void DownloadItemImpl::ValidateDangerousDownload() {
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
307 DCHECK(IsDangerous());
309 VLOG(20) << __FUNCTION__
<< " download=" << DebugString(true);
311 if (IsDone() || !IsDangerous())
314 RecordDangerousDownloadAccept(GetDangerType(),
315 GetTargetFilePath());
317 danger_type_
= DOWNLOAD_DANGER_TYPE_USER_VALIDATED
;
319 bound_net_log_
.AddEvent(
320 net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED
,
321 base::Bind(&ItemCheckedNetLogCallback
, GetDangerType()));
325 MaybeCompleteDownload();
328 void DownloadItemImpl::StealDangerousDownload(
329 const AcquireFileCallback
& callback
) {
330 VLOG(20) << __FUNCTION__
<< "() download = " << DebugString(true);
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
332 DCHECK(IsDangerous());
333 if (download_file_
) {
334 BrowserThread::PostTaskAndReplyWithResult(
337 base::Bind(&DownloadFileDetach
, base::Passed(&download_file_
)),
340 callback
.Run(current_path_
);
342 current_path_
.clear();
344 // We have now been deleted.
347 void DownloadItemImpl::Pause() {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
350 // Ignore irrelevant states.
351 if (state_
!= IN_PROGRESS_INTERNAL
|| is_paused_
)
354 request_handle_
->PauseRequest();
359 void DownloadItemImpl::Resume() {
360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
362 case IN_PROGRESS_INTERNAL
:
365 request_handle_
->ResumeRequest();
370 case COMPLETING_INTERNAL
:
371 case COMPLETE_INTERNAL
:
372 case CANCELLED_INTERNAL
:
373 case RESUMING_INTERNAL
:
376 case INTERRUPTED_INTERNAL
:
377 auto_resume_count_
= 0; // User input resets the counter.
378 ResumeInterruptedDownload();
381 case MAX_DOWNLOAD_INTERNAL_STATE
:
386 void DownloadItemImpl::Cancel(bool user_cancel
) {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
389 VLOG(20) << __FUNCTION__
<< "() download = " << DebugString(true);
390 if (state_
!= IN_PROGRESS_INTERNAL
&&
391 state_
!= INTERRUPTED_INTERNAL
&&
392 state_
!= RESUMING_INTERNAL
) {
393 // Small downloads might be complete before this method has a chance to run.
398 RecordDangerousDownloadDiscard(
399 user_cancel
? DOWNLOAD_DISCARD_DUE_TO_USER_ACTION
400 : DOWNLOAD_DISCARD_DUE_TO_SHUTDOWN
,
402 GetTargetFilePath());
405 last_reason_
= user_cancel
? DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
406 : DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN
;
408 RecordDownloadCount(CANCELLED_COUNT
);
410 // TODO(rdsmith/benjhayden): Remove condition as part of
411 // |SavePackage| integration.
412 // |download_file_| can be NULL if Interrupt() is called after the
413 // download file has been released.
414 if (!is_save_package_download_
&& download_file_
)
415 ReleaseDownloadFile(true);
417 if (state_
== IN_PROGRESS_INTERNAL
) {
418 // Cancel the originating URL request unless it's already been cancelled
420 request_handle_
->CancelRequest();
423 // Remove the intermediate file if we are cancelling an interrupted download.
424 // Continuable interruptions leave the intermediate file around.
425 if ((state_
== INTERRUPTED_INTERNAL
|| state_
== RESUMING_INTERNAL
) &&
426 !current_path_
.empty()) {
427 BrowserThread::PostTask(
428 BrowserThread::FILE, FROM_HERE
,
429 base::Bind(base::IgnoreResult(&DeleteDownloadedFile
), current_path_
));
430 current_path_
.clear();
433 TransitionTo(CANCELLED_INTERNAL
, UPDATE_OBSERVERS
);
436 void DownloadItemImpl::Remove() {
437 VLOG(20) << __FUNCTION__
<< "() download = " << DebugString(true);
438 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
440 delegate_
->AssertStateConsistent(this);
442 delegate_
->AssertStateConsistent(this);
445 delegate_
->DownloadRemoved(this);
446 // We have now been deleted.
449 void DownloadItemImpl::OpenDownload() {
450 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
453 // We don't honor the open_when_complete_ flag for temporary
454 // downloads. Don't set it because it shows up in the UI.
456 open_when_complete_
= !open_when_complete_
;
460 if (state_
!= COMPLETE_INTERNAL
|| file_externally_removed_
)
463 // Ideally, we want to detect errors in opening and report them, but we
464 // don't generally have the proper interface for that to the external
465 // program that opens the file. So instead we spawn a check to update
466 // the UI if the file has been deleted in parallel with the open.
467 delegate_
->CheckForFileRemoval(this);
468 RecordOpen(GetEndTime(), !GetOpened());
470 FOR_EACH_OBSERVER(Observer
, observers_
, OnDownloadOpened(this));
471 delegate_
->OpenDownload(this);
474 void DownloadItemImpl::ShowDownloadInShell() {
475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
477 delegate_
->ShowDownloadInShell(this);
480 uint32
DownloadItemImpl::GetId() const {
484 DownloadItem::DownloadState
DownloadItemImpl::GetState() const {
485 return InternalToExternalState(state_
);
488 DownloadInterruptReason
DownloadItemImpl::GetLastReason() const {
492 bool DownloadItemImpl::IsPaused() const {
496 bool DownloadItemImpl::IsTemporary() const {
497 return is_temporary_
;
500 bool DownloadItemImpl::CanResume() const {
501 if ((GetState() == IN_PROGRESS
) && IsPaused())
504 if (state_
!= INTERRUPTED_INTERNAL
)
507 // Downloads that don't have a WebContents should still be resumable, but this
508 // isn't currently the case. See ResumeInterruptedDownload().
509 if (!GetWebContents())
512 ResumeMode resume_mode
= GetResumeMode();
513 return IsDownloadResumptionEnabled() &&
514 (resume_mode
== RESUME_MODE_USER_RESTART
||
515 resume_mode
== RESUME_MODE_USER_CONTINUE
);
518 bool DownloadItemImpl::IsDone() const {
520 case IN_PROGRESS_INTERNAL
:
521 case COMPLETING_INTERNAL
:
524 case COMPLETE_INTERNAL
:
525 case CANCELLED_INTERNAL
:
528 case INTERRUPTED_INTERNAL
:
531 case RESUMING_INTERNAL
:
534 case MAX_DOWNLOAD_INTERNAL_STATE
:
541 const GURL
& DownloadItemImpl::GetURL() const {
542 return url_chain_
.empty() ? GURL::EmptyGURL() : url_chain_
.back();
545 const std::vector
<GURL
>& DownloadItemImpl::GetUrlChain() const {
549 const GURL
& DownloadItemImpl::GetOriginalUrl() const {
550 // Be careful about taking the front() of possibly-empty vectors!
551 // http://crbug.com/190096
552 return url_chain_
.empty() ? GURL::EmptyGURL() : url_chain_
.front();
555 const GURL
& DownloadItemImpl::GetReferrerUrl() const {
556 return referrer_url_
;
559 const GURL
& DownloadItemImpl::GetTabUrl() const {
563 const GURL
& DownloadItemImpl::GetTabReferrerUrl() const {
564 return tab_referrer_url_
;
567 std::string
DownloadItemImpl::GetSuggestedFilename() const {
568 return suggested_filename_
;
571 std::string
DownloadItemImpl::GetContentDisposition() const {
572 return content_disposition_
;
575 std::string
DownloadItemImpl::GetMimeType() const {
579 std::string
DownloadItemImpl::GetOriginalMimeType() const {
580 return original_mime_type_
;
583 std::string
DownloadItemImpl::GetRemoteAddress() const {
584 return remote_address_
;
587 bool DownloadItemImpl::HasUserGesture() const {
588 return has_user_gesture_
;
591 PageTransition
DownloadItemImpl::GetTransitionType() const {
592 return transition_type_
;
595 const std::string
& DownloadItemImpl::GetLastModifiedTime() const {
596 return last_modified_time_
;
599 const std::string
& DownloadItemImpl::GetETag() const {
603 bool DownloadItemImpl::IsSavePackageDownload() const {
604 return is_save_package_download_
;
607 const base::FilePath
& DownloadItemImpl::GetFullPath() const {
608 return current_path_
;
611 const base::FilePath
& DownloadItemImpl::GetTargetFilePath() const {
615 const base::FilePath
& DownloadItemImpl::GetForcedFilePath() const {
616 // TODO(asanka): Get rid of GetForcedFilePath(). We should instead just
617 // require that clients respect GetTargetFilePath() if it is already set.
618 return forced_file_path_
;
621 base::FilePath
DownloadItemImpl::GetFileNameToReportUser() const {
622 if (!display_name_
.empty())
623 return display_name_
;
624 return target_path_
.BaseName();
627 DownloadItem::TargetDisposition
DownloadItemImpl::GetTargetDisposition() const {
628 return target_disposition_
;
631 const std::string
& DownloadItemImpl::GetHash() const {
635 const std::string
& DownloadItemImpl::GetHashState() const {
639 bool DownloadItemImpl::GetFileExternallyRemoved() const {
640 return file_externally_removed_
;
643 void DownloadItemImpl::DeleteFile(const base::Callback
<void(bool)>& callback
) {
644 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
645 if (GetState() != DownloadItem::COMPLETE
) {
646 // Pass a null WeakPtr so it doesn't call OnDownloadedFileRemoved.
647 BrowserThread::PostTask(
648 BrowserThread::UI
, FROM_HERE
,
649 base::Bind(&DeleteDownloadedFileDone
,
650 base::WeakPtr
<DownloadItemImpl
>(), callback
, false));
653 if (current_path_
.empty() || file_externally_removed_
) {
654 // Pass a null WeakPtr so it doesn't call OnDownloadedFileRemoved.
655 BrowserThread::PostTask(
656 BrowserThread::UI
, FROM_HERE
,
657 base::Bind(&DeleteDownloadedFileDone
,
658 base::WeakPtr
<DownloadItemImpl
>(), callback
, true));
661 BrowserThread::PostTaskAndReplyWithResult(
662 BrowserThread::FILE, FROM_HERE
,
663 base::Bind(&DeleteDownloadedFile
, current_path_
),
664 base::Bind(&DeleteDownloadedFileDone
,
665 weak_ptr_factory_
.GetWeakPtr(), callback
));
668 bool DownloadItemImpl::IsDangerous() const {
670 // TODO(noelutz): At this point only the windows views UI supports
671 // warnings based on dangerous content.
672 return (danger_type_
== DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
||
673 danger_type_
== DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
||
674 danger_type_
== DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
||
675 danger_type_
== DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
||
676 danger_type_
== DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
||
677 danger_type_
== DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED
);
679 return (danger_type_
== DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
||
680 danger_type_
== DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
);
684 DownloadDangerType
DownloadItemImpl::GetDangerType() const {
688 bool DownloadItemImpl::TimeRemaining(base::TimeDelta
* remaining
) const {
689 if (total_bytes_
<= 0)
690 return false; // We never received the content_length for this download.
692 int64 speed
= CurrentSpeed();
696 *remaining
= base::TimeDelta::FromSeconds(
697 (total_bytes_
- received_bytes_
) / speed
);
701 int64
DownloadItemImpl::CurrentSpeed() const {
704 return bytes_per_sec_
;
707 int DownloadItemImpl::PercentComplete() const {
708 // If the delegate is delaying completion of the download, then we have no
709 // idea how long it will take.
710 if (delegate_delayed_complete_
|| total_bytes_
<= 0)
713 return static_cast<int>(received_bytes_
* 100.0 / total_bytes_
);
716 bool DownloadItemImpl::AllDataSaved() const {
717 return all_data_saved_
;
720 int64
DownloadItemImpl::GetTotalBytes() const {
724 int64
DownloadItemImpl::GetReceivedBytes() const {
725 return received_bytes_
;
728 base::Time
DownloadItemImpl::GetStartTime() const {
732 base::Time
DownloadItemImpl::GetEndTime() const {
736 bool DownloadItemImpl::CanShowInFolder() {
737 // A download can be shown in the folder if the downloaded file is in a known
739 return CanOpenDownload() && !GetFullPath().empty();
742 bool DownloadItemImpl::CanOpenDownload() {
743 // We can open the file or mark it for opening on completion if the download
744 // is expected to complete successfully. Exclude temporary downloads, since
745 // they aren't owned by the download system.
746 const bool is_complete
= GetState() == DownloadItem::COMPLETE
;
747 return (!IsDone() || is_complete
) && !IsTemporary() &&
748 !file_externally_removed_
;
751 bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() {
752 return delegate_
->ShouldOpenFileBasedOnExtension(GetTargetFilePath());
755 bool DownloadItemImpl::GetOpenWhenComplete() const {
756 return open_when_complete_
;
759 bool DownloadItemImpl::GetAutoOpened() {
763 bool DownloadItemImpl::GetOpened() const {
767 BrowserContext
* DownloadItemImpl::GetBrowserContext() const {
768 return delegate_
->GetBrowserContext();
771 WebContents
* DownloadItemImpl::GetWebContents() const {
772 // TODO(rdsmith): Remove null check after removing GetWebContents() from
773 // paths that might be used by DownloadItems created from history import.
774 // Currently such items have null request_handle_s, where other items
775 // (regular and SavePackage downloads) have actual objects off the pointer.
777 return request_handle_
->GetWebContents();
781 void DownloadItemImpl::OnContentCheckCompleted(DownloadDangerType danger_type
) {
782 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
783 DCHECK(AllDataSaved());
784 VLOG(20) << __FUNCTION__
<< " danger_type=" << danger_type
785 << " download=" << DebugString(true);
786 SetDangerType(danger_type
);
790 void DownloadItemImpl::SetOpenWhenComplete(bool open
) {
791 open_when_complete_
= open
;
794 void DownloadItemImpl::SetIsTemporary(bool temporary
) {
795 is_temporary_
= temporary
;
798 void DownloadItemImpl::SetOpened(bool opened
) {
802 void DownloadItemImpl::SetDisplayName(const base::FilePath
& name
) {
803 display_name_
= name
;
806 std::string
DownloadItemImpl::DebugString(bool verbose
) const {
807 std::string description
=
808 base::StringPrintf("{ id = %d"
811 DebugDownloadStateString(state_
));
813 // Construct a string of the URL chain.
814 std::string
url_list("<none>");
815 if (!url_chain_
.empty()) {
816 std::vector
<GURL
>::const_iterator iter
= url_chain_
.begin();
817 std::vector
<GURL
>::const_iterator last
= url_chain_
.end();
818 url_list
= (*iter
).is_valid() ? (*iter
).spec() : "<invalid>";
820 for ( ; verbose
&& (iter
!= last
); ++iter
) {
821 url_list
+= " ->\n\t";
822 const GURL
& next_url
= *iter
;
823 url_list
+= next_url
.is_valid() ? next_url
.spec() : "<invalid>";
828 description
+= base::StringPrintf(
830 " received = %" PRId64
834 " auto_resume_count = %d"
836 " all_data_saved = %c"
837 " last_modified = '%s'"
839 " has_download_file = %s"
840 " url_chain = \n\t\"%s\"\n\t"
841 " full_path = \"%" PRFilePath
"\"\n\t"
842 " target_path = \"%" PRFilePath
"\"",
845 DownloadInterruptReasonToString(last_reason_
).c_str(),
846 IsPaused() ? 'T' : 'F',
847 DebugResumeModeString(GetResumeMode()),
850 AllDataSaved() ? 'T' : 'F',
851 GetLastModifiedTime().c_str(),
853 download_file_
.get() ? "true" : "false",
855 GetFullPath().value().c_str(),
856 GetTargetFilePath().value().c_str());
858 description
+= base::StringPrintf(" url = \"%s\"", url_list
.c_str());
866 DownloadItemImpl::ResumeMode
DownloadItemImpl::GetResumeMode() const {
867 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
868 // We can't continue without a handle on the intermediate file.
869 // We also can't continue if we don't have some verifier to make sure
870 // we're getting the same file.
871 const bool force_restart
=
872 (current_path_
.empty() || (etag_
.empty() && last_modified_time_
.empty()));
874 // We won't auto-restart if we've used up our attempts or the
875 // download has been paused by user action.
876 const bool force_user
=
877 (auto_resume_count_
>= kMaxAutoResumeAttempts
|| is_paused_
);
879 ResumeMode mode
= RESUME_MODE_INVALID
;
881 switch(last_reason_
) {
882 case DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR
:
883 case DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT
:
884 if (force_restart
&& force_user
)
885 mode
= RESUME_MODE_USER_RESTART
;
886 else if (force_restart
)
887 mode
= RESUME_MODE_IMMEDIATE_RESTART
;
889 mode
= RESUME_MODE_USER_CONTINUE
;
891 mode
= RESUME_MODE_IMMEDIATE_CONTINUE
;
894 case DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION
:
895 case DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE
:
896 case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT
:
898 mode
= RESUME_MODE_USER_RESTART
;
900 mode
= RESUME_MODE_IMMEDIATE_RESTART
;
903 case DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED
:
904 case DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
:
905 case DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN
:
906 case DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST
:
907 case DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED
:
908 case DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN
:
909 case DOWNLOAD_INTERRUPT_REASON_CRASH
:
911 mode
= RESUME_MODE_USER_RESTART
;
913 mode
= RESUME_MODE_USER_CONTINUE
;
916 case DOWNLOAD_INTERRUPT_REASON_FILE_FAILED
:
917 case DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
:
918 case DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE
:
919 case DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG
:
920 case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE
:
921 mode
= RESUME_MODE_USER_RESTART
;
924 case DOWNLOAD_INTERRUPT_REASON_NONE
:
925 case DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED
:
926 case DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT
:
927 case DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
:
928 case DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED
:
929 case DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED
:
930 case DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED
:
931 case DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM
:
932 mode
= RESUME_MODE_INVALID
;
939 void DownloadItemImpl::MergeOriginInfoOnResume(
940 const DownloadCreateInfo
& new_create_info
) {
941 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
942 DCHECK_EQ(RESUMING_INTERNAL
, state_
);
943 DCHECK(!new_create_info
.url_chain
.empty());
945 // We are going to tack on any new redirects to our list of redirects.
946 // When a download is resumed, the URL used for the resumption request is the
947 // one at the end of the previous redirect chain. Tacking additional redirects
948 // to the end of this chain ensures that:
949 // - If the download needs to be resumed again, the ETag/Last-Modified headers
950 // will be used with the last server that sent them to us.
951 // - The redirect chain contains all the servers that were involved in this
952 // download since the initial request, in order.
953 std::vector
<GURL
>::const_iterator chain_iter
=
954 new_create_info
.url_chain
.begin();
955 if (*chain_iter
== url_chain_
.back())
958 // Record some stats. If the precondition failed (the server returned
959 // HTTP_PRECONDITION_FAILED), then the download will automatically retried as
960 // a full request rather than a partial. Full restarts clobber validators.
961 int origin_state
= 0;
962 if (chain_iter
!= new_create_info
.url_chain
.end())
963 origin_state
|= ORIGIN_STATE_ON_RESUMPTION_ADDITIONAL_REDIRECTS
;
964 if (etag_
!= new_create_info
.etag
||
965 last_modified_time_
!= new_create_info
.last_modified
)
966 origin_state
|= ORIGIN_STATE_ON_RESUMPTION_VALIDATORS_CHANGED
;
967 if (content_disposition_
!= new_create_info
.content_disposition
)
968 origin_state
|= ORIGIN_STATE_ON_RESUMPTION_CONTENT_DISPOSITION_CHANGED
;
969 RecordOriginStateOnResumption(new_create_info
.save_info
->offset
!= 0,
973 url_chain_
.end(), chain_iter
, new_create_info
.url_chain
.end());
974 etag_
= new_create_info
.etag
;
975 last_modified_time_
= new_create_info
.last_modified
;
976 content_disposition_
= new_create_info
.content_disposition
;
978 // Don't update observers. This method is expected to be called just before a
979 // DownloadFile is created and Start() is called. The observers will be
980 // notified when the download transitions to the IN_PROGRESS state.
983 void DownloadItemImpl::NotifyRemoved() {
984 FOR_EACH_OBSERVER(Observer
, observers_
, OnDownloadRemoved(this));
987 void DownloadItemImpl::OnDownloadedFileRemoved() {
988 file_externally_removed_
= true;
989 VLOG(20) << __FUNCTION__
<< " download=" << DebugString(true);
993 base::WeakPtr
<DownloadDestinationObserver
>
994 DownloadItemImpl::DestinationObserverAsWeakPtr() {
995 return weak_ptr_factory_
.GetWeakPtr();
998 const net::BoundNetLog
& DownloadItemImpl::GetBoundNetLog() const {
999 return bound_net_log_
;
1002 void DownloadItemImpl::SetTotalBytes(int64 total_bytes
) {
1003 total_bytes_
= total_bytes
;
1006 void DownloadItemImpl::OnAllDataSaved(const std::string
& final_hash
) {
1007 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1009 DCHECK_EQ(IN_PROGRESS_INTERNAL
, state_
);
1010 DCHECK(!all_data_saved_
);
1011 all_data_saved_
= true;
1012 VLOG(20) << __FUNCTION__
<< " download=" << DebugString(true);
1014 // Store final hash and null out intermediate serialized hash state.
1021 void DownloadItemImpl::MarkAsComplete() {
1022 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1024 DCHECK(all_data_saved_
);
1025 end_time_
= base::Time::Now();
1026 TransitionTo(COMPLETE_INTERNAL
, UPDATE_OBSERVERS
);
1029 void DownloadItemImpl::DestinationUpdate(int64 bytes_so_far
,
1030 int64 bytes_per_sec
,
1031 const std::string
& hash_state
) {
1032 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1033 VLOG(20) << __FUNCTION__
<< " so_far=" << bytes_so_far
1034 << " per_sec=" << bytes_per_sec
<< " download=" << DebugString(true);
1036 if (GetState() != IN_PROGRESS
) {
1037 // Ignore if we're no longer in-progress. This can happen if we race a
1038 // Cancel on the UI thread with an update on the FILE thread.
1040 // TODO(rdsmith): Arguably we should let this go through, as this means
1041 // the download really did get further than we know before it was
1042 // cancelled. But the gain isn't very large, and the code is more
1043 // fragile if it has to support in progress updates in a non-in-progress
1044 // state. This issue should be readdressed when we revamp performance
1048 bytes_per_sec_
= bytes_per_sec
;
1049 hash_state_
= hash_state
;
1050 received_bytes_
= bytes_so_far
;
1052 // If we've received more data than we were expecting (bad server info?),
1053 // revert to 'unknown size mode'.
1054 if (received_bytes_
> total_bytes_
)
1057 if (bound_net_log_
.IsLogging()) {
1058 bound_net_log_
.AddEvent(
1059 net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED
,
1060 net::NetLog::Int64Callback("bytes_so_far", received_bytes_
));
1066 void DownloadItemImpl::DestinationError(DownloadInterruptReason reason
) {
1067 // Postpone recognition of this error until after file name determination
1068 // has completed and the intermediate file has been renamed to simplify
1069 // resumption conditions.
1070 if (current_path_
.empty() || target_path_
.empty())
1071 destination_error_
= reason
;
1076 void DownloadItemImpl::DestinationCompleted(const std::string
& final_hash
) {
1077 VLOG(20) << __FUNCTION__
<< " download=" << DebugString(true);
1078 if (GetState() != IN_PROGRESS
)
1080 OnAllDataSaved(final_hash
);
1081 MaybeCompleteDownload();
1084 // **** Download progression cascade
1086 void DownloadItemImpl::Init(bool active
,
1087 DownloadType download_type
) {
1088 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1091 RecordDownloadCount(START_COUNT
);
1093 std::string file_name
;
1094 if (download_type
== SRC_HISTORY_IMPORT
) {
1095 // target_path_ works for History and Save As versions.
1096 file_name
= target_path_
.AsUTF8Unsafe();
1098 // See if it's set programmatically.
1099 file_name
= forced_file_path_
.AsUTF8Unsafe();
1100 // Possibly has a 'download' attribute for the anchor.
1101 if (file_name
.empty())
1102 file_name
= suggested_filename_
;
1103 // From the URL file name.
1104 if (file_name
.empty())
1105 file_name
= GetURL().ExtractFileName();
1108 base::Callback
<base::Value
*(net::NetLog::LogLevel
)> active_data
= base::Bind(
1109 &ItemActivatedNetLogCallback
, this, download_type
, &file_name
);
1111 bound_net_log_
.BeginEvent(
1112 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE
, active_data
);
1114 bound_net_log_
.AddEvent(
1115 net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE
, active_data
);
1118 VLOG(20) << __FUNCTION__
<< "() " << DebugString(true);
1121 // We're starting the download.
1122 void DownloadItemImpl::Start(
1123 scoped_ptr
<DownloadFile
> file
,
1124 scoped_ptr
<DownloadRequestHandleInterface
> req_handle
) {
1125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1126 DCHECK(!download_file_
.get());
1128 DCHECK(req_handle
.get());
1130 download_file_
= file
.Pass();
1131 request_handle_
= req_handle
.Pass();
1133 if (GetState() == CANCELLED
) {
1134 // The download was in the process of resuming when it was cancelled. Don't
1136 ReleaseDownloadFile(true);
1137 request_handle_
->CancelRequest();
1141 TransitionTo(IN_PROGRESS_INTERNAL
, UPDATE_OBSERVERS
);
1143 BrowserThread::PostTask(
1144 BrowserThread::FILE, FROM_HERE
,
1145 base::Bind(&DownloadFile::Initialize
,
1146 // Safe because we control download file lifetime.
1147 base::Unretained(download_file_
.get()),
1148 base::Bind(&DownloadItemImpl::OnDownloadFileInitialized
,
1149 weak_ptr_factory_
.GetWeakPtr())));
1152 void DownloadItemImpl::OnDownloadFileInitialized(
1153 DownloadInterruptReason result
) {
1154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1155 if (result
!= DOWNLOAD_INTERRUPT_REASON_NONE
) {
1157 // TODO(rdsmith/asanka): Arguably we should show this in the UI, but
1158 // it's not at all clear what to show--we haven't done filename
1159 // determination, so we don't know what name to display. OTOH,
1160 // the failure mode of not showing the DI if the file initialization
1161 // fails isn't a good one. Can we hack up a name based on the
1162 // URLRequest? We'll need to make sure that initialization happens
1163 // properly. Possibly the right thing is to have the UI handle
1164 // this case specially.
1168 delegate_
->DetermineDownloadTarget(
1169 this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined
,
1170 weak_ptr_factory_
.GetWeakPtr()));
1173 // Called by delegate_ when the download target path has been
1175 void DownloadItemImpl::OnDownloadTargetDetermined(
1176 const base::FilePath
& target_path
,
1177 TargetDisposition disposition
,
1178 DownloadDangerType danger_type
,
1179 const base::FilePath
& intermediate_path
) {
1180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1182 // If the |target_path| is empty, then we consider this download to be
1184 if (target_path
.empty()) {
1189 // TODO(rdsmith,asanka): We are ignoring the possibility that the download
1190 // has been interrupted at this point until we finish the intermediate
1191 // rename and set the full path. That's dangerous, because we might race
1192 // with resumption, either manual (because the interrupt is visible to the
1193 // UI) or automatic. If we keep the "ignore an error on download until file
1194 // name determination complete" semantics, we need to make sure that the
1195 // error is kept completely invisible until that point.
1197 VLOG(20) << __FUNCTION__
<< " " << target_path
.value() << " " << disposition
1198 << " " << danger_type
<< " " << DebugString(true);
1200 target_path_
= target_path
;
1201 target_disposition_
= disposition
;
1202 SetDangerType(danger_type
);
1204 // We want the intermediate and target paths to refer to the same directory so
1205 // that they are both on the same device and subject to same
1206 // space/permission/availability constraints.
1207 DCHECK(intermediate_path
.DirName() == target_path
.DirName());
1209 // During resumption, we may choose to proceed with the same intermediate
1210 // file. No rename is necessary if our intermediate file already has the
1213 // The intermediate name may change from its original value during filename
1214 // determination on resumption, for example if the reason for the interruption
1215 // was the download target running out space, resulting in a user prompt.
1216 if (intermediate_path
== current_path_
) {
1217 OnDownloadRenamedToIntermediateName(DOWNLOAD_INTERRUPT_REASON_NONE
,
1222 // Rename to intermediate name.
1223 // TODO(asanka): Skip this rename if AllDataSaved() is true. This avoids a
1224 // spurious rename when we can just rename to the final
1225 // filename. Unnecessary renames may cause bugs like
1226 // http://crbug.com/74187.
1227 DCHECK(!is_save_package_download_
);
1228 DCHECK(download_file_
.get());
1229 DownloadFile::RenameCompletionCallback callback
=
1230 base::Bind(&DownloadItemImpl::OnDownloadRenamedToIntermediateName
,
1231 weak_ptr_factory_
.GetWeakPtr());
1232 BrowserThread::PostTask(
1233 BrowserThread::FILE, FROM_HERE
,
1234 base::Bind(&DownloadFile::RenameAndUniquify
,
1235 // Safe because we control download file lifetime.
1236 base::Unretained(download_file_
.get()),
1237 intermediate_path
, callback
));
1240 void DownloadItemImpl::OnDownloadRenamedToIntermediateName(
1241 DownloadInterruptReason reason
,
1242 const base::FilePath
& full_path
) {
1243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1244 VLOG(20) << __FUNCTION__
<< " download=" << DebugString(true);
1246 if (DOWNLOAD_INTERRUPT_REASON_NONE
!= destination_error_
) {
1247 // Process destination error. If both |reason| and |destination_error_|
1248 // refer to actual errors, we want to use the |destination_error_| as the
1249 // argument to the Interrupt() routine, as it happened first.
1250 if (reason
== DOWNLOAD_INTERRUPT_REASON_NONE
)
1251 SetFullPath(full_path
);
1252 Interrupt(destination_error_
);
1253 destination_error_
= DOWNLOAD_INTERRUPT_REASON_NONE
;
1254 } else if (DOWNLOAD_INTERRUPT_REASON_NONE
!= reason
) {
1256 // All file errors result in file deletion above; no need to cleanup. The
1257 // current_path_ should be empty. Resuming this download will force a
1258 // restart and a re-doing of filename determination.
1259 DCHECK(current_path_
.empty());
1261 SetFullPath(full_path
);
1263 MaybeCompleteDownload();
1267 // When SavePackage downloads MHTML to GData (see
1268 // SavePackageFilePickerChromeOS), GData calls MaybeCompleteDownload() like it
1269 // does for non-SavePackage downloads, but SavePackage downloads never satisfy
1270 // IsDownloadReadyForCompletion(). GDataDownloadObserver manually calls
1271 // DownloadItem::UpdateObservers() when the upload completes so that SavePackage
1272 // notices that the upload has completed and runs its normal Finish() pathway.
1273 // MaybeCompleteDownload() is never the mechanism by which SavePackage completes
1274 // downloads. SavePackage always uses its own Finish() to mark downloads
1276 void DownloadItemImpl::MaybeCompleteDownload() {
1277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1278 DCHECK(!is_save_package_download_
);
1280 if (!IsDownloadReadyForCompletion(
1281 base::Bind(&DownloadItemImpl::MaybeCompleteDownload
,
1282 weak_ptr_factory_
.GetWeakPtr())))
1285 // TODO(rdsmith): DCHECK that we only pass through this point
1286 // once per download. The natural way to do this is by a state
1287 // transition on the DownloadItem.
1289 // Confirm we're in the proper set of states to be here;
1290 // have all data, have a history handle, (validated or safe).
1291 DCHECK_EQ(IN_PROGRESS_INTERNAL
, state_
);
1292 DCHECK(!IsDangerous());
1293 DCHECK(all_data_saved_
);
1295 OnDownloadCompleting();
1298 // Called by MaybeCompleteDownload() when it has determined that the download
1299 // is ready for completion.
1300 void DownloadItemImpl::OnDownloadCompleting() {
1301 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1303 if (state_
!= IN_PROGRESS_INTERNAL
)
1306 VLOG(20) << __FUNCTION__
<< "()"
1307 << " " << DebugString(true);
1308 DCHECK(!GetTargetFilePath().empty());
1309 DCHECK(!IsDangerous());
1311 // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration.
1312 if (is_save_package_download_
) {
1313 // Avoid doing anything on the file thread; there's nothing we control
1315 // Strictly speaking, this skips giving the embedder a chance to open
1316 // the download. But on a save package download, there's no real
1317 // concept of opening.
1322 DCHECK(download_file_
.get());
1323 // Unilaterally rename; even if it already has the right name,
1324 // we need theannotation.
1325 DownloadFile::RenameCompletionCallback callback
=
1326 base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName
,
1327 weak_ptr_factory_
.GetWeakPtr());
1328 BrowserThread::PostTask(
1329 BrowserThread::FILE, FROM_HERE
,
1330 base::Bind(&DownloadFile::RenameAndAnnotate
,
1331 base::Unretained(download_file_
.get()),
1332 GetTargetFilePath(), callback
));
1335 void DownloadItemImpl::OnDownloadRenamedToFinalName(
1336 DownloadInterruptReason reason
,
1337 const base::FilePath
& full_path
) {
1338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1339 DCHECK(!is_save_package_download_
);
1341 // If a cancel or interrupt hit, we'll cancel the DownloadFile, which
1342 // will result in deleting the file on the file thread. So we don't
1343 // care about the name having been changed.
1344 if (state_
!= IN_PROGRESS_INTERNAL
)
1347 VLOG(20) << __FUNCTION__
<< "()"
1348 << " full_path = \"" << full_path
.value() << "\""
1349 << " " << DebugString(false);
1351 if (DOWNLOAD_INTERRUPT_REASON_NONE
!= reason
) {
1354 // All file errors should have resulted in in file deletion above. On
1355 // resumption we will need to re-do filename determination.
1356 DCHECK(current_path_
.empty());
1360 DCHECK(target_path_
== full_path
);
1362 if (full_path
!= current_path_
) {
1363 // full_path is now the current and target file path.
1364 DCHECK(!full_path
.empty());
1365 SetFullPath(full_path
);
1368 // Complete the download and release the DownloadFile.
1369 DCHECK(download_file_
.get());
1370 ReleaseDownloadFile(false);
1372 // We're not completely done with the download item yet, but at this
1373 // point we're committed to complete the download. Cancels (or Interrupts,
1374 // though it's not clear how they could happen) after this point will be
1376 TransitionTo(COMPLETING_INTERNAL
, DONT_UPDATE_OBSERVERS
);
1378 if (delegate_
->ShouldOpenDownload(
1379 this, base::Bind(&DownloadItemImpl::DelayedDownloadOpened
,
1380 weak_ptr_factory_
.GetWeakPtr()))) {
1383 delegate_delayed_complete_
= true;
1388 void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened
) {
1389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1391 auto_opened_
= auto_opened
;
1395 void DownloadItemImpl::Completed() {
1396 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1398 VLOG(20) << __FUNCTION__
<< "() " << DebugString(false);
1400 DCHECK(all_data_saved_
);
1401 end_time_
= base::Time::Now();
1402 TransitionTo(COMPLETE_INTERNAL
, UPDATE_OBSERVERS
);
1403 RecordDownloadCompleted(start_tick_
, received_bytes_
);
1406 // If it was already handled by the delegate, do nothing.
1407 } else if (GetOpenWhenComplete() ||
1408 ShouldOpenFileBasedOnExtension() ||
1410 // If the download is temporary, like in drag-and-drop, do not open it but
1411 // we still need to set it auto-opened so that it can be removed from the
1416 auto_opened_
= true;
1421 void DownloadItemImpl::OnResumeRequestStarted(
1423 DownloadInterruptReason interrupt_reason
) {
1424 // If |item| is not NULL, then Start() has been called already, and nothing
1425 // more needs to be done here.
1427 DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
1428 DCHECK_EQ(static_cast<DownloadItem
*>(this), item
);
1431 // Otherwise, the request failed without passing through
1432 // DownloadResourceHandler::OnResponseStarted.
1433 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
1434 Interrupt(interrupt_reason
);
1437 // **** End of Download progression cascade
1439 // An error occurred somewhere.
1440 void DownloadItemImpl::Interrupt(DownloadInterruptReason reason
) {
1441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1442 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE
, reason
);
1444 // Somewhat counter-intuitively, it is possible for us to receive an
1445 // interrupt after we've already been interrupted. The generation of
1446 // interrupts from the file thread Renames and the generation of
1447 // interrupts from disk writes go through two different mechanisms (driven
1448 // by rename requests from UI thread and by write requests from IO thread,
1449 // respectively), and since we choose not to keep state on the File thread,
1450 // this is the place where the races collide. It's also possible for
1451 // interrupts to race with cancels.
1453 // Whatever happens, the first one to hit the UI thread wins.
1454 if (state_
!= IN_PROGRESS_INTERNAL
&& state_
!= RESUMING_INTERNAL
)
1457 last_reason_
= reason
;
1459 ResumeMode resume_mode
= GetResumeMode();
1461 if (state_
== IN_PROGRESS_INTERNAL
) {
1462 // Cancel (delete file) if:
1463 // 1) we're going to restart.
1464 // 2) Resumption isn't possible (download was cancelled or blocked due to
1465 // security restrictions).
1466 // 3) Resumption isn't enabled.
1467 // No point in leaving data around we aren't going to use.
1468 ReleaseDownloadFile(resume_mode
== RESUME_MODE_IMMEDIATE_RESTART
||
1469 resume_mode
== RESUME_MODE_USER_RESTART
||
1470 resume_mode
== RESUME_MODE_INVALID
||
1471 !IsDownloadResumptionEnabled());
1473 // Cancel the originating URL request.
1474 request_handle_
->CancelRequest();
1476 DCHECK(!download_file_
.get());
1479 // Reset all data saved, as even if we did save all the data we're going
1480 // to go through another round of downloading when we resume.
1481 // There's a potential problem here in the abstract, as if we did download
1482 // all the data and then run into a continuable error, on resumption we
1483 // won't download any more data. However, a) there are currently no
1484 // continuable errors that can occur after we download all the data, and
1485 // b) if there were, that would probably simply result in a null range
1486 // request, which would generate a DestinationCompleted() notification
1487 // from the DownloadFile, which would behave properly with setting
1488 // all_data_saved_ to false here.
1489 all_data_saved_
= false;
1491 TransitionTo(INTERRUPTED_INTERNAL
, DONT_UPDATE_OBSERVERS
);
1492 RecordDownloadInterrupted(reason
, received_bytes_
, total_bytes_
);
1493 if (!GetWebContents())
1494 RecordDownloadCount(INTERRUPTED_WITHOUT_WEBCONTENTS
);
1496 AutoResumeIfValid();
1500 void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file
) {
1501 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1504 BrowserThread::PostTask(
1505 BrowserThread::FILE, FROM_HERE
,
1506 // Will be deleted at end of task execution.
1507 base::Bind(&DownloadFileCancel
, base::Passed(&download_file_
)));
1508 // Avoid attempting to reuse the intermediate file by clearing out
1510 current_path_
.clear();
1512 BrowserThread::PostTask(
1513 BrowserThread::FILE,
1515 base::Bind(base::IgnoreResult(&DownloadFileDetach
),
1516 // Will be deleted at end of task execution.
1517 base::Passed(&download_file_
)));
1519 // Don't accept any more messages from the DownloadFile, and null
1520 // out any previous "all data received". This also breaks links to
1521 // other entities we've given out weak pointers to.
1522 weak_ptr_factory_
.InvalidateWeakPtrs();
1525 bool DownloadItemImpl::IsDownloadReadyForCompletion(
1526 const base::Closure
& state_change_notification
) {
1527 // If we don't have all the data, the download is not ready for
1529 if (!AllDataSaved())
1532 // If the download is dangerous, but not yet validated, it's not ready for
1537 // If the download isn't active (e.g. has been cancelled) it's not
1538 // ready for completion.
1539 if (state_
!= IN_PROGRESS_INTERNAL
)
1542 // If the target filename hasn't been determined, then it's not ready for
1543 // completion. This is checked in ReadyForDownloadCompletionDone().
1544 if (GetTargetFilePath().empty())
1547 // This is checked in NeedsRename(). Without this conditional,
1548 // browser_tests:DownloadTest.DownloadMimeType fails the DCHECK.
1549 if (target_path_
.DirName() != current_path_
.DirName())
1552 // Give the delegate a chance to hold up a stop sign. It'll call
1553 // use back through the passed callback if it does and that state changes.
1554 if (!delegate_
->ShouldCompleteDownload(this, state_change_notification
))
1560 void DownloadItemImpl::TransitionTo(DownloadInternalState new_state
,
1561 ShouldUpdateObservers notify_action
) {
1562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1564 if (state_
== new_state
)
1567 DownloadInternalState old_state
= state_
;
1571 case COMPLETING_INTERNAL
:
1572 bound_net_log_
.AddEvent(
1573 net::NetLog::TYPE_DOWNLOAD_ITEM_COMPLETING
,
1574 base::Bind(&ItemCompletingNetLogCallback
, received_bytes_
, &hash_
));
1576 case COMPLETE_INTERNAL
:
1577 bound_net_log_
.AddEvent(
1578 net::NetLog::TYPE_DOWNLOAD_ITEM_FINISHED
,
1579 base::Bind(&ItemFinishedNetLogCallback
, auto_opened_
));
1581 case INTERRUPTED_INTERNAL
:
1582 bound_net_log_
.AddEvent(
1583 net::NetLog::TYPE_DOWNLOAD_ITEM_INTERRUPTED
,
1584 base::Bind(&ItemInterruptedNetLogCallback
, last_reason_
,
1585 received_bytes_
, &hash_state_
));
1587 case IN_PROGRESS_INTERNAL
:
1588 if (old_state
== INTERRUPTED_INTERNAL
) {
1589 bound_net_log_
.AddEvent(
1590 net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED
,
1591 base::Bind(&ItemResumingNetLogCallback
,
1592 false, last_reason_
, received_bytes_
, &hash_state_
));
1595 case CANCELLED_INTERNAL
:
1596 bound_net_log_
.AddEvent(
1597 net::NetLog::TYPE_DOWNLOAD_ITEM_CANCELED
,
1598 base::Bind(&ItemCanceledNetLogCallback
, received_bytes_
,
1605 VLOG(20) << " " << __FUNCTION__
<< "()" << " this = " << DebugString(true)
1606 << " " << InternalToExternalState(old_state
)
1607 << " " << InternalToExternalState(state_
);
1609 bool is_done
= (state_
!= IN_PROGRESS_INTERNAL
&&
1610 state_
!= COMPLETING_INTERNAL
);
1611 bool was_done
= (old_state
!= IN_PROGRESS_INTERNAL
&&
1612 old_state
!= COMPLETING_INTERNAL
);
1614 if (is_done
&& !was_done
)
1615 bound_net_log_
.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE
);
1618 if (was_done
&& !is_done
) {
1619 std::string
file_name(target_path_
.BaseName().AsUTF8Unsafe());
1620 bound_net_log_
.BeginEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE
,
1621 base::Bind(&ItemActivatedNetLogCallback
,
1622 this, SRC_ACTIVE_DOWNLOAD
,
1626 if (notify_action
== UPDATE_OBSERVERS
)
1630 void DownloadItemImpl::SetDangerType(DownloadDangerType danger_type
) {
1631 if (danger_type
!= danger_type_
) {
1632 bound_net_log_
.AddEvent(
1633 net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED
,
1634 base::Bind(&ItemCheckedNetLogCallback
, danger_type
));
1636 // Only record the Malicious UMA stat if it's going from {not malicious} ->
1638 if ((danger_type_
== DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
||
1639 danger_type_
== DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE
||
1640 danger_type_
== DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT
||
1641 danger_type_
== DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT
) &&
1642 (danger_type
== DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST
||
1643 danger_type
== DOWNLOAD_DANGER_TYPE_DANGEROUS_URL
||
1644 danger_type
== DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT
||
1645 danger_type
== DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED
)) {
1646 RecordMaliciousDownloadClassified(danger_type
);
1648 danger_type_
= danger_type
;
1651 void DownloadItemImpl::SetFullPath(const base::FilePath
& new_path
) {
1652 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1653 VLOG(20) << __FUNCTION__
<< "()"
1654 << " new_path = \"" << new_path
.value() << "\""
1655 << " " << DebugString(true);
1656 DCHECK(!new_path
.empty());
1658 bound_net_log_
.AddEvent(
1659 net::NetLog::TYPE_DOWNLOAD_ITEM_RENAMED
,
1660 base::Bind(&ItemRenamedNetLogCallback
, ¤t_path_
, &new_path
));
1662 current_path_
= new_path
;
1665 void DownloadItemImpl::AutoResumeIfValid() {
1666 DVLOG(20) << __FUNCTION__
<< "() " << DebugString(true);
1667 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1668 ResumeMode mode
= GetResumeMode();
1670 if (mode
!= RESUME_MODE_IMMEDIATE_RESTART
&&
1671 mode
!= RESUME_MODE_IMMEDIATE_CONTINUE
) {
1675 auto_resume_count_
++;
1677 ResumeInterruptedDownload();
1680 void DownloadItemImpl::ResumeInterruptedDownload() {
1681 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1683 // If the flag for downloads resumption isn't enabled, ignore
1685 const base::CommandLine
& command_line
=
1686 *base::CommandLine::ForCurrentProcess();
1687 if (!command_line
.HasSwitch(switches::kEnableDownloadResumption
))
1690 // If we're not interrupted, ignore the request; our caller is drunk.
1691 if (state_
!= INTERRUPTED_INTERNAL
)
1694 // If we can't get a web contents, we can't resume the download.
1695 // TODO(rdsmith): Find some alternative web contents to use--this
1696 // means we can't restart a download if it's a download imported
1697 // from the history.
1698 if (!GetWebContents())
1701 // Reset the appropriate state if restarting.
1702 ResumeMode mode
= GetResumeMode();
1703 if (mode
== RESUME_MODE_IMMEDIATE_RESTART
||
1704 mode
== RESUME_MODE_USER_RESTART
) {
1705 received_bytes_
= 0;
1707 last_modified_time_
= "";
1711 scoped_ptr
<DownloadUrlParameters
> download_params(
1712 DownloadUrlParameters::FromWebContents(GetWebContents(),
1715 download_params
->set_file_path(GetFullPath());
1716 download_params
->set_offset(GetReceivedBytes());
1717 download_params
->set_hash_state(GetHashState());
1718 download_params
->set_last_modified(GetLastModifiedTime());
1719 download_params
->set_etag(GetETag());
1720 download_params
->set_callback(
1721 base::Bind(&DownloadItemImpl::OnResumeRequestStarted
,
1722 weak_ptr_factory_
.GetWeakPtr()));
1724 delegate_
->ResumeInterruptedDownload(download_params
.Pass(), GetId());
1725 // Just in case we were interrupted while paused.
1728 TransitionTo(RESUMING_INTERNAL
, DONT_UPDATE_OBSERVERS
);
1732 DownloadItem::DownloadState
DownloadItemImpl::InternalToExternalState(
1733 DownloadInternalState internal_state
) {
1734 switch (internal_state
) {
1735 case IN_PROGRESS_INTERNAL
:
1737 case COMPLETING_INTERNAL
:
1739 case COMPLETE_INTERNAL
:
1741 case CANCELLED_INTERNAL
:
1743 case INTERRUPTED_INTERNAL
:
1745 case RESUMING_INTERNAL
:
1747 case MAX_DOWNLOAD_INTERNAL_STATE
:
1751 return MAX_DOWNLOAD_STATE
;
1755 DownloadItemImpl::DownloadInternalState
1756 DownloadItemImpl::ExternalToInternalState(
1757 DownloadState external_state
) {
1758 switch (external_state
) {
1760 return IN_PROGRESS_INTERNAL
;
1762 return COMPLETE_INTERNAL
;
1764 return CANCELLED_INTERNAL
;
1766 return INTERRUPTED_INTERNAL
;
1770 return MAX_DOWNLOAD_INTERNAL_STATE
;
1773 const char* DownloadItemImpl::DebugDownloadStateString(
1774 DownloadInternalState state
) {
1776 case IN_PROGRESS_INTERNAL
:
1777 return "IN_PROGRESS";
1778 case COMPLETING_INTERNAL
:
1779 return "COMPLETING";
1780 case COMPLETE_INTERNAL
:
1782 case CANCELLED_INTERNAL
:
1784 case INTERRUPTED_INTERNAL
:
1785 return "INTERRUPTED";
1786 case RESUMING_INTERNAL
:
1788 case MAX_DOWNLOAD_INTERNAL_STATE
:
1791 NOTREACHED() << "Unknown download state " << state
;
1795 const char* DownloadItemImpl::DebugResumeModeString(ResumeMode mode
) {
1797 case RESUME_MODE_INVALID
:
1799 case RESUME_MODE_IMMEDIATE_CONTINUE
:
1800 return "IMMEDIATE_CONTINUE";
1801 case RESUME_MODE_IMMEDIATE_RESTART
:
1802 return "IMMEDIATE_RESTART";
1803 case RESUME_MODE_USER_CONTINUE
:
1804 return "USER_CONTINUE";
1805 case RESUME_MODE_USER_RESTART
:
1806 return "USER_RESTART";
1808 NOTREACHED() << "Unknown resume mode " << mode
;
1812 } // namespace content