| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/download/download_item.h" |
| |
| #include "base/basictypes.h" |
| #include "base/file_util.h" |
| #include "base/format_macros.h" |
| #include "base/i18n/case_conversion.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram.h" |
| #include "base/stringprintf.h" |
| #include "base/timer.h" |
| #include "base/utf_string_conversions.h" |
| #include "net/base/net_util.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/content_browser_client.h" |
| #include "content/browser/download/download_file.h" |
| #include "content/browser/download/download_create_info.h" |
| #include "content/browser/download/download_file_manager.h" |
| #include "content/browser/download/download_manager.h" |
| #include "content/browser/download/download_manager_delegate.h" |
| #include "content/browser/download/download_persistent_store_info.h" |
| #include "content/browser/download/download_request_handle.h" |
| #include "content/browser/download/download_stats.h" |
| |
| // A DownloadItem normally goes through the following states: |
| // * Created (when download starts) |
| // * Made visible to consumers (e.g. Javascript) after the |
| // destination file has been determined. |
| // * Entered into the history database. |
| // * Made visible in the download shelf. |
| // * All data is saved. Note that the actual data download occurs |
| // in parallel with the above steps, but until those steps are |
| // complete, completion of the data download will be ignored. |
| // * Download file is renamed to its final name, and possibly |
| // auto-opened. |
| // TODO(rdsmith): This progress should be reflected in |
| // DownloadItem::DownloadState and a state transition table/state diagram. |
| // |
| // TODO(rdsmith): This description should be updated to reflect the cancel |
| // pathways. |
| |
| namespace { |
| |
| // Update frequency (milliseconds). |
| const int kUpdateTimeMs = 1000; |
| |
| static void DeleteDownloadedFile(const FilePath& path) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| |
| // Make sure we only delete files. |
| if (!file_util::DirectoryExists(path)) |
| file_util::Delete(path, false); |
| } |
| |
| const char* DebugSafetyStateString(DownloadItem::SafetyState state) { |
| switch (state) { |
| case DownloadItem::SAFE: |
| return "SAFE"; |
| case DownloadItem::DANGEROUS: |
| return "DANGEROUS"; |
| case DownloadItem::DANGEROUS_BUT_VALIDATED: |
| return "DANGEROUS_BUT_VALIDATED"; |
| default: |
| NOTREACHED() << "Unknown safety state " << state; |
| return "unknown"; |
| }; |
| } |
| |
| const char* DebugDownloadStateString(DownloadItem::DownloadState state) { |
| switch (state) { |
| case DownloadItem::IN_PROGRESS: |
| return "IN_PROGRESS"; |
| case DownloadItem::COMPLETE: |
| return "COMPLETE"; |
| case DownloadItem::CANCELLED: |
| return "CANCELLED"; |
| case DownloadItem::REMOVING: |
| return "REMOVING"; |
| case DownloadItem::INTERRUPTED: |
| return "INTERRUPTED"; |
| default: |
| NOTREACHED() << "Unknown download state " << state; |
| return "unknown"; |
| }; |
| } |
| |
| DownloadItem::SafetyState GetSafetyState(bool dangerous_file, |
| bool dangerous_url) { |
| return (dangerous_url || dangerous_file) ? |
| DownloadItem::DANGEROUS : DownloadItem::SAFE; |
| } |
| |
| // Note: When a download has both |dangerous_file| and |dangerous_url| set, |
| // danger type is set to DANGEROUS_URL since the risk of dangerous URL |
| // overweights that of dangerous file type. |
| DownloadItem::DangerType GetDangerType(bool dangerous_file, |
| bool dangerous_url) { |
| if (dangerous_url) { |
| // dangerous URL overweights dangerous file. We check dangerous URL first. |
| return DownloadItem::DANGEROUS_URL; |
| } |
| return dangerous_file ? |
| DownloadItem::DANGEROUS_FILE : DownloadItem::NOT_DANGEROUS; |
| } |
| |
| } // namespace |
| |
| // Our download table ID starts at 1, so we use 0 to represent a download that |
| // has started, but has not yet had its data persisted in the table. We use fake |
| // database handles in incognito mode starting at -1 and progressively getting |
| // more negative. |
| // static |
| const int DownloadItem::kUninitializedHandle = 0; |
| |
| // Constructor for reading from the history service. |
| DownloadItem::DownloadItem(DownloadManager* download_manager, |
| const DownloadPersistentStoreInfo& info) |
| : download_id_(-1), |
| full_path_(info.path), |
| url_chain_(1, info.url), |
| referrer_url_(info.referrer_url), |
| total_bytes_(info.total_bytes), |
| received_bytes_(info.received_bytes), |
| start_tick_(base::TimeTicks()), |
| state_(static_cast<DownloadState>(info.state)), |
| start_time_(info.start_time), |
| db_handle_(info.db_handle), |
| download_manager_(download_manager), |
| is_paused_(false), |
| open_when_complete_(false), |
| file_externally_removed_(false), |
| safety_state_(SAFE), |
| auto_opened_(false), |
| is_otr_(false), |
| is_temporary_(false), |
| all_data_saved_(false), |
| opened_(false), |
| open_enabled_(true), |
| delegate_delayed_complete_(false) { |
| if (IsInProgress()) |
| state_ = CANCELLED; |
| if (IsComplete()) |
| all_data_saved_ = true; |
| Init(false /* not actively downloading */); |
| } |
| |
| // Constructing for a regular download: |
| DownloadItem::DownloadItem(DownloadManager* download_manager, |
| const DownloadCreateInfo& info, |
| bool is_otr) |
| : state_info_(info.original_name, info.save_info.file_path, |
| info.has_user_gesture, info.transition_type, |
| info.prompt_user_for_save_location, info.path_uniquifier, |
| false, false), |
| request_handle_(info.request_handle), |
| download_id_(info.download_id), |
| full_path_(info.path), |
| url_chain_(info.url_chain), |
| referrer_url_(info.referrer_url), |
| suggested_filename_(UTF16ToUTF8(info.save_info.suggested_name)), |
| content_disposition_(info.content_disposition), |
| mime_type_(info.mime_type), |
| original_mime_type_(info.original_mime_type), |
| referrer_charset_(info.referrer_charset), |
| total_bytes_(info.total_bytes), |
| received_bytes_(0), |
| last_os_error_(0), |
| start_tick_(base::TimeTicks::Now()), |
| state_(IN_PROGRESS), |
| start_time_(info.start_time), |
| db_handle_(DownloadItem::kUninitializedHandle), |
| download_manager_(download_manager), |
| is_paused_(false), |
| open_when_complete_(false), |
| file_externally_removed_(false), |
| safety_state_(SAFE), |
| auto_opened_(false), |
| is_otr_(is_otr), |
| is_temporary_(!info.save_info.file_path.empty()), |
| all_data_saved_(false), |
| opened_(false), |
| open_enabled_(true) { |
| Init(true /* actively downloading */); |
| } |
| |
| // Constructing for the "Save Page As..." feature: |
| DownloadItem::DownloadItem(DownloadManager* download_manager, |
| const FilePath& path, |
| const GURL& url, |
| bool is_otr, |
| int download_id) |
| : download_id_(download_id), |
| full_path_(path), |
| url_chain_(1, url), |
| referrer_url_(GURL()), |
| total_bytes_(0), |
| received_bytes_(0), |
| last_os_error_(0), |
| start_tick_(base::TimeTicks::Now()), |
| state_(IN_PROGRESS), |
| start_time_(base::Time::Now()), |
| db_handle_(DownloadItem::kUninitializedHandle), |
| download_manager_(download_manager), |
| is_paused_(false), |
| open_when_complete_(false), |
| file_externally_removed_(false), |
| safety_state_(SAFE), |
| auto_opened_(false), |
| is_otr_(is_otr), |
| is_temporary_(false), |
| all_data_saved_(false), |
| opened_(false), |
| open_enabled_(true) { |
| Init(true /* actively downloading */); |
| } |
| |
| DownloadItem::~DownloadItem() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| TransitionTo(REMOVING); |
| download_manager_->AssertQueueStateConsistent(this); |
| } |
| |
| void DownloadItem::AddObserver(Observer* observer) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| observers_.AddObserver(observer); |
| } |
| |
| void DownloadItem::RemoveObserver(Observer* observer) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| observers_.RemoveObserver(observer); |
| } |
| |
| void DownloadItem::UpdateObservers() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); |
| } |
| |
| bool DownloadItem::CanShowInFolder() { |
| return !IsCancelled() && !file_externally_removed_; |
| } |
| |
| bool DownloadItem::CanOpenDownload() { |
| return !file_externally_removed_; |
| } |
| |
| bool DownloadItem::ShouldOpenFileBasedOnExtension() { |
| return download_manager_->delegate()->ShouldOpenFileBasedOnExtension( |
| GetUserVerifiedFilePath()); |
| } |
| |
| void DownloadItem::OpenDownload() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| if (IsPartialDownload()) { |
| open_when_complete_ = !open_when_complete_; |
| return; |
| } |
| |
| if (!IsComplete() || file_externally_removed_) |
| return; |
| |
| // Ideally, we want to detect errors in opening and report them, but we |
| // don't generally have the proper interface for that to the external |
| // program that opens the file. So instead we spawn a check to update |
| // the UI if the file has been deleted in parallel with the open. |
| download_manager_->CheckForFileRemoval(this); |
| opened_ = true; |
| FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this)); |
| |
| // For testing: If download opening is disabled on this item, |
| // make the rest of the routine a no-op. |
| if (!open_enabled_) |
| return; |
| |
| if (download_manager_->delegate()->ShouldOpenDownload(this)) |
| content::GetContentClient()->browser()->OpenItem(full_path()); |
| } |
| |
| void DownloadItem::ShowDownloadInShell() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| content::GetContentClient()->browser()->ShowItemInFolder(full_path()); |
| } |
| |
| void DownloadItem::DangerousDownloadValidated() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK_EQ(DANGEROUS, safety_state()); |
| |
| UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated", |
| GetDangerType(), |
| DANGEROUS_TYPE_MAX); |
| |
| safety_state_ = DANGEROUS_BUT_VALIDATED; |
| UpdateObservers(); |
| |
| download_manager_->MaybeCompleteDownload(this); |
| } |
| |
| void DownloadItem::UpdateSize(int64 bytes_so_far) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| received_bytes_ = bytes_so_far; |
| |
| // If we've received more data than we were expecting (bad server info?), |
| // revert to 'unknown size mode'. |
| if (received_bytes_ > total_bytes_) |
| total_bytes_ = 0; |
| } |
| |
| void DownloadItem::StartProgressTimer() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| update_timer_.Start(FROM_HERE, |
| base::TimeDelta::FromMilliseconds(kUpdateTimeMs), this, |
| &DownloadItem::UpdateObservers); |
| } |
| |
| void DownloadItem::StopProgressTimer() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| update_timer_.Stop(); |
| } |
| |
| // Updates from the download thread may have been posted while this download |
| // was being cancelled in the UI thread, so we'll accept them unless we're |
| // complete. |
| void DownloadItem::Update(int64 bytes_so_far) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| if (!IsInProgress()) { |
| NOTREACHED(); |
| return; |
| } |
| UpdateSize(bytes_so_far); |
| UpdateObservers(); |
| } |
| |
| // Triggered by a user action. |
| void DownloadItem::Cancel(bool update_history) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); |
| if (!IsPartialDownload()) { |
| // Small downloads might be complete before this method has |
| // a chance to run. |
| return; |
| } |
| |
| download_stats::RecordDownloadCount(download_stats::CANCELLED_COUNT); |
| |
| TransitionTo(CANCELLED); |
| StopProgressTimer(); |
| if (update_history) |
| download_manager_->DownloadCancelledInternal(this); |
| } |
| |
| void DownloadItem::MarkAsComplete() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| DCHECK(all_data_saved_); |
| TransitionTo(COMPLETE); |
| } |
| |
| void DownloadItem::CompleteDelayedDownload() { |
| auto_opened_ = true; |
| Completed(); |
| } |
| |
| void DownloadItem::OnAllDataSaved(int64 size) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| DCHECK(!all_data_saved_); |
| all_data_saved_ = true; |
| UpdateSize(size); |
| StopProgressTimer(); |
| } |
| |
| void DownloadItem::OnDownloadedFileRemoved() { |
| file_externally_removed_ = true; |
| UpdateObservers(); |
| } |
| |
| void DownloadItem::Completed() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| VLOG(20) << __FUNCTION__ << "() " << DebugString(false); |
| |
| DCHECK(all_data_saved_); |
| TransitionTo(COMPLETE); |
| download_manager_->DownloadCompleted(id()); |
| download_stats::RecordDownloadCompleted(start_tick_); |
| |
| if (auto_opened_) { |
| // If it was already handled by the delegate, do nothing. |
| } else if (open_when_complete() || |
| ShouldOpenFileBasedOnExtension() || |
| is_temporary()) { |
| // If the download is temporary, like in drag-and-drop, do not open it but |
| // we still need to set it auto-opened so that it can be removed from the |
| // download shelf. |
| if (!is_temporary()) |
| OpenDownload(); |
| |
| auto_opened_ = true; |
| UpdateObservers(); |
| } |
| } |
| |
| void DownloadItem::TransitionTo(DownloadState new_state) { |
| if (state_ == new_state) |
| return; |
| |
| state_ = new_state; |
| UpdateObservers(); |
| } |
| |
| void DownloadItem::UpdateSafetyState() { |
| SafetyState updated_value( |
| GetSafetyState(state_info_.is_dangerous_file, |
| state_info_.is_dangerous_url)); |
| if (updated_value != safety_state_) { |
| safety_state_ = updated_value; |
| UpdateObservers(); |
| } |
| } |
| |
| void DownloadItem::UpdateTarget() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| if (state_info_.target_name.value().empty()) |
| state_info_.target_name = full_path_.BaseName(); |
| } |
| |
| void DownloadItem::Interrupted(int64 size, int os_error) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| if (!IsInProgress()) |
| return; |
| |
| last_os_error_ = os_error; |
| UpdateSize(size); |
| StopProgressTimer(); |
| download_stats::RecordDownloadInterrupted(os_error, |
| received_bytes_, |
| total_bytes_); |
| TransitionTo(INTERRUPTED); |
| } |
| |
| void DownloadItem::Delete(DeleteReason reason) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| switch (reason) { |
| case DELETE_DUE_TO_USER_DISCARD: |
| UMA_HISTOGRAM_ENUMERATION("Download.UserDiscard", GetDangerType(), |
| DANGEROUS_TYPE_MAX); |
| break; |
| case DELETE_DUE_TO_BROWSER_SHUTDOWN: |
| UMA_HISTOGRAM_ENUMERATION("Download.Discard", GetDangerType(), |
| DANGEROUS_TYPE_MAX); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| NewRunnableFunction(&DeleteDownloadedFile, full_path_)); |
| Remove(); |
| // We have now been deleted. |
| } |
| |
| void DownloadItem::Remove() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| download_manager_->AssertQueueStateConsistent(this); |
| Cancel(true); |
| download_manager_->AssertQueueStateConsistent(this); |
| |
| TransitionTo(REMOVING); |
| download_manager_->RemoveDownload(db_handle_); |
| // We have now been deleted. |
| } |
| |
| bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const { |
| if (total_bytes_ <= 0) |
| return false; // We never received the content_length for this download. |
| |
| int64 speed = CurrentSpeed(); |
| if (speed == 0) |
| return false; |
| |
| *remaining = base::TimeDelta::FromSeconds( |
| (total_bytes_ - received_bytes_) / speed); |
| return true; |
| } |
| |
| int64 DownloadItem::CurrentSpeed() const { |
| if (is_paused_) |
| return 0; |
| base::TimeDelta diff = base::TimeTicks::Now() - start_tick_; |
| int64 diff_ms = diff.InMilliseconds(); |
| return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms; |
| } |
| |
| int DownloadItem::PercentComplete() const { |
| // If the delegate is delaying completion of the download, then we have no |
| // idea how long it will take. |
| if (delegate_delayed_complete_ || total_bytes_ <= 0) |
| return -1; |
| |
| return static_cast<int>(received_bytes_ * 100.0 / total_bytes_); |
| } |
| |
| void DownloadItem::OnPathDetermined(const FilePath& path) { |
| full_path_ = path; |
| UpdateTarget(); |
| } |
| |
| void DownloadItem::Rename(const FilePath& full_path) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| VLOG(20) << __FUNCTION__ << "()" |
| << " full_path = \"" << full_path.value() << "\"" |
| << " " << DebugString(true); |
| DCHECK(!full_path.empty()); |
| full_path_ = full_path; |
| } |
| |
| void DownloadItem::TogglePause() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| DCHECK(IsInProgress()); |
| if (is_paused_) |
| request_handle_.ResumeRequest(); |
| else |
| request_handle_.PauseRequest(); |
| is_paused_ = !is_paused_; |
| UpdateObservers(); |
| } |
| |
| void DownloadItem::OnDownloadCompleting(DownloadFileManager* file_manager) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| VLOG(20) << __FUNCTION__ << "()" |
| << " needs rename = " << NeedsRename() |
| << " " << DebugString(true); |
| DCHECK_NE(DANGEROUS, safety_state()); |
| DCHECK(file_manager); |
| |
| if (NeedsRename()) { |
| BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| NewRunnableMethod(file_manager, |
| &DownloadFileManager::RenameCompletingDownloadFile, id(), |
| GetTargetFilePath(), safety_state() == SAFE)); |
| return; |
| } |
| |
| Completed(); |
| |
| BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| NewRunnableMethod(file_manager, &DownloadFileManager::CompleteDownload, |
| id())); |
| } |
| |
| void DownloadItem::OnDownloadRenamedToFinalName(const FilePath& full_path) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| VLOG(20) << __FUNCTION__ << "()" |
| << " full_path = \"" << full_path.value() << "\"" |
| << " needed rename = " << NeedsRename() |
| << " " << DebugString(false); |
| DCHECK(NeedsRename()); |
| |
| Rename(full_path); |
| |
| if (download_manager_->delegate()->ShouldCompleteDownload(this)) { |
| Completed(); |
| } else { |
| delegate_delayed_complete_ = true; |
| } |
| } |
| |
| bool DownloadItem::MatchesQuery(const string16& query) const { |
| if (query.empty()) |
| return true; |
| |
| DCHECK_EQ(query, base::i18n::ToLower(query)); |
| |
| string16 url_raw(base::i18n::ToLower(UTF8ToUTF16(GetURL().spec()))); |
| if (url_raw.find(query) != string16::npos) |
| return true; |
| |
| // TODO(phajdan.jr): write a test case for the following code. |
| // A good test case would be: |
| // "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", |
| // L"/\x4f60\x597d\x4f60\x597d", |
| // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD" |
| std::string languages; |
| TabContents* tab = request_handle_.GetTabContents(); |
| if (tab) |
| languages = content::GetContentClient()->browser()->GetAcceptLangs(tab); |
| string16 url_formatted( |
| base::i18n::ToLower(net::FormatUrl(GetURL(), languages))); |
| if (url_formatted.find(query) != string16::npos) |
| return true; |
| |
| string16 path(base::i18n::ToLower(full_path().LossyDisplayName())); |
| // This shouldn't just do a substring match; it is wrong for Unicode |
| // due to normalization and we have a fancier search-query system |
| // used elsewhere. |
| // http://code.google.com/p/chromium/issues/detail?id=71982 |
| return (path.find(query) != string16::npos); |
| } |
| |
| void DownloadItem::SetFileCheckResults(const DownloadStateInfo& state) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true); |
| state_info_ = state; |
| VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true); |
| |
| UpdateSafetyState(); |
| } |
| |
| DownloadItem::DangerType DownloadItem::GetDangerType() const { |
| return ::GetDangerType(state_info_.is_dangerous_file, |
| state_info_.is_dangerous_url); |
| } |
| |
| bool DownloadItem::IsDangerous() const { |
| return GetDangerType() != DownloadItem::NOT_DANGEROUS; |
| } |
| |
| void DownloadItem::MarkFileDangerous() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| state_info_.is_dangerous_file = true; |
| UpdateSafetyState(); |
| } |
| |
| void DownloadItem::MarkUrlDangerous() { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| state_info_.is_dangerous_url = true; |
| UpdateSafetyState(); |
| } |
| |
| DownloadPersistentStoreInfo DownloadItem::GetPersistentStoreInfo() const { |
| return DownloadPersistentStoreInfo(full_path(), |
| GetURL(), |
| referrer_url(), |
| start_time(), |
| received_bytes(), |
| total_bytes(), |
| state(), |
| db_handle()); |
| } |
| |
| FilePath DownloadItem::GetTargetFilePath() const { |
| return full_path_.DirName().Append(state_info_.target_name); |
| } |
| |
| FilePath DownloadItem::GetFileNameToReportUser() const { |
| if (state_info_.path_uniquifier > 0) { |
| FilePath name(state_info_.target_name); |
| DownloadFile::AppendNumberToPath(&name, state_info_.path_uniquifier); |
| return name; |
| } |
| return state_info_.target_name; |
| } |
| |
| FilePath DownloadItem::GetUserVerifiedFilePath() const { |
| return (safety_state_ == DownloadItem::SAFE) ? |
| GetTargetFilePath() : full_path_; |
| } |
| |
| void DownloadItem::OffThreadCancel(DownloadFileManager* file_manager) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| request_handle_.CancelRequest(); |
| |
| BrowserThread::PostTask( |
| BrowserThread::FILE, FROM_HERE, |
| NewRunnableMethod( |
| file_manager, &DownloadFileManager::CancelDownload, download_id_)); |
| } |
| |
| void DownloadItem::Init(bool active) { |
| // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. |
| CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| UpdateTarget(); |
| if (active) { |
| StartProgressTimer(); |
| download_stats::RecordDownloadCount(download_stats::START_COUNT); |
| } |
| VLOG(20) << __FUNCTION__ << "() " << DebugString(true); |
| } |
| |
| // TODO(ahendrickson) -- Move |INTERRUPTED| from |IsCancelled()| to |
| // |IsPartialDownload()|, when resuming interrupted downloads is implemented. |
| bool DownloadItem::IsPartialDownload() const { |
| return (state_ == IN_PROGRESS); |
| } |
| |
| bool DownloadItem::IsInProgress() const { |
| return (state_ == IN_PROGRESS); |
| } |
| |
| bool DownloadItem::IsCancelled() const { |
| return (state_ == CANCELLED) || |
| (state_ == INTERRUPTED); |
| } |
| |
| bool DownloadItem::IsInterrupted() const { |
| return (state_ == INTERRUPTED); |
| } |
| |
| bool DownloadItem::IsComplete() const { |
| return (state_ == COMPLETE); |
| } |
| |
| const GURL& DownloadItem::GetURL() const { |
| return url_chain_.empty() ? |
| GURL::EmptyGURL() : url_chain_.back(); |
| } |
| |
| std::string DownloadItem::DebugString(bool verbose) const { |
| std::string description = |
| base::StringPrintf("{ id = %d" |
| " state = %s", |
| download_id_, |
| DebugDownloadStateString(state())); |
| |
| // Construct a string of the URL chain. |
| std::string url_list("<none>"); |
| if (!url_chain_.empty()) { |
| std::vector<GURL>::const_iterator iter = url_chain_.begin(); |
| std::vector<GURL>::const_iterator last = url_chain_.end(); |
| url_list = (*iter).spec(); |
| ++iter; |
| for ( ; verbose && (iter != last); ++iter) { |
| url_list += " ->\n\t"; |
| const GURL& next_url = *iter; |
| url_list += next_url.spec(); |
| } |
| } |
| |
| if (verbose) { |
| description += base::StringPrintf( |
| " db_handle = %" PRId64 |
| " total_bytes = %" PRId64 |
| " received_bytes = %" PRId64 |
| " is_paused = %c" |
| " is_otr = %c" |
| " safety_state = %s" |
| " url_chain = \n\t\"%s\"\n\t" |
| " target_name = \"%" PRFilePath "\"" |
| " full_path = \"%" PRFilePath "\"", |
| db_handle(), |
| total_bytes(), |
| received_bytes(), |
| is_paused() ? 'T' : 'F', |
| is_otr() ? 'T' : 'F', |
| DebugSafetyStateString(safety_state()), |
| url_list.c_str(), |
| state_info_.target_name.value().c_str(), |
| full_path().value().c_str()); |
| } else { |
| description += base::StringPrintf(" url = \"%s\"", url_list.c_str()); |
| } |
| |
| description += " }"; |
| |
| return description; |
| } |