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