Home | History | Annotate | Download | only in download
      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