• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • 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 
DownloadManager(DownloadStatusUpdater * status_updater)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 
~DownloadManager()59 DownloadManager::~DownloadManager() {
60   DCHECK(!shutdown_needed_);
61   if (status_updater_)
62     status_updater_->RemoveDelegate(this);
63 }
64 
Shutdown()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 
GetTemporaryDownloads(const FilePath & dir_path,std::vector<DownloadItem * > * result)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 
GetAllDownloads(const FilePath & dir_path,std::vector<DownloadItem * > * result)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 
GetCurrentDownloads(const FilePath & dir_path,std::vector<DownloadItem * > * result)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 
SearchDownloads(const string16 & query,std::vector<DownloadItem * > * result)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.
Init(Profile * profile)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.
StartDownload(DownloadCreateInfo * info)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 
CheckDownloadUrlDone(DownloadCreateInfo * info,bool is_dangerous_url)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 
CheckIfSuggestedPathExists(DownloadCreateInfo * info,const FilePath & default_path)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 
OnPathExistenceAvailable(DownloadCreateInfo * info)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 
CreateDownloadItem(DownloadCreateInfo * info)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 
AttachDownloadItem(DownloadCreateInfo * info)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 
UpdateDownload(int32 download_id,int64 size)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 
OnResponseCompleted(int32 download_id,int64 size,int os_error,const std::string & hash)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 
OnAllDataSaved(int32 download_id,int64 size,const std::string & hash)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.
CheckDownloadHashDone(int32 download_id,bool is_dangerous_hash)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 
IsDownloadReadyForCompletion(DownloadItem * download)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 
MaybeCompleteDownload(DownloadItem * download)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 
DownloadCompleted(int32 download_id)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 
OnDownloadRenamedToFinalName(int download_id,const FilePath & full_path,int uniquifier)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 
DownloadCancelled(int32 download_id)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 
DownloadCancelledInternal(int download_id,int render_process_id,int request_id)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 
OnDownloadError(int32 download_id,int64 size,int os_error)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 
PauseDownload(int32 download_id,bool pause)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 
UpdateAppIcon()789 void DownloadManager::UpdateAppIcon() {
790   if (status_updater_)
791     status_updater_->Update();
792 }
793 
PauseDownloadRequest(ResourceDispatcherHost * rdh,int render_process_id,int request_id,bool pause)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 
RemoveDownload(int64 download_handle)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 
RemoveDownloadsBetween(const base::Time remove_begin,const base::Time remove_end)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 
RemoveDownloads(const base::Time remove_begin)871 int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
872   return RemoveDownloadsBetween(remove_begin, base::Time());
873 }
874 
RemoveAllDownloads()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 
SavePageAsDownloadStarted(DownloadItem * download_item)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.
DownloadUrl(const GURL & url,const GURL & referrer,const std::string & referrer_charset,TabContents * tab_contents)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 
DownloadUrlToFile(const GURL & url,const GURL & referrer,const std::string & referrer_charset,const DownloadSaveInfo & save_info,TabContents * tab_contents)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 
AddObserver(Observer * observer)921 void DownloadManager::AddObserver(Observer* observer) {
922   observers_.AddObserver(observer);
923   observer->ModelChanged();
924 }
925 
RemoveObserver(Observer * observer)926 void DownloadManager::RemoveObserver(Observer* observer) {
927   observers_.RemoveObserver(observer);
928 }
929 
ShouldOpenFileBasedOnExtension(const FilePath & path) const930 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 
IsDownloadProgressKnown()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 
GetInProgressDownloadCount()952 int64 DownloadManager::GetInProgressDownloadCount() {
953   return in_progress_.size();
954 }
955 
GetReceivedDownloadBytes()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 
GetTotalDownloadBytes()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 
FileSelected(const FilePath & path,int index,void * params)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 
FileSelectionCanceled(void * params)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 
DangerousDownloadValidated(DownloadItem * download)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).
OnQueryDownloadEntriesComplete(std::vector<DownloadCreateInfo> * entries)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.
OnCreateDownloadEntryComplete(DownloadCreateInfo info,int64 db_handle)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 
ShowDownloadInBrowser(const DownloadCreateInfo & info,DownloadItem * download)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.
ClearLastDownloadPath()1098 void DownloadManager::ClearLastDownloadPath() {
1099   last_download_path_ = FilePath();
1100 }
1101 
NotifyModelChanged()1102 void DownloadManager::NotifyModelChanged() {
1103   FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
1104 }
1105 
GetDownloadItem(int id)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.
AssertContainersConsistent() const1118 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 
OtherDownloadManagerObserver(DownloadManager * observing_download_manager)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 
~OtherDownloadManagerObserver()1177 DownloadManager::OtherDownloadManagerObserver::~OtherDownloadManagerObserver() {
1178   if (observed_download_manager_)
1179     observed_download_manager_->RemoveObserver(this);
1180 }
1181 
ModelChanged()1182 void DownloadManager::OtherDownloadManagerObserver::ModelChanged() {
1183   observing_download_manager_->NotifyModelChanged();
1184 }
1185 
ManagerGoingDown()1186 void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() {
1187   observed_download_manager_ = NULL;
1188 }
1189