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