1 // Copyright (c) 2011 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_manager.h" 6 7 #include "base/callback.h" 8 #include "base/file_util.h" 9 #include "base/logging.h" 10 #include "base/path_service.h" 11 #include "base/rand_util.h" 12 #include "base/stl_util-inl.h" 13 #include "base/stringprintf.h" 14 #include "base/sys_string_conversions.h" 15 #include "base/task.h" 16 #include "base/utf_string_conversions.h" 17 #include "build/build_config.h" 18 #include "chrome/browser/browser_process.h" 19 #include "chrome/browser/download/download_extensions.h" 20 #include "chrome/browser/download/download_file_manager.h" 21 #include "chrome/browser/download/download_history.h" 22 #include "chrome/browser/download/download_item.h" 23 #include "chrome/browser/download/download_prefs.h" 24 #include "chrome/browser/download/download_safe_browsing_client.h" 25 #include "chrome/browser/download/download_status_updater.h" 26 #include "chrome/browser/download/download_util.h" 27 #include "chrome/browser/extensions/extension_service.h" 28 #include "chrome/browser/history/download_create_info.h" 29 #include "chrome/browser/net/chrome_url_request_context.h" 30 #include "chrome/browser/platform_util.h" 31 #include "chrome/browser/profiles/profile.h" 32 #include "chrome/browser/tab_contents/tab_util.h" 33 #include "chrome/browser/ui/browser.h" 34 #include "chrome/browser/ui/browser_list.h" 35 #include "chrome/common/chrome_paths.h" 36 #include "content/browser/browser_thread.h" 37 #include "content/browser/renderer_host/render_process_host.h" 38 #include "content/browser/renderer_host/render_view_host.h" 39 #include "content/browser/renderer_host/resource_dispatcher_host.h" 40 #include "content/browser/tab_contents/tab_contents.h" 41 #include "content/common/notification_type.h" 42 #include "googleurl/src/gurl.h" 43 #include "grit/generated_resources.h" 44 #include "grit/theme_resources.h" 45 #include "net/base/mime_util.h" 46 #include "net/base/net_util.h" 47 #include "ui/base/l10n/l10n_util.h" 48 #include "ui/base/resource/resource_bundle.h" 49 50 DownloadManager::DownloadManager(DownloadStatusUpdater* status_updater) 51 : shutdown_needed_(false), 52 profile_(NULL), 53 file_manager_(NULL), 54 status_updater_(status_updater->AsWeakPtr()) { 55 if (status_updater_) 56 status_updater_->AddDelegate(this); 57 } 58 59 DownloadManager::~DownloadManager() { 60 DCHECK(!shutdown_needed_); 61 if (status_updater_) 62 status_updater_->RemoveDelegate(this); 63 } 64 65 void DownloadManager::Shutdown() { 66 VLOG(20) << __FUNCTION__ << "()" 67 << " shutdown_needed_ = " << shutdown_needed_; 68 if (!shutdown_needed_) 69 return; 70 shutdown_needed_ = false; 71 72 FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown()); 73 74 if (file_manager_) { 75 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 76 NewRunnableMethod(file_manager_, 77 &DownloadFileManager::OnDownloadManagerShutdown, 78 make_scoped_refptr(this))); 79 } 80 81 AssertContainersConsistent(); 82 83 // Go through all downloads in downloads_. Dangerous ones we need to 84 // remove on disk, and in progress ones we need to cancel. 85 for (DownloadSet::iterator it = downloads_.begin(); it != downloads_.end();) { 86 DownloadItem* download = *it; 87 88 // Save iterator from potential erases in this set done by called code. 89 // Iterators after an erasure point are still valid for lists and 90 // associative containers such as sets. 91 it++; 92 93 if (download->safety_state() == DownloadItem::DANGEROUS && 94 download->IsPartialDownload()) { 95 // The user hasn't accepted it, so we need to remove it 96 // from the disk. This may or may not result in it being 97 // removed from the DownloadManager queues and deleted 98 // (specifically, DownloadManager::RemoveDownload only 99 // removes and deletes it if it's known to the history service) 100 // so the only thing we know after calling this function is that 101 // the download was deleted if-and-only-if it was removed 102 // from all queues. 103 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN); 104 } else if (download->IsPartialDownload()) { 105 download->Cancel(false); 106 download_history_->UpdateEntry(download); 107 } 108 } 109 110 // At this point, all dangerous downloads have had their files removed 111 // and all in progress downloads have been cancelled. We can now delete 112 // anything left. 113 STLDeleteElements(&downloads_); 114 115 // And clear all non-owning containers. 116 in_progress_.clear(); 117 active_downloads_.clear(); 118 #if !defined(NDEBUG) 119 save_page_as_downloads_.clear(); 120 #endif 121 122 file_manager_ = NULL; 123 124 // Make sure the save as dialog doesn't notify us back if we're gone before 125 // it returns. 126 if (select_file_dialog_.get()) 127 select_file_dialog_->ListenerDestroyed(); 128 129 download_history_.reset(); 130 download_prefs_.reset(); 131 132 request_context_getter_ = NULL; 133 134 shutdown_needed_ = false; 135 } 136 137 void DownloadManager::GetTemporaryDownloads( 138 const FilePath& dir_path, std::vector<DownloadItem*>* result) { 139 DCHECK(result); 140 141 for (DownloadMap::iterator it = history_downloads_.begin(); 142 it != history_downloads_.end(); ++it) { 143 if (it->second->is_temporary() && 144 it->second->full_path().DirName() == dir_path) 145 result->push_back(it->second); 146 } 147 } 148 149 void DownloadManager::GetAllDownloads( 150 const FilePath& dir_path, std::vector<DownloadItem*>* result) { 151 DCHECK(result); 152 153 for (DownloadMap::iterator it = history_downloads_.begin(); 154 it != history_downloads_.end(); ++it) { 155 if (!it->second->is_temporary() && 156 (dir_path.empty() || it->second->full_path().DirName() == dir_path)) 157 result->push_back(it->second); 158 } 159 } 160 161 void DownloadManager::GetCurrentDownloads( 162 const FilePath& dir_path, std::vector<DownloadItem*>* result) { 163 DCHECK(result); 164 165 for (DownloadMap::iterator it = history_downloads_.begin(); 166 it != history_downloads_.end(); ++it) { 167 DownloadItem* item =it->second; 168 // Skip temporary items. 169 if (item->is_temporary()) 170 continue; 171 // Skip items that have all their data, and are OK to save. 172 if (!item->IsPartialDownload() && 173 (item->safety_state() != DownloadItem::DANGEROUS)) 174 continue; 175 // Skip items that don't match |dir_path|. 176 // If |dir_path| is empty, all remaining items match. 177 if (!dir_path.empty() && (it->second->full_path().DirName() != dir_path)) 178 continue; 179 180 result->push_back(item); 181 } 182 183 // If we have a parent profile, let it add its downloads to the results. 184 Profile* original_profile = profile_->GetOriginalProfile(); 185 if (original_profile != profile_) 186 original_profile->GetDownloadManager()->GetCurrentDownloads(dir_path, 187 result); 188 } 189 190 void DownloadManager::SearchDownloads(const string16& query, 191 std::vector<DownloadItem*>* result) { 192 DCHECK(result); 193 194 string16 query_lower(l10n_util::ToLower(query)); 195 196 for (DownloadMap::iterator it = history_downloads_.begin(); 197 it != history_downloads_.end(); ++it) { 198 DownloadItem* download_item = it->second; 199 200 if (download_item->is_temporary() || download_item->is_extension_install()) 201 continue; 202 203 // Display Incognito downloads only in Incognito window, and vice versa. 204 // The Incognito Downloads page will get the list of non-Incognito downloads 205 // from its parent profile. 206 if (profile_->IsOffTheRecord() != download_item->is_otr()) 207 continue; 208 209 if (download_item->MatchesQuery(query_lower)) 210 result->push_back(download_item); 211 } 212 213 // If we have a parent profile, let it add its downloads to the results. 214 Profile* original_profile = profile_->GetOriginalProfile(); 215 if (original_profile != profile_) 216 original_profile->GetDownloadManager()->SearchDownloads(query, result); 217 } 218 219 // Query the history service for information about all persisted downloads. 220 bool DownloadManager::Init(Profile* profile) { 221 DCHECK(profile); 222 DCHECK(!shutdown_needed_) << "DownloadManager already initialized."; 223 shutdown_needed_ = true; 224 225 profile_ = profile; 226 request_context_getter_ = profile_->GetRequestContext(); 227 download_history_.reset(new DownloadHistory(profile)); 228 download_history_->Load( 229 NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete)); 230 231 download_prefs_.reset(new DownloadPrefs(profile_->GetPrefs())); 232 233 // In test mode, there may be no ResourceDispatcherHost. In this case it's 234 // safe to avoid setting |file_manager_| because we only call a small set of 235 // functions, none of which need it. 236 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host(); 237 if (rdh) { 238 file_manager_ = rdh->download_file_manager(); 239 DCHECK(file_manager_); 240 } 241 242 other_download_manager_observer_.reset( 243 new OtherDownloadManagerObserver(this)); 244 245 return true; 246 } 247 248 // We have received a message from DownloadFileManager about a new download. We 249 // create a download item and store it in our download map, and inform the 250 // history system of a new download. Since this method can be called while the 251 // history service thread is still reading the persistent state, we do not 252 // insert the new DownloadItem into 'history_downloads_' or inform our 253 // observers at this point. OnCreateDownloadEntryComplete() handles that 254 // finalization of the the download creation as a callback from the 255 // history thread. 256 void DownloadManager::StartDownload(DownloadCreateInfo* info) { 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 258 259 // Create a client to verify download URL with safebrowsing. 260 // It deletes itself after the callback. 261 scoped_refptr<DownloadSBClient> sb_client = new DownloadSBClient( 262 info->download_id, info->url_chain, info->referrer_url); 263 sb_client->CheckDownloadUrl( 264 info, NewCallback(this, &DownloadManager::CheckDownloadUrlDone)); 265 } 266 267 void DownloadManager::CheckDownloadUrlDone(DownloadCreateInfo* info, 268 bool is_dangerous_url) { 269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 270 DCHECK(info); 271 272 info->is_dangerous_url = is_dangerous_url; 273 274 // Check whether this download is for an extension install or not. 275 // Allow extensions to be explicitly saved. 276 if (!info->prompt_user_for_save_location) { 277 if (UserScript::IsURLUserScript(info->url(), info->mime_type) || 278 info->mime_type == Extension::kMimeType) { 279 info->is_extension_install = true; 280 } 281 } 282 283 if (info->save_info.file_path.empty()) { 284 FilePath generated_name; 285 download_util::GenerateFileNameFromInfo(info, &generated_name); 286 287 // Freeze the user's preference for showing a Save As dialog. We're going 288 // to bounce around a bunch of threads and we don't want to worry about race 289 // conditions where the user changes this pref out from under us. 290 if (download_prefs_->PromptForDownload()) { 291 // But ignore the user's preference for the following scenarios: 292 // 1) Extension installation. Note that we only care here about the case 293 // where an extension is installed, not when one is downloaded with 294 // "save as...". 295 // 2) Filetypes marked "always open." If the user just wants this file 296 // opened, don't bother asking where to keep it. 297 if (!info->is_extension_install && 298 !ShouldOpenFileBasedOnExtension(generated_name)) 299 info->prompt_user_for_save_location = true; 300 } 301 if (download_prefs_->IsDownloadPathManaged()) { 302 info->prompt_user_for_save_location = false; 303 } 304 305 // Determine the proper path for a download, by either one of the following: 306 // 1) using the default download directory. 307 // 2) prompting the user. 308 if (info->prompt_user_for_save_location && !last_download_path_.empty()) { 309 info->suggested_path = last_download_path_; 310 } else { 311 info->suggested_path = download_prefs_->download_path(); 312 } 313 info->suggested_path = info->suggested_path.Append(generated_name); 314 } else { 315 info->suggested_path = info->save_info.file_path; 316 } 317 318 if (!info->prompt_user_for_save_location && 319 info->save_info.file_path.empty()) { 320 info->is_dangerous_file = download_util::IsDangerous( 321 info, profile(), ShouldOpenFileBasedOnExtension(info->suggested_path)); 322 } 323 324 // We need to move over to the download thread because we don't want to stat 325 // the suggested path on the UI thread. 326 // We can only access preferences on the UI thread, so check the download path 327 // now and pass the value to the FILE thread. 328 BrowserThread::PostTask( 329 BrowserThread::FILE, FROM_HERE, 330 NewRunnableMethod( 331 this, 332 &DownloadManager::CheckIfSuggestedPathExists, 333 info, 334 download_prefs()->download_path())); 335 } 336 337 void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info, 338 const FilePath& default_path) { 339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 340 DCHECK(info); 341 342 // Make sure the default download directory exists. 343 // TODO(phajdan.jr): only create the directory when we're sure the user 344 // is going to save there and not to another directory of his choice. 345 file_util::CreateDirectory(default_path); 346 347 // Check writability of the suggested path. If we can't write to it, default 348 // to the user's "My Documents" directory. We'll prompt them in this case. 349 FilePath dir = info->suggested_path.DirName(); 350 FilePath filename = info->suggested_path.BaseName(); 351 if (!file_util::PathIsWritable(dir)) { 352 VLOG(1) << "Unable to write to directory \"" << dir.value() << "\""; 353 info->prompt_user_for_save_location = true; 354 PathService::Get(chrome::DIR_USER_DOCUMENTS, &info->suggested_path); 355 info->suggested_path = info->suggested_path.Append(filename); 356 } 357 358 // If the download is deemed dangerous, we'll use a temporary name for it. 359 if (info->IsDangerous()) { 360 info->original_name = FilePath(info->suggested_path).BaseName(); 361 // Create a temporary file to hold the file until the user approves its 362 // download. 363 FilePath::StringType file_name; 364 FilePath path; 365 #if defined(OS_WIN) 366 string16 unconfirmed_prefix = 367 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); 368 #else 369 std::string unconfirmed_prefix = 370 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX); 371 #endif 372 373 while (path.empty()) { 374 base::SStringPrintf( 375 &file_name, 376 unconfirmed_prefix.append( 377 FILE_PATH_LITERAL(" %d.crdownload")).c_str(), 378 base::RandInt(0, 100000)); 379 path = dir.Append(file_name); 380 if (file_util::PathExists(path)) 381 path = FilePath(); 382 } 383 info->suggested_path = path; 384 } else { 385 // Do not add the path uniquifier if we are saving to a specific path as in 386 // the drag-out case. 387 if (info->save_info.file_path.empty()) { 388 info->path_uniquifier = download_util::GetUniquePathNumberWithCrDownload( 389 info->suggested_path); 390 } 391 // We know the final path, build it if necessary. 392 if (info->path_uniquifier > 0) { 393 download_util::AppendNumberToPath(&(info->suggested_path), 394 info->path_uniquifier); 395 // Setting path_uniquifier to 0 to make sure we don't try to unique it 396 // later on. 397 info->path_uniquifier = 0; 398 } else if (info->path_uniquifier == -1) { 399 // We failed to find a unique path. We have to prompt the user. 400 VLOG(1) << "Unable to find a unique path for suggested path \"" 401 << info->suggested_path.value() << "\""; 402 info->prompt_user_for_save_location = true; 403 } 404 } 405 406 // Create an empty file at the suggested path so that we don't allocate the 407 // same "non-existant" path to multiple downloads. 408 // See: http://code.google.com/p/chromium/issues/detail?id=3662 409 if (!info->prompt_user_for_save_location && 410 info->save_info.file_path.empty()) { 411 if (info->IsDangerous()) 412 file_util::WriteFile(info->suggested_path, "", 0); 413 else 414 file_util::WriteFile(download_util::GetCrDownloadPath( 415 info->suggested_path), "", 0); 416 } 417 418 BrowserThread::PostTask( 419 BrowserThread::UI, FROM_HERE, 420 NewRunnableMethod(this, 421 &DownloadManager::OnPathExistenceAvailable, 422 info)); 423 } 424 425 void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) { 426 VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString(); 427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 428 DCHECK(info); 429 430 if (info->prompt_user_for_save_location) { 431 // We must ask the user for the place to put the download. 432 if (!select_file_dialog_.get()) 433 select_file_dialog_ = SelectFileDialog::Create(this); 434 435 TabContents* contents = tab_util::GetTabContentsByID(info->child_id, 436 info->render_view_id); 437 SelectFileDialog::FileTypeInfo file_type_info; 438 file_type_info.extensions.resize(1); 439 file_type_info.extensions[0].push_back(info->suggested_path.Extension()); 440 if (!file_type_info.extensions[0][0].empty()) 441 file_type_info.extensions[0][0].erase(0, 1); // drop the . 442 file_type_info.include_all_files = true; 443 gfx::NativeWindow owning_window = 444 contents ? platform_util::GetTopLevel(contents->GetNativeView()) : NULL; 445 select_file_dialog_->SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE, 446 string16(), 447 info->suggested_path, 448 &file_type_info, 0, FILE_PATH_LITERAL(""), 449 contents, owning_window, info); 450 FOR_EACH_OBSERVER(Observer, observers_, 451 SelectFileDialogDisplayed(info->download_id)); 452 } else { 453 // No prompting for download, just continue with the suggested name. 454 info->path = info->suggested_path; 455 AttachDownloadItem(info); 456 } 457 } 458 459 void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) { 460 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 461 462 DownloadItem* download = new DownloadItem(this, *info, 463 profile_->IsOffTheRecord()); 464 DCHECK(!ContainsKey(in_progress_, info->download_id)); 465 DCHECK(!ContainsKey(active_downloads_, info->download_id)); 466 downloads_.insert(download); 467 active_downloads_[info->download_id] = download; 468 } 469 470 void DownloadManager::AttachDownloadItem(DownloadCreateInfo* info) { 471 VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString(); 472 473 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 474 475 // Life of |info| ends here. No more references to it after this method. 476 scoped_ptr<DownloadCreateInfo> infop(info); 477 478 // NOTE(ahendrickson) Eventually |active_downloads_| will replace 479 // |in_progress_|, but we don't want to change the semantics yet. 480 DCHECK(!ContainsKey(in_progress_, info->download_id)); 481 DCHECK(ContainsKey(active_downloads_, info->download_id)); 482 DownloadItem* download = active_downloads_[info->download_id]; 483 DCHECK(download != NULL); 484 DCHECK(ContainsKey(downloads_, download)); 485 486 download->SetFileCheckResults(info->path, 487 info->is_dangerous_file, 488 info->is_dangerous_url, 489 info->path_uniquifier, 490 info->prompt_user_for_save_location, 491 info->is_extension_install, 492 info->original_name); 493 in_progress_[info->download_id] = download; 494 UpdateAppIcon(); // Reflect entry into in_progress_. 495 496 // Rename to intermediate name. 497 FilePath download_path; 498 if (info->IsDangerous()) { 499 // The download is not safe. We can now rename the file to its 500 // tentative name using RenameInProgressDownloadFile. 501 // NOTE: The |Rename| below will be a no-op for dangerous files, as we're 502 // renaming it to the same name. 503 download_path = info->path; 504 } else { 505 // The download is a safe download. We need to 506 // rename it to its intermediate '.crdownload' path. The final 507 // name after user confirmation will be set from 508 // DownloadItem::OnDownloadCompleting. 509 download_path = download_util::GetCrDownloadPath(info->path); 510 } 511 512 BrowserThread::PostTask( 513 BrowserThread::FILE, FROM_HERE, 514 NewRunnableMethod( 515 file_manager_, &DownloadFileManager::RenameInProgressDownloadFile, 516 download->id(), download_path)); 517 518 download->Rename(download_path); 519 520 download_history_->AddEntry(*info, download, 521 NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete)); 522 } 523 524 void DownloadManager::UpdateDownload(int32 download_id, int64 size) { 525 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 526 DownloadMap::iterator it = active_downloads_.find(download_id); 527 if (it != active_downloads_.end()) { 528 DownloadItem* download = it->second; 529 if (download->IsInProgress()) { 530 download->Update(size); 531 UpdateAppIcon(); // Reflect size updates. 532 download_history_->UpdateEntry(download); 533 } 534 } 535 } 536 537 void DownloadManager::OnResponseCompleted(int32 download_id, 538 int64 size, 539 int os_error, 540 const std::string& hash) { 541 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 542 // ERR_CONNECTION_CLOSED is allowed since a number of servers in the wild 543 // advertise a larger Content-Length than the amount of bytes in the message 544 // body, and then close the connection. Other browsers - IE8, Firefox 4.0.1, 545 // and Safari 5.0.4 - treat the download as complete in this case, so we 546 // follow their lead. 547 if (os_error == 0 || os_error == net::ERR_CONNECTION_CLOSED) { 548 OnAllDataSaved(download_id, size, hash); 549 } else { 550 OnDownloadError(download_id, size, os_error); 551 } 552 } 553 554 void DownloadManager::OnAllDataSaved(int32 download_id, 555 int64 size, 556 const std::string& hash) { 557 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id 558 << " size = " << size; 559 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 560 561 // If it's not in active_downloads_, that means it was cancelled; just 562 // ignore the notification. 563 if (active_downloads_.count(download_id) == 0) 564 return; 565 566 DownloadItem* download = active_downloads_[download_id]; 567 download->OnAllDataSaved(size); 568 569 // When hash is not available, it means either it is not calculated 570 // or there is error while it is calculated. We will skip the download hash 571 // check in that case. 572 if (!hash.empty()) { 573 scoped_refptr<DownloadSBClient> sb_client = 574 new DownloadSBClient(download_id, 575 download->url_chain(), 576 download->referrer_url()); 577 sb_client->CheckDownloadHash( 578 hash, NewCallback(this, &DownloadManager::CheckDownloadHashDone)); 579 } 580 MaybeCompleteDownload(download); 581 } 582 583 // TODO(lzheng): This function currently works as a callback place holder. 584 // Once we decide the hash check is reliable, we could move the 585 // MaybeCompleteDownload in OnAllDataSaved to this function. 586 void DownloadManager::CheckDownloadHashDone(int32 download_id, 587 bool is_dangerous_hash) { 588 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 589 DVLOG(1) << "CheckDownloadHashDone, download_id: " << download_id 590 << " is dangerous_hash: " << is_dangerous_hash; 591 592 // If it's not in active_downloads_, that means it was cancelled or 593 // the download already finished. 594 if (active_downloads_.count(download_id) == 0) 595 return; 596 597 DVLOG(1) << "CheckDownloadHashDone, url: " 598 << active_downloads_[download_id]->url().spec(); 599 } 600 601 bool DownloadManager::IsDownloadReadyForCompletion(DownloadItem* download) { 602 // If we don't have all the data, the download is not ready for 603 // completion. 604 if (!download->all_data_saved()) 605 return false; 606 607 // If the download is dangerous, but not yet validated, it's not ready for 608 // completion. 609 if (download->safety_state() == DownloadItem::DANGEROUS) 610 return false; 611 612 // If the download isn't active (e.g. has been cancelled) it's not 613 // ready for completion. 614 if (active_downloads_.count(download->id()) == 0) 615 return false; 616 617 // If the download hasn't been inserted into the history system 618 // (which occurs strictly after file name determination, intermediate 619 // file rename, and UI display) then it's not ready for completion. 620 return (download->db_handle() != DownloadHistory::kUninitializedHandle); 621 } 622 623 void DownloadManager::MaybeCompleteDownload(DownloadItem* download) { 624 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 625 VLOG(20) << __FUNCTION__ << "()" << " download = " 626 << download->DebugString(false); 627 628 if (!IsDownloadReadyForCompletion(download)) 629 return; 630 631 // TODO(rdsmith): DCHECK that we only pass through this point 632 // once per download. The natural way to do this is by a state 633 // transition on the DownloadItem. 634 635 // Confirm we're in the proper set of states to be here; 636 // in in_progress_, have all data, have a history handle, (validated or safe). 637 DCHECK_NE(DownloadItem::DANGEROUS, download->safety_state()); 638 DCHECK_EQ(1u, in_progress_.count(download->id())); 639 DCHECK(download->all_data_saved()); 640 DCHECK(download->db_handle() != DownloadHistory::kUninitializedHandle); 641 DCHECK_EQ(1u, history_downloads_.count(download->db_handle())); 642 643 VLOG(20) << __FUNCTION__ << "()" << " executing: download = " 644 << download->DebugString(false); 645 646 // Remove the id from in_progress 647 in_progress_.erase(download->id()); 648 UpdateAppIcon(); // Reflect removal from in_progress_. 649 650 download_history_->UpdateEntry(download); 651 652 // Finish the download. 653 download->OnDownloadCompleting(file_manager_); 654 } 655 656 void DownloadManager::DownloadCompleted(int32 download_id) { 657 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 658 DownloadItem* download = GetDownloadItem(download_id); 659 DCHECK(download); 660 download_history_->UpdateEntry(download); 661 active_downloads_.erase(download_id); 662 } 663 664 void DownloadManager::OnDownloadRenamedToFinalName(int download_id, 665 const FilePath& full_path, 666 int uniquifier) { 667 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id 668 << " full_path = \"" << full_path.value() << "\"" 669 << " uniquifier = " << uniquifier; 670 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 671 672 DownloadItem* item = GetDownloadItem(download_id); 673 if (!item) 674 return; 675 676 if (item->safety_state() == DownloadItem::SAFE) { 677 DCHECK_EQ(0, uniquifier) << "We should not uniquify SAFE downloads twice"; 678 } 679 680 BrowserThread::PostTask( 681 BrowserThread::FILE, FROM_HERE, 682 NewRunnableMethod( 683 file_manager_, &DownloadFileManager::CompleteDownload, download_id)); 684 685 if (uniquifier) 686 item->set_path_uniquifier(uniquifier); 687 688 item->OnDownloadRenamedToFinalName(full_path); 689 download_history_->UpdateDownloadPath(item, full_path); 690 } 691 692 void DownloadManager::DownloadCancelled(int32 download_id) { 693 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 694 DownloadMap::iterator it = in_progress_.find(download_id); 695 if (it == in_progress_.end()) 696 return; 697 DownloadItem* download = it->second; 698 699 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id 700 << " download = " << download->DebugString(true); 701 702 // Clean up will happen when the history system create callback runs if we 703 // don't have a valid db_handle yet. 704 if (download->db_handle() != DownloadHistory::kUninitializedHandle) { 705 in_progress_.erase(it); 706 active_downloads_.erase(download_id); 707 UpdateAppIcon(); // Reflect removal from in_progress_. 708 download_history_->UpdateEntry(download); 709 } 710 711 DownloadCancelledInternal(download_id, 712 download->render_process_id(), 713 download->request_id()); 714 } 715 716 void DownloadManager::DownloadCancelledInternal(int download_id, 717 int render_process_id, 718 int request_id) { 719 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 720 // Cancel the network request. RDH is guaranteed to outlive the IO thread. 721 BrowserThread::PostTask( 722 BrowserThread::IO, FROM_HERE, 723 NewRunnableFunction(&download_util::CancelDownloadRequest, 724 g_browser_process->resource_dispatcher_host(), 725 render_process_id, 726 request_id)); 727 728 BrowserThread::PostTask( 729 BrowserThread::FILE, FROM_HERE, 730 NewRunnableMethod( 731 file_manager_, &DownloadFileManager::CancelDownload, download_id)); 732 } 733 734 void DownloadManager::OnDownloadError(int32 download_id, 735 int64 size, 736 int os_error) { 737 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 738 DownloadMap::iterator it = active_downloads_.find(download_id); 739 // A cancel at the right time could remove the download from the 740 // |active_downloads_| map before we get here. 741 if (it == active_downloads_.end()) 742 return; 743 744 DownloadItem* download = it->second; 745 746 VLOG(20) << "Error " << os_error << " at offset " 747 << download->received_bytes() << " for download = " 748 << download->DebugString(true); 749 750 // TODO(ahendrickson) - Remove this when we add resuming of interrupted 751 // downloads, as we will keep the download item around in that case. 752 // 753 // Clean up will happen when the history system create callback runs if we 754 // don't have a valid db_handle yet. 755 if (download->db_handle() != DownloadHistory::kUninitializedHandle) { 756 in_progress_.erase(download_id); 757 active_downloads_.erase(download_id); 758 UpdateAppIcon(); // Reflect removal from in_progress_. 759 download_history_->UpdateEntry(download); 760 } 761 762 download->Interrupted(size, os_error); 763 764 BrowserThread::PostTask( 765 BrowserThread::FILE, FROM_HERE, 766 NewRunnableMethod( 767 file_manager_, &DownloadFileManager::CancelDownload, download_id)); 768 } 769 770 void DownloadManager::PauseDownload(int32 download_id, bool pause) { 771 DownloadMap::iterator it = in_progress_.find(download_id); 772 if (it == in_progress_.end()) 773 return; 774 775 DownloadItem* download = it->second; 776 if (pause == download->is_paused()) 777 return; 778 779 BrowserThread::PostTask( 780 BrowserThread::IO, FROM_HERE, 781 NewRunnableMethod(this, 782 &DownloadManager::PauseDownloadRequest, 783 g_browser_process->resource_dispatcher_host(), 784 download->render_process_id(), 785 download->request_id(), 786 pause)); 787 } 788 789 void DownloadManager::UpdateAppIcon() { 790 if (status_updater_) 791 status_updater_->Update(); 792 } 793 794 void DownloadManager::PauseDownloadRequest(ResourceDispatcherHost* rdh, 795 int render_process_id, 796 int request_id, 797 bool pause) { 798 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 799 rdh->PauseRequest(render_process_id, request_id, pause); 800 } 801 802 void DownloadManager::RemoveDownload(int64 download_handle) { 803 DownloadMap::iterator it = history_downloads_.find(download_handle); 804 if (it == history_downloads_.end()) 805 return; 806 807 // Make history update. 808 DownloadItem* download = it->second; 809 download_history_->RemoveEntry(download); 810 811 // Remove from our tables and delete. 812 history_downloads_.erase(it); 813 int downloads_count = downloads_.erase(download); 814 DCHECK_EQ(1, downloads_count); 815 816 // Tell observers to refresh their views. 817 NotifyModelChanged(); 818 819 delete download; 820 } 821 822 int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin, 823 const base::Time remove_end) { 824 download_history_->RemoveEntriesBetween(remove_begin, remove_end); 825 826 // All downloads visible to the user will be in the history, 827 // so scan that map. 828 DownloadMap::iterator it = history_downloads_.begin(); 829 std::vector<DownloadItem*> pending_deletes; 830 while (it != history_downloads_.end()) { 831 DownloadItem* download = it->second; 832 if (download->start_time() >= remove_begin && 833 (remove_end.is_null() || download->start_time() < remove_end) && 834 (download->IsComplete() || 835 download->IsCancelled() || 836 download->IsInterrupted())) { 837 // Remove from the map and move to the next in the list. 838 history_downloads_.erase(it++); 839 840 // Also remove it from any completed dangerous downloads. 841 pending_deletes.push_back(download); 842 843 continue; 844 } 845 846 ++it; 847 } 848 849 // If we aren't deleting anything, we're done. 850 if (pending_deletes.empty()) 851 return 0; 852 853 // Remove the chosen downloads from the main owning container. 854 for (std::vector<DownloadItem*>::iterator it = pending_deletes.begin(); 855 it != pending_deletes.end(); it++) { 856 downloads_.erase(*it); 857 } 858 859 // Tell observers to refresh their views. 860 NotifyModelChanged(); 861 862 // Delete the download items themselves. 863 int num_deleted = static_cast<int>(pending_deletes.size()); 864 865 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end()); 866 pending_deletes.clear(); 867 868 return num_deleted; 869 } 870 871 int DownloadManager::RemoveDownloads(const base::Time remove_begin) { 872 return RemoveDownloadsBetween(remove_begin, base::Time()); 873 } 874 875 int DownloadManager::RemoveAllDownloads() { 876 if (this != profile_->GetOriginalProfile()->GetDownloadManager()) { 877 // This is an incognito downloader. Clear All should clear main download 878 // manager as well. 879 profile_->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads(); 880 } 881 // The null times make the date range unbounded. 882 return RemoveDownloadsBetween(base::Time(), base::Time()); 883 } 884 885 void DownloadManager::SavePageAsDownloadStarted(DownloadItem* download_item) { 886 #if !defined(NDEBUG) 887 save_page_as_downloads_.insert(download_item); 888 #endif 889 downloads_.insert(download_item); 890 } 891 892 // Initiate a download of a specific URL. We send the request to the 893 // ResourceDispatcherHost, and let it send us responses like a regular 894 // download. 895 void DownloadManager::DownloadUrl(const GURL& url, 896 const GURL& referrer, 897 const std::string& referrer_charset, 898 TabContents* tab_contents) { 899 DownloadUrlToFile(url, referrer, referrer_charset, DownloadSaveInfo(), 900 tab_contents); 901 } 902 903 void DownloadManager::DownloadUrlToFile(const GURL& url, 904 const GURL& referrer, 905 const std::string& referrer_charset, 906 const DownloadSaveInfo& save_info, 907 TabContents* tab_contents) { 908 DCHECK(tab_contents); 909 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 910 NewRunnableFunction(&download_util::DownloadUrl, 911 url, 912 referrer, 913 referrer_charset, 914 save_info, 915 g_browser_process->resource_dispatcher_host(), 916 tab_contents->GetRenderProcessHost()->id(), 917 tab_contents->render_view_host()->routing_id(), 918 request_context_getter_)); 919 } 920 921 void DownloadManager::AddObserver(Observer* observer) { 922 observers_.AddObserver(observer); 923 observer->ModelChanged(); 924 } 925 926 void DownloadManager::RemoveObserver(Observer* observer) { 927 observers_.RemoveObserver(observer); 928 } 929 930 bool DownloadManager::ShouldOpenFileBasedOnExtension( 931 const FilePath& path) const { 932 FilePath::StringType extension = path.Extension(); 933 if (extension.empty()) 934 return false; 935 if (Extension::IsExtension(path)) 936 return false; 937 DCHECK(extension[0] == FilePath::kExtensionSeparator); 938 extension.erase(0, 1); 939 return download_prefs_->IsAutoOpenEnabledForExtension(extension); 940 } 941 942 bool DownloadManager::IsDownloadProgressKnown() { 943 for (DownloadMap::iterator i = in_progress_.begin(); 944 i != in_progress_.end(); ++i) { 945 if (i->second->total_bytes() <= 0) 946 return false; 947 } 948 949 return true; 950 } 951 952 int64 DownloadManager::GetInProgressDownloadCount() { 953 return in_progress_.size(); 954 } 955 956 int64 DownloadManager::GetReceivedDownloadBytes() { 957 DCHECK(IsDownloadProgressKnown()); 958 int64 received_bytes = 0; 959 for (DownloadMap::iterator i = in_progress_.begin(); 960 i != in_progress_.end(); ++i) { 961 received_bytes += i->second->received_bytes(); 962 } 963 return received_bytes; 964 } 965 966 int64 DownloadManager::GetTotalDownloadBytes() { 967 DCHECK(IsDownloadProgressKnown()); 968 int64 total_bytes = 0; 969 for (DownloadMap::iterator i = in_progress_.begin(); 970 i != in_progress_.end(); ++i) { 971 total_bytes += i->second->total_bytes(); 972 } 973 return total_bytes; 974 } 975 976 void DownloadManager::FileSelected(const FilePath& path, 977 int index, void* params) { 978 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params); 979 if (info->prompt_user_for_save_location) 980 last_download_path_ = path.DirName(); 981 982 info->path = path; 983 AttachDownloadItem(info); 984 } 985 986 void DownloadManager::FileSelectionCanceled(void* params) { 987 // The user didn't pick a place to save the file, so need to cancel the 988 // download that's already in progress to the temporary location. 989 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params); 990 DownloadCancelledInternal(info->download_id, 991 info->child_id, 992 info->request_id); 993 } 994 995 void DownloadManager::DangerousDownloadValidated(DownloadItem* download) { 996 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 997 DCHECK_EQ(DownloadItem::DANGEROUS, download->safety_state()); 998 download->set_safety_state(DownloadItem::DANGEROUS_BUT_VALIDATED); 999 download->UpdateObservers(); 1000 1001 MaybeCompleteDownload(download); 1002 } 1003 1004 // Operations posted to us from the history service ---------------------------- 1005 1006 // The history service has retrieved all download entries. 'entries' contains 1007 // 'DownloadCreateInfo's in sorted order (by ascending start_time). 1008 void DownloadManager::OnQueryDownloadEntriesComplete( 1009 std::vector<DownloadCreateInfo>* entries) { 1010 for (size_t i = 0; i < entries->size(); ++i) { 1011 DownloadItem* download = new DownloadItem(this, entries->at(i)); 1012 DCHECK(!ContainsKey(history_downloads_, download->db_handle())); 1013 downloads_.insert(download); 1014 history_downloads_[download->db_handle()] = download; 1015 VLOG(20) << __FUNCTION__ << "()" << i << ">" 1016 << " download = " << download->DebugString(true); 1017 } 1018 NotifyModelChanged(); 1019 } 1020 1021 // Once the new DownloadItem's creation info has been committed to the history 1022 // service, we associate the DownloadItem with the db handle, update our 1023 // 'history_downloads_' map and inform observers. 1024 void DownloadManager::OnCreateDownloadEntryComplete( 1025 DownloadCreateInfo info, 1026 int64 db_handle) { 1027 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1028 DownloadMap::iterator it = in_progress_.find(info.download_id); 1029 DCHECK(it != in_progress_.end()); 1030 1031 DownloadItem* download = it->second; 1032 VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle 1033 << " download_id = " << info.download_id 1034 << " download = " << download->DebugString(true); 1035 1036 // It's not immediately obvious, but HistoryBackend::CreateDownload() can 1037 // call this function with an invalid |db_handle|. For instance, this can 1038 // happen when the history database is offline. We cannot have multiple 1039 // DownloadItems with the same invalid db_handle, so we need to assign a 1040 // unique |db_handle| here. 1041 if (db_handle == DownloadHistory::kUninitializedHandle) 1042 db_handle = download_history_->GetNextFakeDbHandle(); 1043 1044 DCHECK(download->db_handle() == DownloadHistory::kUninitializedHandle); 1045 download->set_db_handle(db_handle); 1046 1047 DCHECK(!ContainsKey(history_downloads_, download->db_handle())); 1048 history_downloads_[download->db_handle()] = download; 1049 1050 // Show in the appropriate browser UI. 1051 // This includes buttons to save or cancel, for a dangerous download. 1052 ShowDownloadInBrowser(info, download); 1053 1054 // Inform interested objects about the new download. 1055 NotifyModelChanged(); 1056 1057 // If the download is still in progress, try to complete it. 1058 // 1059 // Otherwise, download has been cancelled or interrupted before we've 1060 // received the DB handle. We post one final message to the history 1061 // service so that it can be properly in sync with the DownloadItem's 1062 // completion status, and also inform any observers so that they get 1063 // more than just the start notification. 1064 if (download->IsInProgress()) { 1065 MaybeCompleteDownload(download); 1066 } else { 1067 DCHECK(download->IsCancelled()) 1068 << " download = " << download->DebugString(true); 1069 in_progress_.erase(it); 1070 active_downloads_.erase(info.download_id); 1071 download_history_->UpdateEntry(download); 1072 download->UpdateObservers(); 1073 } 1074 } 1075 1076 void DownloadManager::ShowDownloadInBrowser(const DownloadCreateInfo& info, 1077 DownloadItem* download) { 1078 // The 'contents' may no longer exist if the user closed the tab before we 1079 // get this start completion event. If it does, tell the origin TabContents 1080 // to display its download shelf. 1081 TabContents* contents = tab_util::GetTabContentsByID(info.child_id, 1082 info.render_view_id); 1083 1084 // If the contents no longer exists, we start the download in the last active 1085 // browser. This is not ideal but better than fully hiding the download from 1086 // the user. 1087 if (!contents) { 1088 Browser* last_active = BrowserList::GetLastActive(); 1089 if (last_active) 1090 contents = last_active->GetSelectedTabContents(); 1091 } 1092 1093 if (contents) 1094 contents->OnStartDownload(download); 1095 } 1096 1097 // Clears the last download path, used to initialize "save as" dialogs. 1098 void DownloadManager::ClearLastDownloadPath() { 1099 last_download_path_ = FilePath(); 1100 } 1101 1102 void DownloadManager::NotifyModelChanged() { 1103 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged()); 1104 } 1105 1106 DownloadItem* DownloadManager::GetDownloadItem(int id) { 1107 for (DownloadMap::iterator it = history_downloads_.begin(); 1108 it != history_downloads_.end(); ++it) { 1109 DownloadItem* item = it->second; 1110 if (item->id() == id) 1111 return item; 1112 } 1113 return NULL; 1114 } 1115 1116 // Confirm that everything in all maps is also in |downloads_|, and that 1117 // everything in |downloads_| is also in some other map. 1118 void DownloadManager::AssertContainersConsistent() const { 1119 #if !defined(NDEBUG) 1120 // Turn everything into sets. 1121 DownloadSet active_set, history_set; 1122 const DownloadMap* input_maps[] = {&active_downloads_, &history_downloads_}; 1123 DownloadSet* local_sets[] = {&active_set, &history_set}; 1124 DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(local_sets)); 1125 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input_maps); i++) { 1126 for (DownloadMap::const_iterator it = input_maps[i]->begin(); 1127 it != input_maps[i]->end(); it++) { 1128 local_sets[i]->insert(&*it->second); 1129 } 1130 } 1131 1132 // Check if each set is fully present in downloads, and create a union. 1133 const DownloadSet* all_sets[] = {&active_set, &history_set, 1134 &save_page_as_downloads_}; 1135 DownloadSet downloads_union; 1136 for (int i = 0; i < static_cast<int>(ARRAYSIZE_UNSAFE(all_sets)); i++) { 1137 DownloadSet remainder; 1138 std::insert_iterator<DownloadSet> insert_it(remainder, remainder.begin()); 1139 std::set_difference(all_sets[i]->begin(), all_sets[i]->end(), 1140 downloads_.begin(), downloads_.end(), 1141 insert_it); 1142 DCHECK(remainder.empty()); 1143 std::insert_iterator<DownloadSet> 1144 insert_union(downloads_union, downloads_union.end()); 1145 std::set_union(downloads_union.begin(), downloads_union.end(), 1146 all_sets[i]->begin(), all_sets[i]->end(), 1147 insert_union); 1148 } 1149 1150 // Is everything in downloads_ present in one of the other sets? 1151 DownloadSet remainder; 1152 std::insert_iterator<DownloadSet> 1153 insert_remainder(remainder, remainder.begin()); 1154 std::set_difference(downloads_.begin(), downloads_.end(), 1155 downloads_union.begin(), downloads_union.end(), 1156 insert_remainder); 1157 DCHECK(remainder.empty()); 1158 #endif 1159 } 1160 1161 // DownloadManager::OtherDownloadManagerObserver implementation ---------------- 1162 1163 DownloadManager::OtherDownloadManagerObserver::OtherDownloadManagerObserver( 1164 DownloadManager* observing_download_manager) 1165 : observing_download_manager_(observing_download_manager), 1166 observed_download_manager_(NULL) { 1167 if (observing_download_manager->profile_->GetOriginalProfile() == 1168 observing_download_manager->profile_) { 1169 return; 1170 } 1171 1172 observed_download_manager_ = observing_download_manager_-> 1173 profile_->GetOriginalProfile()->GetDownloadManager(); 1174 observed_download_manager_->AddObserver(this); 1175 } 1176 1177 DownloadManager::OtherDownloadManagerObserver::~OtherDownloadManagerObserver() { 1178 if (observed_download_manager_) 1179 observed_download_manager_->RemoveObserver(this); 1180 } 1181 1182 void DownloadManager::OtherDownloadManagerObserver::ModelChanged() { 1183 observing_download_manager_->NotifyModelChanged(); 1184 } 1185 1186 void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() { 1187 observed_download_manager_ = NULL; 1188 } 1189