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