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
50DownloadManager::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
59DownloadManager::~DownloadManager() {
60  DCHECK(!shutdown_needed_);
61  if (status_updater_)
62    status_updater_->RemoveDelegate(this);
63}
64
65void 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
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    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
190void 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.
220bool 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.
256void 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
267void 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
337void 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
425void 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
459void 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
470void 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
524void 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
537void 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
554void 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.
586void 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
601bool 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
623void 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
656void 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
664void 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
692void 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
716void 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
734void 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
770void 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
789void DownloadManager::UpdateAppIcon() {
790  if (status_updater_)
791    status_updater_->Update();
792}
793
794void 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
802void 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
822int 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
871int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
872  return RemoveDownloadsBetween(remove_begin, base::Time());
873}
874
875int 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
885void 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.
895void 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
903void 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
921void DownloadManager::AddObserver(Observer* observer) {
922  observers_.AddObserver(observer);
923  observer->ModelChanged();
924}
925
926void DownloadManager::RemoveObserver(Observer* observer) {
927  observers_.RemoveObserver(observer);
928}
929
930bool 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
942bool 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
952int64 DownloadManager::GetInProgressDownloadCount() {
953  return in_progress_.size();
954}
955
956int64 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
966int64 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
976void 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
986void 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
995void 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).
1008void 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.
1024void 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
1076void 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.
1098void DownloadManager::ClearLastDownloadPath() {
1099  last_download_path_ = FilePath();
1100}
1101
1102void DownloadManager::NotifyModelChanged() {
1103  FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
1104}
1105
1106DownloadItem* 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.
1118void 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
1163DownloadManager::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
1177DownloadManager::OtherDownloadManagerObserver::~OtherDownloadManagerObserver() {
1178  if (observed_download_manager_)
1179    observed_download_manager_->RemoveObserver(this);
1180}
1181
1182void DownloadManager::OtherDownloadManagerObserver::ModelChanged() {
1183  observing_download_manager_->NotifyModelChanged();
1184}
1185
1186void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() {
1187  observed_download_manager_ = NULL;
1188}
1189