download_item.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/download/download_item.h" 6 7#include "app/l10n_util.h" 8#include "base/file_util.h" 9#include "base/logging.h" 10#include "base/timer.h" 11#include "base/utf_string_conversions.h" 12#include "net/base/net_util.h" 13#include "chrome/browser/browser_thread.h" 14#include "chrome/browser/download/download_history.h" 15#include "chrome/browser/download/download_manager.h" 16#include "chrome/browser/download/download_prefs.h" 17#include "chrome/browser/download/download_util.h" 18#include "chrome/browser/history/download_create_info.h" 19#include "chrome/browser/platform_util.h" 20#include "chrome/browser/prefs/pref_service.h" 21#include "chrome/browser/profile.h" 22#include "chrome/common/extensions/extension.h" 23#include "chrome/common/pref_names.h" 24 25namespace { 26 27// Update frequency (milliseconds). 28const int kUpdateTimeMs = 1000; 29 30void DeleteDownloadedFile(const FilePath& path) { 31 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 32 33 // Make sure we only delete files. 34 if (!file_util::DirectoryExists(path)) 35 file_util::Delete(path, false); 36} 37 38} // namespace 39 40// Constructor for reading from the history service. 41DownloadItem::DownloadItem(DownloadManager* download_manager, 42 const DownloadCreateInfo& info) 43 : id_(-1), 44 full_path_(info.path), 45 path_uniquifier_(0), 46 url_(info.url), 47 referrer_url_(info.referrer_url), 48 mime_type_(info.mime_type), 49 original_mime_type_(info.original_mime_type), 50 total_bytes_(info.total_bytes), 51 received_bytes_(info.received_bytes), 52 start_tick_(base::TimeTicks()), 53 state_(static_cast<DownloadState>(info.state)), 54 start_time_(info.start_time), 55 db_handle_(info.db_handle), 56 download_manager_(download_manager), 57 is_paused_(false), 58 open_when_complete_(false), 59 safety_state_(SAFE), 60 auto_opened_(false), 61 target_name_(info.original_name), 62 render_process_id_(-1), 63 request_id_(-1), 64 save_as_(false), 65 is_otr_(false), 66 is_extension_install_(info.is_extension_install), 67 name_finalized_(false), 68 is_temporary_(false), 69 opened_(false) { 70 if (state_ == IN_PROGRESS) 71 state_ = CANCELLED; 72 Init(false /* don't start progress timer */); 73} 74 75// Constructing for a regular download: 76DownloadItem::DownloadItem(DownloadManager* download_manager, 77 const DownloadCreateInfo& info, 78 bool is_otr) 79 : id_(info.download_id), 80 full_path_(info.path), 81 path_uniquifier_(info.path_uniquifier), 82 url_(info.url), 83 referrer_url_(info.referrer_url), 84 mime_type_(info.mime_type), 85 original_mime_type_(info.original_mime_type), 86 total_bytes_(info.total_bytes), 87 received_bytes_(0), 88 start_tick_(base::TimeTicks::Now()), 89 state_(IN_PROGRESS), 90 start_time_(info.start_time), 91 db_handle_(DownloadHistory::kUninitializedHandle), 92 download_manager_(download_manager), 93 is_paused_(false), 94 open_when_complete_(false), 95 safety_state_(info.is_dangerous ? DANGEROUS : SAFE), 96 auto_opened_(false), 97 target_name_(info.original_name), 98 render_process_id_(info.child_id), 99 request_id_(info.request_id), 100 save_as_(info.prompt_user_for_save_location), 101 is_otr_(is_otr), 102 is_extension_install_(info.is_extension_install), 103 name_finalized_(false), 104 is_temporary_(!info.save_info.file_path.empty()), 105 opened_(false) { 106 Init(true /* start progress timer */); 107} 108 109// Constructing for the "Save Page As..." feature: 110DownloadItem::DownloadItem(DownloadManager* download_manager, 111 const FilePath& path, 112 const GURL& url, 113 bool is_otr) 114 : id_(1), 115 full_path_(path), 116 path_uniquifier_(0), 117 url_(url), 118 referrer_url_(GURL()), 119 mime_type_(std::string()), 120 original_mime_type_(std::string()), 121 total_bytes_(0), 122 received_bytes_(0), 123 start_tick_(base::TimeTicks::Now()), 124 state_(IN_PROGRESS), 125 start_time_(base::Time::Now()), 126 db_handle_(DownloadHistory::kUninitializedHandle), 127 download_manager_(download_manager), 128 is_paused_(false), 129 open_when_complete_(false), 130 safety_state_(SAFE), 131 auto_opened_(false), 132 render_process_id_(-1), 133 request_id_(-1), 134 save_as_(false), 135 is_otr_(is_otr), 136 is_extension_install_(false), 137 name_finalized_(false), 138 is_temporary_(false), 139 opened_(false) { 140 Init(true /* start progress timer */); 141} 142 143DownloadItem::~DownloadItem() { 144 state_ = REMOVING; 145 UpdateObservers(); 146} 147 148void DownloadItem::AddObserver(Observer* observer) { 149 observers_.AddObserver(observer); 150} 151 152void DownloadItem::RemoveObserver(Observer* observer) { 153 observers_.RemoveObserver(observer); 154} 155 156void DownloadItem::UpdateObservers() { 157 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); 158} 159 160void DownloadItem::NotifyObserversDownloadFileCompleted() { 161 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadFileCompleted(this)); 162} 163 164bool DownloadItem::CanOpenDownload() { 165 return !Extension::IsExtension(target_name_) && 166 !download_util::IsExecutableFile(target_name_); 167} 168 169bool DownloadItem::ShouldOpenFileBasedOnExtension() { 170 return download_manager_->ShouldOpenFileBasedOnExtension( 171 GetUserVerifiedFileName()); 172} 173 174void DownloadItem::OpenFilesBasedOnExtension(bool open) { 175 DownloadPrefs* prefs = download_manager_->download_prefs(); 176 if (open) 177 prefs->EnableAutoOpenBasedOnExtension(GetUserVerifiedFileName()); 178 else 179 prefs->DisableAutoOpenBasedOnExtension(GetUserVerifiedFileName()); 180} 181 182void DownloadItem::OpenDownload() { 183 if (state() == DownloadItem::IN_PROGRESS) { 184 open_when_complete_ = !open_when_complete_; 185 } else if (state() == DownloadItem::COMPLETE) { 186 opened_ = true; 187 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this)); 188 if (is_extension_install()) { 189 download_util::OpenChromeExtension(download_manager_->profile(), 190 download_manager_, 191 *this); 192 return; 193 } 194#if defined(OS_MACOSX) 195 // Mac OS X requires opening downloads on the UI thread. 196 platform_util::OpenItem(full_path()); 197#else 198 BrowserThread::PostTask( 199 BrowserThread::FILE, FROM_HERE, 200 NewRunnableFunction(&platform_util::OpenItem, full_path())); 201#endif 202 } 203} 204 205void DownloadItem::ShowDownloadInShell() { 206#if defined(OS_MACOSX) 207 // Mac needs to run this operation on the UI thread. 208 platform_util::ShowItemInFolder(full_path()); 209#else 210 BrowserThread::PostTask( 211 BrowserThread::FILE, FROM_HERE, 212 NewRunnableFunction(&platform_util::ShowItemInFolder, 213 full_path())); 214#endif 215} 216 217void DownloadItem::DangerousDownloadValidated() { 218 download_manager_->DangerousDownloadValidated(this); 219} 220 221void DownloadItem::UpdateSize(int64 bytes_so_far) { 222 received_bytes_ = bytes_so_far; 223 224 // If we've received more data than we were expecting (bad server info?), 225 // revert to 'unknown size mode'. 226 if (received_bytes_ > total_bytes_) 227 total_bytes_ = 0; 228} 229 230void DownloadItem::StartProgressTimer() { 231 update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateTimeMs), this, 232 &DownloadItem::UpdateObservers); 233} 234 235void DownloadItem::StopProgressTimer() { 236 update_timer_.Stop(); 237} 238 239// Updates from the download thread may have been posted while this download 240// was being cancelled in the UI thread, so we'll accept them unless we're 241// complete. 242void DownloadItem::Update(int64 bytes_so_far) { 243 if (state_ == COMPLETE) { 244 NOTREACHED(); 245 return; 246 } 247 UpdateSize(bytes_so_far); 248 UpdateObservers(); 249} 250 251// Triggered by a user action. 252void DownloadItem::Cancel(bool update_history) { 253 if (state_ != IN_PROGRESS) { 254 // Small downloads might be complete before this method has a chance to run. 255 return; 256 } 257 state_ = CANCELLED; 258 UpdateObservers(); 259 StopProgressTimer(); 260 if (update_history) 261 download_manager_->DownloadCancelled(id_); 262} 263 264void DownloadItem::OnAllDataSaved(int64 size) { 265 state_ = COMPLETE; 266 UpdateSize(size); 267 StopProgressTimer(); 268} 269 270void DownloadItem::Finished() { 271 // Handle chrome extensions explicitly and skip the shell execute. 272 if (is_extension_install()) { 273 download_util::OpenChromeExtension(download_manager_->profile(), 274 download_manager_, 275 *this); 276 auto_opened_ = true; 277 } else if (open_when_complete() || 278 download_manager_->ShouldOpenFileBasedOnExtension( 279 GetUserVerifiedFileName()) || 280 is_temporary()) { 281 // If the download is temporary, like in drag-and-drop, do not open it but 282 // we still need to set it auto-opened so that it can be removed from the 283 // download shelf. 284 if (!is_temporary()) 285 OpenDownload(); 286 auto_opened_ = true; 287 } 288 289 // Notify our observers that we are complete (the call to OnAllDataSaved() 290 // set the state to complete but did not notify). 291 UpdateObservers(); 292 293 // The download file is meant to be completed if both the filename is 294 // finalized and the file data is downloaded. The ordering of these two 295 // actions is indeterministic. Thus, if the filename is not finalized yet, 296 // delay the notification. 297 if (name_finalized()) 298 NotifyObserversDownloadFileCompleted(); 299} 300 301void DownloadItem::Remove(bool delete_on_disk) { 302 Cancel(true); 303 state_ = REMOVING; 304 if (delete_on_disk) { 305 BrowserThread::PostTask( 306 BrowserThread::FILE, FROM_HERE, 307 NewRunnableFunction(&DeleteDownloadedFile, full_path_)); 308 } 309 download_manager_->RemoveDownload(db_handle_); 310 // We have now been deleted. 311} 312 313bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const { 314 if (total_bytes_ <= 0) 315 return false; // We never received the content_length for this download. 316 317 int64 speed = CurrentSpeed(); 318 if (speed == 0) 319 return false; 320 321 *remaining = 322 base::TimeDelta::FromSeconds((total_bytes_ - received_bytes_) / speed); 323 return true; 324} 325 326int64 DownloadItem::CurrentSpeed() const { 327 base::TimeDelta diff = base::TimeTicks::Now() - start_tick_; 328 int64 diff_ms = diff.InMilliseconds(); 329 return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms; 330} 331 332int DownloadItem::PercentComplete() const { 333 int percent = -1; 334 if (total_bytes_ > 0) 335 percent = static_cast<int>(received_bytes_ * 100.0 / total_bytes_); 336 return percent; 337} 338 339void DownloadItem::Rename(const FilePath& full_path) { 340 DCHECK(!full_path.empty()); 341 full_path_ = full_path; 342} 343 344void DownloadItem::TogglePause() { 345 DCHECK(state_ == IN_PROGRESS); 346 download_manager_->PauseDownload(id_, !is_paused_); 347 is_paused_ = !is_paused_; 348 UpdateObservers(); 349} 350 351void DownloadItem::OnNameFinalized() { 352 name_finalized_ = true; 353 354 // The download file is meant to be completed if both the filename is 355 // finalized and the file data is downloaded. The ordering of these two 356 // actions is indeterministic. Thus, if we are still in downloading the 357 // file, delay the notification. 358 if (state() == DownloadItem::COMPLETE) 359 NotifyObserversDownloadFileCompleted(); 360} 361 362bool DownloadItem::MatchesQuery(const string16& query) const { 363 if (query.empty()) 364 return true; 365 366 DCHECK_EQ(query, l10n_util::ToLower(query)); 367 368 string16 url_raw(l10n_util::ToLower(UTF8ToUTF16(url_.spec()))); 369 if (url_raw.find(query) != string16::npos) 370 return true; 371 372 // TODO(phajdan.jr): write a test case for the following code. 373 // A good test case would be: 374 // "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", 375 // L"/\x4f60\x597d\x4f60\x597d", 376 // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD" 377 PrefService* prefs = download_manager_->profile()->GetPrefs(); 378 std::string languages(prefs->GetString(prefs::kAcceptLanguages)); 379 string16 url_formatted(l10n_util::ToLower(net::FormatUrl(url_, languages))); 380 if (url_formatted.find(query) != string16::npos) 381 return true; 382 383 string16 path(l10n_util::ToLower(WideToUTF16(full_path().ToWStringHack()))); 384 if (path.find(query) != std::wstring::npos) 385 return true; 386 387 return false; 388} 389 390FilePath DownloadItem::GetTargetFilePath() const { 391 return full_path_.DirName().Append(target_name_); 392} 393 394FilePath DownloadItem::GetFileNameToReportUser() const { 395 if (path_uniquifier_ > 0) { 396 FilePath name(target_name_); 397 download_util::AppendNumberToPath(&name, path_uniquifier_); 398 return name; 399 } 400 return target_name_; 401} 402 403FilePath DownloadItem::GetUserVerifiedFileName() const { 404 if (safety_state_ == DownloadItem::SAFE) 405 return target_name_; 406 return full_path_.BaseName(); 407} 408 409void DownloadItem::Init(bool start_timer) { 410 if (target_name_.value().empty()) 411 target_name_ = full_path_.BaseName(); 412 if (start_timer) 413 StartProgressTimer(); 414} 415