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