blob: 5fcd9d4ab86fc2a6d7461d9cd499656e6610a8f1 [file] [log] [blame]
[email protected]f4f50ef2011-01-21 19:01:191// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]cdaa8652008-09-13 02:48:595#include "chrome/browser/download/download_manager.h"
initial.commit09911bf2008-07-26 23:55:296
[email protected]2041cf342010-02-19 03:15:597#include "base/callback.h"
initial.commit09911bf2008-07-26 23:55:298#include "base/file_util.h"
[email protected]503d03872011-05-06 08:36:269#include "base/i18n/case_conversion.h"
initial.commit09911bf2008-07-26 23:55:2910#include "base/logging.h"
[email protected]fed38252011-07-08 17:26:5011#include "base/path_service.h"
[email protected]1b5044d2009-02-24 00:04:1412#include "base/rand_util.h"
[email protected]7286e3fc2011-07-19 22:13:2413#include "base/stl_util.h"
[email protected]2594c2b2010-11-08 23:04:2614#include "base/stringprintf.h"
[email protected]1b5044d2009-02-24 00:04:1415#include "base/sys_string_conversions.h"
initial.commit09911bf2008-07-26 23:55:2916#include "base/task.h"
[email protected]ce7f62e32010-08-10 23:43:5917#include "base/utf_string_conversions.h"
[email protected]d2a8fb72010-01-21 05:31:4218#include "build/build_config.h"
initial.commit09911bf2008-07-26 23:55:2919#include "chrome/browser/browser_process.h"
[email protected]4cd82f72011-05-23 19:15:0120#include "chrome/browser/download/download_create_info.h"
[email protected]cd448092010-12-06 23:49:1321#include "chrome/browser/download/download_extensions.h"
[email protected]6c69796d2010-07-16 21:41:1622#include "chrome/browser/download/download_file_manager.h"
[email protected]82f37b02010-07-29 22:04:5723#include "chrome/browser/download/download_history.h"
[email protected]6c69796d2010-07-16 21:41:1624#include "chrome/browser/download/download_item.h"
[email protected]da1a27b2011-07-29 23:16:3325#include "chrome/browser/download/download_manager_delegate.h"
[email protected]e5dc4222010-08-30 22:16:3226#include "chrome/browser/download/download_prefs.h"
[email protected]db6831a2011-06-09 21:08:2827#include "chrome/browser/download/download_request_handle.h"
[email protected]287b86b2011-02-26 00:11:3528#include "chrome/browser/download/download_safe_browsing_client.h"
[email protected]073ed7b2010-09-27 09:20:0229#include "chrome/browser/download/download_status_updater.h"
[email protected]e9ef0a62009-08-11 22:50:1330#include "chrome/browser/download/download_util.h"
[email protected]eaa7dd182010-12-14 11:09:0031#include "chrome/browser/extensions/extension_service.h"
[email protected]4cd82f72011-05-23 19:15:0132#include "chrome/browser/history/download_history_info.h"
[email protected]8ecad5e2010-12-02 21:18:3333#include "chrome/browser/profiles/profile.h"
[email protected]d2a8fb72010-01-21 05:31:4234#include "chrome/browser/tab_contents/tab_util.h"
initial.commit09911bf2008-07-26 23:55:2935#include "chrome/common/chrome_paths.h"
[email protected]8c40da62011-07-13 22:58:4636#include "chrome/common/pref_names.h"
[email protected]7324d1d2011-03-01 05:02:1637#include "content/browser/browser_thread.h"
[email protected]7324d1d2011-03-01 05:02:1638#include "content/browser/renderer_host/render_process_host.h"
39#include "content/browser/renderer_host/render_view_host.h"
40#include "content/browser/renderer_host/resource_dispatcher_host.h"
41#include "content/browser/tab_contents/tab_contents.h"
[email protected]432115822011-07-10 15:52:2742#include "content/common/content_notification_types.h"
[email protected]6d0146c2011-08-04 19:13:0443#include "content/common/notification_service.h"
[email protected]46072d42008-07-28 14:49:3544#include "googleurl/src/gurl.h"
[email protected]34ac8f32009-02-22 23:03:2745#include "grit/generated_resources.h"
[email protected]21ca982c2010-01-26 22:49:5546#include "grit/theme_resources.h"
initial.commit09911bf2008-07-26 23:55:2947#include "net/base/mime_util.h"
48#include "net/base/net_util.h"
[email protected]c051a1b2011-01-21 23:30:1749#include "ui/base/l10n/l10n_util.h"
[email protected]42ce29d2011-01-20 23:19:4650#include "ui/base/resource/resource_bundle.h"
initial.commit09911bf2008-07-26 23:55:2951
[email protected]da1a27b2011-07-29 23:16:3352DownloadManager::DownloadManager(DownloadManagerDelegate* delegate,
53 DownloadStatusUpdater* status_updater)
initial.commit09911bf2008-07-26 23:55:2954 : shutdown_needed_(false),
55 profile_(NULL),
[email protected]073ed7b2010-09-27 09:20:0256 file_manager_(NULL),
[email protected]da1a27b2011-07-29 23:16:3357 status_updater_(status_updater->AsWeakPtr()),
[email protected]6d0146c2011-08-04 19:13:0458 next_save_page_id_(0),
[email protected]da1a27b2011-07-29 23:16:3359 delegate_(delegate) {
[email protected]073ed7b2010-09-27 09:20:0260 if (status_updater_)
61 status_updater_->AddDelegate(this);
initial.commit09911bf2008-07-26 23:55:2962}
63
64DownloadManager::~DownloadManager() {
[email protected]326a6a92010-09-10 20:21:1365 DCHECK(!shutdown_needed_);
[email protected]073ed7b2010-09-27 09:20:0266 if (status_updater_)
67 status_updater_->RemoveDelegate(this);
initial.commit09911bf2008-07-26 23:55:2968}
69
70void DownloadManager::Shutdown() {
[email protected]da6e3922010-11-24 21:45:5071 VLOG(20) << __FUNCTION__ << "()"
72 << " shutdown_needed_ = " << shutdown_needed_;
[email protected]326a6a92010-09-10 20:21:1373 if (!shutdown_needed_)
74 return;
75 shutdown_needed_ = false;
initial.commit09911bf2008-07-26 23:55:2976
[email protected]326a6a92010-09-10 20:21:1377 FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown());
78
79 if (file_manager_) {
[email protected]ca4b5fa32010-10-09 12:42:1880 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
[email protected]326a6a92010-09-10 20:21:1381 NewRunnableMethod(file_manager_,
82 &DownloadFileManager::OnDownloadManagerShutdown,
[email protected]dc7cdcb92010-12-14 06:40:5483 make_scoped_refptr(this)));
[email protected]326a6a92010-09-10 20:21:1384 }
initial.commit09911bf2008-07-26 23:55:2985
[email protected]f04182f32010-12-10 19:12:0786 AssertContainersConsistent();
87
88 // Go through all downloads in downloads_. Dangerous ones we need to
89 // remove on disk, and in progress ones we need to cancel.
[email protected]57fd1252010-12-23 17:24:0990 for (DownloadSet::iterator it = downloads_.begin(); it != downloads_.end();) {
[email protected]f04182f32010-12-10 19:12:0791 DownloadItem* download = *it;
92
93 // Save iterator from potential erases in this set done by called code.
94 // Iterators after an erasure point are still valid for lists and
95 // associative containers such as sets.
96 it++;
97
98 if (download->safety_state() == DownloadItem::DANGEROUS &&
[email protected]48837962011-04-19 17:03:2999 download->IsPartialDownload()) {
[email protected]f04182f32010-12-10 19:12:07100 // The user hasn't accepted it, so we need to remove it
101 // from the disk. This may or may not result in it being
102 // removed from the DownloadManager queues and deleted
103 // (specifically, DownloadManager::RemoveDownload only
104 // removes and deletes it if it's known to the history service)
105 // so the only thing we know after calling this function is that
106 // the download was deleted if-and-only-if it was removed
107 // from all queues.
[email protected]303077002011-04-19 23:21:01108 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN);
[email protected]bf68a00b2011-04-07 17:28:26109 } else if (download->IsPartialDownload()) {
[email protected]54610672011-07-18 18:24:43110 download->Cancel(false);
111 download_history_->UpdateEntry(download);
initial.commit09911bf2008-07-26 23:55:29112 }
113 }
114
[email protected]f04182f32010-12-10 19:12:07115 // At this point, all dangerous downloads have had their files removed
116 // and all in progress downloads have been cancelled. We can now delete
117 // anything left.
[email protected]9ccbb372008-10-10 18:50:32118
[email protected]5cd11b6e2011-06-10 20:30:59119 // Copy downloads_ to separate container so as not to set off checks
120 // in DownloadItem destruction.
121 DownloadSet downloads_to_delete;
122 downloads_to_delete.swap(downloads_);
123
initial.commit09911bf2008-07-26 23:55:29124 in_progress_.clear();
[email protected]70850c72011-01-11 17:31:27125 active_downloads_.clear();
[email protected]5cd11b6e2011-06-10 20:30:59126 history_downloads_.clear();
[email protected]5cd11b6e2011-06-10 20:30:59127 STLDeleteElements(&downloads_to_delete);
initial.commit09911bf2008-07-26 23:55:29128
[email protected]6d0146c2011-08-04 19:13:04129 DCHECK(save_page_downloads_.empty());
130
initial.commit09911bf2008-07-26 23:55:29131 file_manager_ = NULL;
132
[email protected]82f37b02010-07-29 22:04:57133 download_history_.reset();
[email protected]68a49e52011-01-28 10:39:51134 download_prefs_.reset();
[email protected]82f37b02010-07-29 22:04:57135
initial.commit09911bf2008-07-26 23:55:29136 shutdown_needed_ = false;
137}
138
[email protected]82f37b02010-07-29 22:04:57139void DownloadManager::GetTemporaryDownloads(
[email protected]6d0146c2011-08-04 19:13:04140 const FilePath& dir_path, DownloadVector* result) {
[email protected]82f37b02010-07-29 22:04:57141 DCHECK(result);
[email protected]6aa4a1c02010-01-15 18:49:58142
[email protected]f04182f32010-12-10 19:12:07143 for (DownloadMap::iterator it = history_downloads_.begin();
144 it != history_downloads_.end(); ++it) {
[email protected]6aa4a1c02010-01-15 18:49:58145 if (it->second->is_temporary() &&
146 it->second->full_path().DirName() == dir_path)
[email protected]82f37b02010-07-29 22:04:57147 result->push_back(it->second);
[email protected]6aa4a1c02010-01-15 18:49:58148 }
[email protected]6aa4a1c02010-01-15 18:49:58149}
150
[email protected]82f37b02010-07-29 22:04:57151void DownloadManager::GetAllDownloads(
[email protected]6d0146c2011-08-04 19:13:04152 const FilePath& dir_path, DownloadVector* result) {
[email protected]82f37b02010-07-29 22:04:57153 DCHECK(result);
[email protected]8ddbd66a2010-05-21 16:38:34154
[email protected]f04182f32010-12-10 19:12:07155 for (DownloadMap::iterator it = history_downloads_.begin();
156 it != history_downloads_.end(); ++it) {
[email protected]8ddbd66a2010-05-21 16:38:34157 if (!it->second->is_temporary() &&
158 (dir_path.empty() || it->second->full_path().DirName() == dir_path))
[email protected]82f37b02010-07-29 22:04:57159 result->push_back(it->second);
[email protected]8ddbd66a2010-05-21 16:38:34160 }
[email protected]8ddbd66a2010-05-21 16:38:34161}
162
[email protected]82f37b02010-07-29 22:04:57163void DownloadManager::GetCurrentDownloads(
[email protected]6d0146c2011-08-04 19:13:04164 const FilePath& dir_path, DownloadVector* result) {
[email protected]82f37b02010-07-29 22:04:57165 DCHECK(result);
[email protected]c4a530b2010-03-08 17:33:03166
[email protected]f04182f32010-12-10 19:12:07167 for (DownloadMap::iterator it = history_downloads_.begin();
168 it != history_downloads_.end(); ++it) {
[email protected]bf68a00b2011-04-07 17:28:26169 DownloadItem* item =it->second;
170 // Skip temporary items.
171 if (item->is_temporary())
172 continue;
173 // Skip items that have all their data, and are OK to save.
174 if (!item->IsPartialDownload() &&
175 (item->safety_state() != DownloadItem::DANGEROUS))
176 continue;
177 // Skip items that don't match |dir_path|.
178 // If |dir_path| is empty, all remaining items match.
179 if (!dir_path.empty() && (it->second->full_path().DirName() != dir_path))
180 continue;
181
182 result->push_back(item);
[email protected]c4a530b2010-03-08 17:33:03183 }
[email protected]f7e9fd62010-09-28 15:45:06184
185 // If we have a parent profile, let it add its downloads to the results.
186 Profile* original_profile = profile_->GetOriginalProfile();
187 if (original_profile != profile_)
188 original_profile->GetDownloadManager()->GetCurrentDownloads(dir_path,
189 result);
[email protected]c4a530b2010-03-08 17:33:03190}
191
[email protected]d3b12902010-08-16 23:39:42192void DownloadManager::SearchDownloads(const string16& query,
[email protected]6d0146c2011-08-04 19:13:04193 DownloadVector* result) {
[email protected]d3b12902010-08-16 23:39:42194 DCHECK(result);
195
[email protected]503d03872011-05-06 08:36:26196 string16 query_lower(base::i18n::ToLower(query));
[email protected]d3b12902010-08-16 23:39:42197
[email protected]f04182f32010-12-10 19:12:07198 for (DownloadMap::iterator it = history_downloads_.begin();
199 it != history_downloads_.end(); ++it) {
[email protected]d3b12902010-08-16 23:39:42200 DownloadItem* download_item = it->second;
201
202 if (download_item->is_temporary() || download_item->is_extension_install())
203 continue;
204
205 // Display Incognito downloads only in Incognito window, and vice versa.
206 // The Incognito Downloads page will get the list of non-Incognito downloads
207 // from its parent profile.
208 if (profile_->IsOffTheRecord() != download_item->is_otr())
209 continue;
210
211 if (download_item->MatchesQuery(query_lower))
212 result->push_back(download_item);
213 }
214
215 // If we have a parent profile, let it add its downloads to the results.
216 Profile* original_profile = profile_->GetOriginalProfile();
217 if (original_profile != profile_)
218 original_profile->GetDownloadManager()->SearchDownloads(query, result);
219}
220
initial.commit09911bf2008-07-26 23:55:29221// Query the history service for information about all persisted downloads.
222bool DownloadManager::Init(Profile* profile) {
223 DCHECK(profile);
224 DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
225 shutdown_needed_ = true;
226
227 profile_ = profile;
[email protected]d3b12902010-08-16 23:39:42228 download_history_.reset(new DownloadHistory(profile));
[email protected]82f37b02010-07-29 22:04:57229 download_history_->Load(
230 NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete));
[email protected]024f2f02010-04-30 22:51:46231
[email protected]e5dc4222010-08-30 22:16:32232 download_prefs_.reset(new DownloadPrefs(profile_->GetPrefs()));
233
[email protected]2941c2392010-07-15 22:54:30234 // In test mode, there may be no ResourceDispatcherHost. In this case it's
235 // safe to avoid setting |file_manager_| because we only call a small set of
236 // functions, none of which need it.
initial.commit09911bf2008-07-26 23:55:29237 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
[email protected]2941c2392010-07-15 22:54:30238 if (rdh) {
239 file_manager_ = rdh->download_file_manager();
240 DCHECK(file_manager_);
initial.commit09911bf2008-07-26 23:55:29241 }
242
[email protected]b0ab1d42010-02-24 19:29:28243 other_download_manager_observer_.reset(
244 new OtherDownloadManagerObserver(this));
245
initial.commit09911bf2008-07-26 23:55:29246 return true;
247}
248
initial.commit09911bf2008-07-26 23:55:29249// We have received a message from DownloadFileManager about a new download. We
250// create a download item and store it in our download map, and inform the
251// history system of a new download. Since this method can be called while the
252// history service thread is still reading the persistent state, we do not
[email protected]f04182f32010-12-10 19:12:07253// insert the new DownloadItem into 'history_downloads_' or inform our
[email protected]adb2f3d12011-01-23 16:24:54254// observers at this point. OnCreateDownloadEntryComplete() handles that
[email protected]f04182f32010-12-10 19:12:07255// finalization of the the download creation as a callback from the
256// history thread.
[email protected]4cd82f72011-05-23 19:15:01257void DownloadManager::StartDownload(int32 download_id) {
[email protected]ca4b5fa32010-10-09 12:42:18258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]287b86b2011-02-26 00:11:35259
[email protected]4cd82f72011-05-23 19:15:01260 DownloadItem* download = GetActiveDownloadItem(download_id);
261 if (!download)
262 return;
263
[email protected]4b58e7d2011-07-11 10:22:56264#if defined(ENABLE_SAFE_BROWSING)
[email protected]287b86b2011-02-26 00:11:35265 // Create a client to verify download URL with safebrowsing.
266 // It deletes itself after the callback.
[email protected]26711732011-03-09 00:21:22267 scoped_refptr<DownloadSBClient> sb_client = new DownloadSBClient(
[email protected]8c40da62011-07-13 22:58:46268 download_id, download->url_chain(), download->referrer_url(),
269 profile_->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled));
[email protected]287b86b2011-02-26 00:11:35270 sb_client->CheckDownloadUrl(
[email protected]4cd82f72011-05-23 19:15:01271 NewCallback(this, &DownloadManager::CheckDownloadUrlDone));
[email protected]4b58e7d2011-07-11 10:22:56272#else
273 CheckDownloadUrlDone(download_id, false);
274#endif
[email protected]287b86b2011-02-26 00:11:35275}
276
[email protected]9fc114672011-06-15 08:17:48277void DownloadManager::CheckForHistoryFilesRemoval() {
278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
279 for (DownloadMap::iterator it = history_downloads_.begin();
280 it != history_downloads_.end(); ++it) {
281 CheckForFileRemoval(it->second);
282 }
283}
284
285void DownloadManager::CheckForFileRemoval(DownloadItem* download_item) {
286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287 if (download_item->IsComplete() &&
288 !download_item->file_externally_removed()) {
289 BrowserThread::PostTask(
290 BrowserThread::FILE, FROM_HERE,
291 NewRunnableMethod(this,
292 &DownloadManager::CheckForFileRemovalOnFileThread,
293 download_item->db_handle(),
294 download_item->GetTargetFilePath()));
295 }
296}
297
298void DownloadManager::CheckForFileRemovalOnFileThread(
299 int64 db_handle, const FilePath& path) {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
301 if (!file_util::PathExists(path)) {
302 BrowserThread::PostTask(
303 BrowserThread::UI, FROM_HERE,
304 NewRunnableMethod(this,
305 &DownloadManager::OnFileRemovalDetected,
306 db_handle));
307 }
308}
309
310void DownloadManager::OnFileRemovalDetected(int64 db_handle) {
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
312 DownloadMap::iterator it = history_downloads_.find(db_handle);
313 if (it != history_downloads_.end()) {
314 DownloadItem* download_item = it->second;
315 download_item->OnDownloadedFileRemoved();
316 }
317}
318
[email protected]4cd82f72011-05-23 19:15:01319void DownloadManager::CheckDownloadUrlDone(int32 download_id,
[email protected]287b86b2011-02-26 00:11:35320 bool is_dangerous_url) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
initial.commit09911bf2008-07-26 23:55:29322
[email protected]4cd82f72011-05-23 19:15:01323 DownloadItem* download = GetActiveDownloadItem(download_id);
324 if (!download)
325 return;
326
327 if (is_dangerous_url)
328 download->MarkUrlDangerous();
329
[email protected]88008002011-05-24 23:14:15330 download_history_->CheckVisitedReferrerBefore(download_id,
331 download->referrer_url(),
332 NewCallback(this, &DownloadManager::CheckVisitedReferrerBeforeDone));
333}
334
335void DownloadManager::CheckVisitedReferrerBeforeDone(
336 int32 download_id,
337 bool visited_referrer_before) {
338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
339
340 DownloadItem* download = GetActiveDownloadItem(download_id);
341 if (!download)
342 return;
[email protected]287b86b2011-02-26 00:11:35343
[email protected]a60c8ae2009-12-25 06:50:57344 // Check whether this download is for an extension install or not.
[email protected]e6875c12010-07-18 11:14:13345 // Allow extensions to be explicitly saved.
[email protected]88008002011-05-24 23:14:15346 DownloadStateInfo state = download->state_info();
[email protected]4cd82f72011-05-23 19:15:01347 if (!state.prompt_user_for_save_location) {
348 if (UserScript::IsURLUserScript(download->GetURL(),
349 download->mime_type()) ||
350 (download->mime_type() == Extension::kMimeType)) {
351 state.is_extension_install = true;
[email protected]bac4f4b2011-03-05 02:01:40352 }
[email protected]a60c8ae2009-12-25 06:50:57353 }
354
[email protected]4cd82f72011-05-23 19:15:01355 if (state.force_file_name.empty()) {
[email protected]2941c2392010-07-15 22:54:30356 FilePath generated_name;
[email protected]0622875ab2011-07-27 12:10:34357 download_util::GenerateFileNameFromRequest(*download,
[email protected]4cd82f72011-05-23 19:15:01358 &generated_name);
[email protected]2941c2392010-07-15 22:54:30359
360 // Freeze the user's preference for showing a Save As dialog. We're going
361 // to bounce around a bunch of threads and we don't want to worry about race
362 // conditions where the user changes this pref out from under us.
[email protected]0e9b6072011-02-17 12:42:05363 if (download_prefs_->PromptForDownload()) {
[email protected]2941c2392010-07-15 22:54:30364 // But ignore the user's preference for the following scenarios:
365 // 1) Extension installation. Note that we only care here about the case
366 // where an extension is installed, not when one is downloaded with
367 // "save as...".
368 // 2) Filetypes marked "always open." If the user just wants this file
369 // opened, don't bother asking where to keep it.
[email protected]4cd82f72011-05-23 19:15:01370 if (!state.is_extension_install &&
[email protected]2941c2392010-07-15 22:54:30371 !ShouldOpenFileBasedOnExtension(generated_name))
[email protected]4cd82f72011-05-23 19:15:01372 state.prompt_user_for_save_location = true;
[email protected]2941c2392010-07-15 22:54:30373 }
[email protected]14e0a102011-02-22 14:04:01374 if (download_prefs_->IsDownloadPathManaged()) {
[email protected]4cd82f72011-05-23 19:15:01375 state.prompt_user_for_save_location = false;
[email protected]14e0a102011-02-22 14:04:01376 }
[email protected]2941c2392010-07-15 22:54:30377
[email protected]8af9d032010-02-10 00:00:32378 // Determine the proper path for a download, by either one of the following:
379 // 1) using the default download directory.
380 // 2) prompting the user.
[email protected]4cd82f72011-05-23 19:15:01381 if (state.prompt_user_for_save_location && !last_download_path_.empty()) {
382 state.suggested_path = last_download_path_;
[email protected]80dc3612010-07-27 19:35:08383 } else {
[email protected]4cd82f72011-05-23 19:15:01384 state.suggested_path = download_prefs_->download_path();
[email protected]80dc3612010-07-27 19:35:08385 }
[email protected]4cd82f72011-05-23 19:15:01386 state.suggested_path = state.suggested_path.Append(generated_name);
[email protected]8af9d032010-02-10 00:00:32387 } else {
[email protected]4cd82f72011-05-23 19:15:01388 state.suggested_path = state.force_file_name;
[email protected]8af9d032010-02-10 00:00:32389 }
initial.commit09911bf2008-07-26 23:55:29390
[email protected]88008002011-05-24 23:14:15391 if (!state.prompt_user_for_save_location && state.force_file_name.empty()) {
392 state.is_dangerous_file =
[email protected]da1a27b2011-07-29 23:16:33393 IsDangerousFile(*download, state, visited_referrer_before);
[email protected]88008002011-05-24 23:14:15394 }
[email protected]e9ebf3fc2008-10-17 22:06:58395
initial.commit09911bf2008-07-26 23:55:29396 // We need to move over to the download thread because we don't want to stat
397 // the suggested path on the UI thread.
[email protected]5a3b97e2010-10-05 09:49:11398 // We can only access preferences on the UI thread, so check the download path
399 // now and pass the value to the FILE thread.
[email protected]ca4b5fa32010-10-09 12:42:18400 BrowserThread::PostTask(
401 BrowserThread::FILE, FROM_HERE,
[email protected]d83d03aa2009-11-02 21:44:37402 NewRunnableMethod(
[email protected]5a3b97e2010-10-05 09:49:11403 this,
404 &DownloadManager::CheckIfSuggestedPathExists,
[email protected]88008002011-05-24 23:14:15405 download->id(),
[email protected]4cd82f72011-05-23 19:15:01406 state,
[email protected]5a3b97e2010-10-05 09:49:11407 download_prefs()->download_path()));
initial.commit09911bf2008-07-26 23:55:29408}
409
[email protected]fed38252011-07-08 17:26:50410void DownloadManager::CheckIfSuggestedPathExists(int32 download_id,
411 DownloadStateInfo state,
412 const FilePath& default_path) {
[email protected]ca4b5fa32010-10-09 12:42:18413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
initial.commit09911bf2008-07-26 23:55:29414
[email protected]fed38252011-07-08 17:26:50415 // Make sure the default download directory exists.
416 // TODO(phajdan.jr): only create the directory when we're sure the user
417 // is going to save there and not to another directory of his choice.
418 file_util::CreateDirectory(default_path);
419
420 // Check writability of the suggested path. If we can't write to it, default
421 // to the user's "My Documents" directory. We'll prompt them in this case.
422 FilePath dir = state.suggested_path.DirName();
423 FilePath filename = state.suggested_path.BaseName();
424 if (!file_util::PathIsWritable(dir)) {
425 VLOG(1) << "Unable to write to directory \"" << dir.value() << "\"";
[email protected]4cd82f72011-05-23 19:15:01426 state.prompt_user_for_save_location = true;
[email protected]fed38252011-07-08 17:26:50427 PathService::Get(chrome::DIR_USER_DOCUMENTS, &state.suggested_path);
428 state.suggested_path = state.suggested_path.Append(filename);
429 }
initial.commit09911bf2008-07-26 23:55:29430
[email protected]6cade212008-12-03 00:32:22431 // If the download is deemed dangerous, we'll use a temporary name for it.
[email protected]4cd82f72011-05-23 19:15:01432 if (state.IsDangerous()) {
[email protected]fed38252011-07-08 17:26:50433 state.target_name = FilePath(state.suggested_path).BaseName();
[email protected]9ccbb372008-10-10 18:50:32434 // Create a temporary file to hold the file until the user approves its
435 // download.
[email protected]7ae7c2cb2009-01-06 23:31:41436 FilePath::StringType file_name;
437 FilePath path;
[email protected]463e1b432011-01-21 22:05:51438#if defined(OS_WIN)
439 string16 unconfirmed_prefix =
440 l10n_util::GetStringUTF16(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
441#else
442 std::string unconfirmed_prefix =
443 l10n_util::GetStringUTF8(IDS_DOWNLOAD_UNCONFIRMED_PREFIX);
444#endif
445
[email protected]9ccbb372008-10-10 18:50:32446 while (path.empty()) {
[email protected]2594c2b2010-11-08 23:04:26447 base::SStringPrintf(
448 &file_name,
[email protected]463e1b432011-01-21 22:05:51449 unconfirmed_prefix.append(
450 FILE_PATH_LITERAL(" %d.crdownload")).c_str(),
[email protected]2594c2b2010-11-08 23:04:26451 base::RandInt(0, 100000));
[email protected]fed38252011-07-08 17:26:50452 path = dir.Append(file_name);
[email protected]7d3851d82008-12-12 03:26:07453 if (file_util::PathExists(path))
[email protected]7ae7c2cb2009-01-06 23:31:41454 path = FilePath();
[email protected]9ccbb372008-10-10 18:50:32455 }
[email protected]4cd82f72011-05-23 19:15:01456 state.suggested_path = path;
[email protected]7a256ea2008-10-17 17:34:16457 } else {
[email protected]594cd7d2010-07-21 03:23:56458 // Do not add the path uniquifier if we are saving to a specific path as in
459 // the drag-out case.
[email protected]4cd82f72011-05-23 19:15:01460 if (state.force_file_name.empty()) {
461 state.path_uniquifier = download_util::GetUniquePathNumberWithCrDownload(
462 state.suggested_path);
[email protected]594cd7d2010-07-21 03:23:56463 }
[email protected]7a256ea2008-10-17 17:34:16464 // We know the final path, build it if necessary.
[email protected]4cd82f72011-05-23 19:15:01465 if (state.path_uniquifier > 0) {
466 download_util::AppendNumberToPath(&(state.suggested_path),
467 state.path_uniquifier);
[email protected]7a256ea2008-10-17 17:34:16468 // Setting path_uniquifier to 0 to make sure we don't try to unique it
469 // later on.
[email protected]4cd82f72011-05-23 19:15:01470 state.path_uniquifier = 0;
471 } else if (state.path_uniquifier == -1) {
[email protected]7d3851d82008-12-12 03:26:07472 // We failed to find a unique path. We have to prompt the user.
[email protected]da6e3922010-11-24 21:45:50473 VLOG(1) << "Unable to find a unique path for suggested path \""
[email protected]4cd82f72011-05-23 19:15:01474 << state.suggested_path.value() << "\"";
475 state.prompt_user_for_save_location = true;
[email protected]7a256ea2008-10-17 17:34:16476 }
[email protected]9ccbb372008-10-10 18:50:32477 }
478
[email protected]594cd7d2010-07-21 03:23:56479 // Create an empty file at the suggested path so that we don't allocate the
480 // same "non-existant" path to multiple downloads.
481 // See: http://code.google.com/p/chromium/issues/detail?id=3662
[email protected]4cd82f72011-05-23 19:15:01482 if (!state.prompt_user_for_save_location &&
483 state.force_file_name.empty()) {
484 if (state.IsDangerous())
485 file_util::WriteFile(state.suggested_path, "", 0);
[email protected]594cd7d2010-07-21 03:23:56486 else
487 file_util::WriteFile(download_util::GetCrDownloadPath(
[email protected]4cd82f72011-05-23 19:15:01488 state.suggested_path), "", 0);
[email protected]7d3851d82008-12-12 03:26:07489 }
490
[email protected]ca4b5fa32010-10-09 12:42:18491 BrowserThread::PostTask(
492 BrowserThread::UI, FROM_HERE,
initial.commit09911bf2008-07-26 23:55:29493 NewRunnableMethod(this,
494 &DownloadManager::OnPathExistenceAvailable,
[email protected]4cd82f72011-05-23 19:15:01495 download_id,
496 state));
initial.commit09911bf2008-07-26 23:55:29497}
498
[email protected]22001462011-06-22 17:42:51499void DownloadManager::OnPathExistenceAvailable(
500 int32 download_id, const DownloadStateInfo& new_state) {
[email protected]ca4b5fa32010-10-09 12:42:18501 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
initial.commit09911bf2008-07-26 23:55:29502
[email protected]4cd82f72011-05-23 19:15:01503 DownloadItem* download = GetActiveDownloadItem(download_id);
504 if (!download)
505 return;
506
507 VLOG(20) << __FUNCTION__ << "()"
508 << " download = " << download->DebugString(true);
509
510 download->SetFileCheckResults(new_state);
511
512 FilePath suggested_path = download->suggested_path();
513
[email protected]da1a27b2011-07-29 23:16:33514 if (download->prompt_user_for_save_location()) {
initial.commit09911bf2008-07-26 23:55:29515 // We must ask the user for the place to put the download.
[email protected]db6831a2011-06-09 21:08:28516 DownloadRequestHandle request_handle = download->request_handle();
517 TabContents* contents = request_handle.GetTabContents();
[email protected]99cb7f82011-07-28 17:27:26518
[email protected]4cd82f72011-05-23 19:15:01519 // |id_ptr| will be deleted in either FileSelected() or
520 // FileSelectionCancelled().
521 int32* id_ptr = new int32;
522 *id_ptr = download_id;
[email protected]99cb7f82011-07-28 17:27:26523
[email protected]da1a27b2011-07-29 23:16:33524 delegate_->ChooseDownloadPath(
[email protected]99cb7f82011-07-28 17:27:26525 this, contents, suggested_path, reinterpret_cast<void*>(id_ptr));
526
[email protected]f5920322011-03-24 20:34:16527 FOR_EACH_OBSERVER(Observer, observers_,
[email protected]fed38252011-07-08 17:26:50528 SelectFileDialogDisplayed(download_id));
initial.commit09911bf2008-07-26 23:55:29529 } else {
530 // No prompting for download, just continue with the suggested name.
[email protected]4cd82f72011-05-23 19:15:01531 ContinueDownloadWithPath(download, suggested_path);
initial.commit09911bf2008-07-26 23:55:29532 }
533}
534
[email protected]c2e76012010-12-23 21:10:29535void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) {
536 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
537
538 DownloadItem* download = new DownloadItem(this, *info,
539 profile_->IsOffTheRecord());
[email protected]4cd82f72011-05-23 19:15:01540 int32 download_id = info->download_id;
541 DCHECK(!ContainsKey(in_progress_, download_id));
542 DCHECK(!ContainsKey(active_downloads_, download_id));
[email protected]c2e76012010-12-23 21:10:29543 downloads_.insert(download);
[email protected]4cd82f72011-05-23 19:15:01544 active_downloads_[download_id] = download;
[email protected]c2e76012010-12-23 21:10:29545}
546
[email protected]4cd82f72011-05-23 19:15:01547void DownloadManager::ContinueDownloadWithPath(DownloadItem* download,
548 const FilePath& chosen_file) {
[email protected]ca4b5fa32010-10-09 12:42:18549 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]4cd82f72011-05-23 19:15:01550 DCHECK(download);
[email protected]aa033af2010-07-27 18:16:39551
[email protected]4cd82f72011-05-23 19:15:01552 int32 download_id = download->id();
initial.commit09911bf2008-07-26 23:55:29553
[email protected]70850c72011-01-11 17:31:27554 // NOTE(ahendrickson) Eventually |active_downloads_| will replace
555 // |in_progress_|, but we don't want to change the semantics yet.
[email protected]4cd82f72011-05-23 19:15:01556 DCHECK(!ContainsKey(in_progress_, download_id));
[email protected]70850c72011-01-11 17:31:27557 DCHECK(ContainsKey(downloads_, download));
[email protected]4cd82f72011-05-23 19:15:01558 DCHECK(ContainsKey(active_downloads_, download_id));
[email protected]70850c72011-01-11 17:31:27559
[email protected]4cd82f72011-05-23 19:15:01560 // Make sure the initial file name is set only once.
561 DCHECK(download->full_path().empty());
562 download->OnPathDetermined(chosen_file);
[email protected]4cd82f72011-05-23 19:15:01563
564 VLOG(20) << __FUNCTION__ << "()"
565 << " download = " << download->DebugString(true);
566
567 in_progress_[download_id] = download;
[email protected]adb2f3d12011-01-23 16:24:54568 UpdateAppIcon(); // Reflect entry into in_progress_.
initial.commit09911bf2008-07-26 23:55:29569
[email protected]adb2f3d12011-01-23 16:24:54570 // Rename to intermediate name.
[email protected]f5920322011-03-24 20:34:16571 FilePath download_path;
[email protected]4cd82f72011-05-23 19:15:01572 if (download->IsDangerous()) {
[email protected]adb2f3d12011-01-23 16:24:54573 // The download is not safe. We can now rename the file to its
[email protected]f5920322011-03-24 20:34:16574 // tentative name using RenameInProgressDownloadFile.
575 // NOTE: The |Rename| below will be a no-op for dangerous files, as we're
576 // renaming it to the same name.
[email protected]4cd82f72011-05-23 19:15:01577 download_path = download->full_path();
[email protected]594cd7d2010-07-21 03:23:56578 } else {
[email protected]adb2f3d12011-01-23 16:24:54579 // The download is a safe download. We need to
580 // rename it to its intermediate '.crdownload' path. The final
581 // name after user confirmation will be set from
[email protected]48837962011-04-19 17:03:29582 // DownloadItem::OnDownloadCompleting.
[email protected]4cd82f72011-05-23 19:15:01583 download_path =
584 download_util::GetCrDownloadPath(download->full_path());
[email protected]594cd7d2010-07-21 03:23:56585 }
586
[email protected]f5920322011-03-24 20:34:16587 BrowserThread::PostTask(
588 BrowserThread::FILE, FROM_HERE,
589 NewRunnableMethod(
590 file_manager_, &DownloadFileManager::RenameInProgressDownloadFile,
591 download->id(), download_path));
592
593 download->Rename(download_path);
594
[email protected]4cd82f72011-05-23 19:15:01595 download_history_->AddEntry(download,
[email protected]82f37b02010-07-29 22:04:57596 NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
initial.commit09911bf2008-07-26 23:55:29597}
598
initial.commit09911bf2008-07-26 23:55:29599void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
[email protected]70850c72011-01-11 17:31:27600 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
601 DownloadMap::iterator it = active_downloads_.find(download_id);
602 if (it != active_downloads_.end()) {
initial.commit09911bf2008-07-26 23:55:29603 DownloadItem* download = it->second;
[email protected]bf68a00b2011-04-07 17:28:26604 if (download->IsInProgress()) {
[email protected]70850c72011-01-11 17:31:27605 download->Update(size);
[email protected]adb2f3d12011-01-23 16:24:54606 UpdateAppIcon(); // Reflect size updates.
[email protected]70850c72011-01-11 17:31:27607 download_history_->UpdateEntry(download);
608 }
initial.commit09911bf2008-07-26 23:55:29609 }
610}
611
[email protected]bf68a00b2011-04-07 17:28:26612void DownloadManager::OnResponseCompleted(int32 download_id,
613 int64 size,
614 int os_error,
615 const std::string& hash) {
616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]7c8e55ba2011-07-01 18:59:23617 // ERR_CONNECTION_CLOSED is allowed since a number of servers in the wild
618 // advertise a larger Content-Length than the amount of bytes in the message
619 // body, and then close the connection. Other browsers - IE8, Firefox 4.0.1,
620 // and Safari 5.0.4 - treat the download as complete in this case, so we
621 // follow their lead.
[email protected]1822a422011-07-15 15:49:19622 if (os_error == 0 || os_error == net::ERR_CONNECTION_CLOSED) {
[email protected]bf68a00b2011-04-07 17:28:26623 OnAllDataSaved(download_id, size, hash);
624 } else {
625 OnDownloadError(download_id, size, os_error);
626 }
627}
628
[email protected]26711732011-03-09 00:21:22629void DownloadManager::OnAllDataSaved(int32 download_id,
630 int64 size,
631 const std::string& hash) {
[email protected]da6e3922010-11-24 21:45:50632 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
633 << " size = " << size;
[email protected]9d7ef802011-02-25 19:03:35634 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]9ccbb372008-10-10 18:50:32635
[email protected]c4f02c42011-01-24 21:55:06636 // If it's not in active_downloads_, that means it was cancelled; just
637 // ignore the notification.
638 if (active_downloads_.count(download_id) == 0)
639 return;
640
[email protected]adb2f3d12011-01-23 16:24:54641 DownloadItem* download = active_downloads_[download_id];
[email protected]a850ba42010-09-10 22:00:30642 download->OnAllDataSaved(size);
[email protected]9ccbb372008-10-10 18:50:32643
[email protected]26711732011-03-09 00:21:22644 // When hash is not available, it means either it is not calculated
645 // or there is error while it is calculated. We will skip the download hash
646 // check in that case.
647 if (!hash.empty()) {
[email protected]4b58e7d2011-07-11 10:22:56648#if defined(ENABLE_SAFE_BROWSING)
[email protected]26711732011-03-09 00:21:22649 scoped_refptr<DownloadSBClient> sb_client =
650 new DownloadSBClient(download_id,
[email protected]8799e542011-04-20 03:47:34651 download->url_chain(),
[email protected]8c40da62011-07-13 22:58:46652 download->referrer_url(),
653 profile_->GetPrefs()->GetBoolean(
654 prefs::kSafeBrowsingEnabled));
[email protected]26711732011-03-09 00:21:22655 sb_client->CheckDownloadHash(
656 hash, NewCallback(this, &DownloadManager::CheckDownloadHashDone));
[email protected]4b58e7d2011-07-11 10:22:56657#else
658 CheckDownloadHashDone(download_id, false);
659#endif
[email protected]26711732011-03-09 00:21:22660 }
[email protected]adb2f3d12011-01-23 16:24:54661 MaybeCompleteDownload(download);
662}
[email protected]9ccbb372008-10-10 18:50:32663
[email protected]26711732011-03-09 00:21:22664// TODO(lzheng): This function currently works as a callback place holder.
665// Once we decide the hash check is reliable, we could move the
666// MaybeCompleteDownload in OnAllDataSaved to this function.
667void DownloadManager::CheckDownloadHashDone(int32 download_id,
668 bool is_dangerous_hash) {
669 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
670 DVLOG(1) << "CheckDownloadHashDone, download_id: " << download_id
671 << " is dangerous_hash: " << is_dangerous_hash;
672
673 // If it's not in active_downloads_, that means it was cancelled or
674 // the download already finished.
675 if (active_downloads_.count(download_id) == 0)
676 return;
677
678 DVLOG(1) << "CheckDownloadHashDone, url: "
[email protected]4cd82f72011-05-23 19:15:01679 << active_downloads_[download_id]->GetURL().spec();
[email protected]26711732011-03-09 00:21:22680}
681
[email protected]7d413112011-06-16 18:50:17682void DownloadManager::AssertQueueStateConsistent(DownloadItem* download) {
[email protected]5cd11b6e2011-06-10 20:30:59683 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
[email protected]7d413112011-06-16 18:50:17684 if (download->state() == DownloadItem::REMOVING) {
685 CHECK(!ContainsKey(downloads_, download));
686 CHECK(!ContainsKey(active_downloads_, download->id()));
687 CHECK(!ContainsKey(in_progress_, download->id()));
688 CHECK(!ContainsKey(history_downloads_, download->db_handle()));
689 return;
690 }
691
692 // Should be in downloads_ if we're not REMOVING.
693 CHECK(ContainsKey(downloads_, download));
694
695 // Check history_downloads_ consistency.
696 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
697 CHECK(ContainsKey(history_downloads_, download->db_handle()));
698 } else {
699 // TODO(rdsmith): Somewhat painful; make sure to disable in
700 // release builds after resolution of http://crbug.com/85408.
701 for (DownloadMap::iterator it = history_downloads_.begin();
702 it != history_downloads_.end(); ++it) {
703 CHECK(it->second != download);
704 }
705 }
706
707 CHECK(ContainsKey(active_downloads_, download->id()) ==
708 (download->state() == DownloadItem::IN_PROGRESS));
[email protected]54610672011-07-18 18:24:43709 CHECK(ContainsKey(in_progress_, download->id()) ==
710 (download->state() == DownloadItem::IN_PROGRESS));
[email protected]5cd11b6e2011-06-10 20:30:59711}
712
[email protected]adb2f3d12011-01-23 16:24:54713bool DownloadManager::IsDownloadReadyForCompletion(DownloadItem* download) {
714 // If we don't have all the data, the download is not ready for
715 // completion.
716 if (!download->all_data_saved())
717 return false;
[email protected]6a7fb042010-02-01 16:30:47718
[email protected]9d7ef802011-02-25 19:03:35719 // If the download is dangerous, but not yet validated, it's not ready for
720 // completion.
721 if (download->safety_state() == DownloadItem::DANGEROUS)
722 return false;
723
[email protected]adb2f3d12011-01-23 16:24:54724 // If the download isn't active (e.g. has been cancelled) it's not
725 // ready for completion.
726 if (active_downloads_.count(download->id()) == 0)
727 return false;
728
729 // If the download hasn't been inserted into the history system
730 // (which occurs strictly after file name determination, intermediate
731 // file rename, and UI display) then it's not ready for completion.
[email protected]7054b592011-06-22 14:46:42732 if (download->db_handle() == DownloadHistory::kUninitializedHandle)
733 return false;
734
735 return true;
[email protected]adb2f3d12011-01-23 16:24:54736}
737
738void DownloadManager::MaybeCompleteDownload(DownloadItem* download) {
739 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
740 VLOG(20) << __FUNCTION__ << "()" << " download = "
741 << download->DebugString(false);
742
743 if (!IsDownloadReadyForCompletion(download))
[email protected]9ccbb372008-10-10 18:50:32744 return;
[email protected]9ccbb372008-10-10 18:50:32745
[email protected]adb2f3d12011-01-23 16:24:54746 // TODO(rdsmith): DCHECK that we only pass through this point
747 // once per download. The natural way to do this is by a state
748 // transition on the DownloadItem.
[email protected]594cd7d2010-07-21 03:23:56749
[email protected]adb2f3d12011-01-23 16:24:54750 // Confirm we're in the proper set of states to be here;
[email protected]9d7ef802011-02-25 19:03:35751 // in in_progress_, have all data, have a history handle, (validated or safe).
752 DCHECK_NE(DownloadItem::DANGEROUS, download->safety_state());
[email protected]adb2f3d12011-01-23 16:24:54753 DCHECK_EQ(1u, in_progress_.count(download->id()));
754 DCHECK(download->all_data_saved());
755 DCHECK(download->db_handle() != DownloadHistory::kUninitializedHandle);
756 DCHECK_EQ(1u, history_downloads_.count(download->db_handle()));
757
758 VLOG(20) << __FUNCTION__ << "()" << " executing: download = "
759 << download->DebugString(false);
760
761 // Remove the id from in_progress
762 in_progress_.erase(download->id());
763 UpdateAppIcon(); // Reflect removal from in_progress_.
764
[email protected]adb2f3d12011-01-23 16:24:54765 download_history_->UpdateEntry(download);
766
[email protected]f5920322011-03-24 20:34:16767 // Finish the download.
[email protected]48837962011-04-19 17:03:29768 download->OnDownloadCompleting(file_manager_);
[email protected]9ccbb372008-10-10 18:50:32769}
770
[email protected]cc3c7c092011-05-09 18:40:21771void DownloadManager::DownloadCompleted(int32 download_id) {
[email protected]70850c72011-01-11 17:31:27772 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cc3c7c092011-05-09 18:40:21773 DownloadItem* download = GetDownloadItem(download_id);
774 DCHECK(download);
775 download_history_->UpdateEntry(download);
[email protected]70850c72011-01-11 17:31:27776 active_downloads_.erase(download_id);
777}
778
[email protected]f5920322011-03-24 20:34:16779void DownloadManager::OnDownloadRenamedToFinalName(int download_id,
780 const FilePath& full_path,
781 int uniquifier) {
[email protected]da6e3922010-11-24 21:45:50782 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
[email protected]f5920322011-03-24 20:34:16783 << " full_path = \"" << full_path.value() << "\""
784 << " uniquifier = " << uniquifier;
[email protected]ca4b5fa32010-10-09 12:42:18785 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]f5920322011-03-24 20:34:16786
[email protected]2e030682010-07-23 19:45:36787 DownloadItem* item = GetDownloadItem(download_id);
788 if (!item)
789 return;
[email protected]6cade212008-12-03 00:32:22790
[email protected]8fa1eeb52011-04-13 14:18:02791 if (item->safety_state() == DownloadItem::SAFE) {
792 DCHECK_EQ(0, uniquifier) << "We should not uniquify SAFE downloads twice";
793 }
794
[email protected]ca4b5fa32010-10-09 12:42:18795 BrowserThread::PostTask(
[email protected]f5920322011-03-24 20:34:16796 BrowserThread::FILE, FROM_HERE,
797 NewRunnableMethod(
798 file_manager_, &DownloadFileManager::CompleteDownload, download_id));
[email protected]9ccbb372008-10-10 18:50:32799
[email protected]f5920322011-03-24 20:34:16800 if (uniquifier)
801 item->set_path_uniquifier(uniquifier);
[email protected]9ccbb372008-10-10 18:50:32802
[email protected]f5920322011-03-24 20:34:16803 item->OnDownloadRenamedToFinalName(full_path);
804 download_history_->UpdateDownloadPath(item, full_path);
initial.commit09911bf2008-07-26 23:55:29805}
806
[email protected]54610672011-07-18 18:24:43807void DownloadManager::DownloadCancelled(int32 download_id) {
[email protected]70850c72011-01-11 17:31:27808 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]54610672011-07-18 18:24:43809 DownloadMap::iterator it = in_progress_.find(download_id);
810 if (it == in_progress_.end())
initial.commit09911bf2008-07-26 23:55:29811 return;
812 DownloadItem* download = it->second;
813
[email protected]da6e3922010-11-24 21:45:50814 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
815 << " download = " << download->DebugString(true);
816
[email protected]54610672011-07-18 18:24:43817 // Clean up will happen when the history system create callback runs if we
818 // don't have a valid db_handle yet.
819 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
820 in_progress_.erase(it);
821 active_downloads_.erase(download_id);
822 UpdateAppIcon(); // Reflect removal from in_progress_.
823 download_history_->UpdateEntry(download);
824 }
initial.commit09911bf2008-07-26 23:55:29825
[email protected]54610672011-07-18 18:24:43826 DownloadCancelledInternal(download_id, download->request_handle());
827}
[email protected]d7d1c5c2009-08-05 23:52:50828
[email protected]54610672011-07-18 18:24:43829void DownloadManager::DownloadCancelledInternal(
830 int download_id, const DownloadRequestHandle& request_handle) {
831 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
832 request_handle.CancelRequest();
[email protected]d7d1c5c2009-08-05 23:52:50833
[email protected]ca4b5fa32010-10-09 12:42:18834 BrowserThread::PostTask(
835 BrowserThread::FILE, FROM_HERE,
[email protected]d83d03aa2009-11-02 21:44:37836 NewRunnableMethod(
837 file_manager_, &DownloadFileManager::CancelDownload, download_id));
initial.commit09911bf2008-07-26 23:55:29838}
839
[email protected]bf68a00b2011-04-07 17:28:26840void DownloadManager::OnDownloadError(int32 download_id,
841 int64 size,
842 int os_error) {
843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
844 DownloadMap::iterator it = active_downloads_.find(download_id);
845 // A cancel at the right time could remove the download from the
846 // |active_downloads_| map before we get here.
847 if (it == active_downloads_.end())
848 return;
849
850 DownloadItem* download = it->second;
851
[email protected]4cd82f72011-05-23 19:15:01852 VLOG(20) << __FUNCTION__ << "()" << " Error " << os_error
853 << " at offset " << download->received_bytes()
854 << " for download = " << download->DebugString(true);
855
[email protected]54610672011-07-18 18:24:43856 download->Interrupted(size, os_error);
857
858 // TODO(ahendrickson) - Remove this when we add resuming of interrupted
859 // downloads, as we will keep the download item around in that case.
860 //
861 // Clean up will happen when the history system create callback runs if we
862 // don't have a valid db_handle yet.
863 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
864 in_progress_.erase(download_id);
865 active_downloads_.erase(download_id);
866 UpdateAppIcon(); // Reflect removal from in_progress_.
867 download_history_->UpdateEntry(download);
868 }
869
870 BrowserThread::PostTask(
871 BrowserThread::FILE, FROM_HERE,
872 NewRunnableMethod(
873 file_manager_, &DownloadFileManager::CancelDownload, download_id));
[email protected]bf68a00b2011-04-07 17:28:26874}
875
[email protected]6a7fb042010-02-01 16:30:47876void DownloadManager::UpdateAppIcon() {
[email protected]073ed7b2010-09-27 09:20:02877 if (status_updater_)
878 status_updater_->Update();
[email protected]6a7fb042010-02-01 16:30:47879}
880
[email protected]6d0146c2011-08-04 19:13:04881int DownloadManager::RemoveDownloadItems(
882 const DownloadVector& pending_deletes) {
883 if (pending_deletes.empty())
884 return 0;
885
886 // Delete from internal maps.
887 for (DownloadVector::const_iterator it = pending_deletes.begin();
888 it != pending_deletes.end();
889 ++it) {
890 DownloadItem* download = *it;
891 DCHECK(download);
892 history_downloads_.erase(download->db_handle());
893 save_page_downloads_.erase(download->id());
894 downloads_.erase(download);
895 }
896
897 // Tell observers to refresh their views.
898 NotifyModelChanged();
899
900 // Delete the download items themselves.
901 const int num_deleted = static_cast<int>(pending_deletes.size());
902 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
903 return num_deleted;
904}
905
[email protected]54610672011-07-18 18:24:43906void DownloadManager::RemoveDownload(int64 download_handle) {
907 DownloadMap::iterator it = history_downloads_.find(download_handle);
908 if (it == history_downloads_.end())
909 return;
910
911 // Make history update.
912 DownloadItem* download = it->second;
913 download_history_->RemoveEntry(download);
initial.commit09911bf2008-07-26 23:55:29914
915 // Remove from our tables and delete.
[email protected]6d0146c2011-08-04 19:13:04916 int downloads_count = RemoveDownloadItems(DownloadVector(1, download));
[email protected]f04182f32010-12-10 19:12:07917 DCHECK_EQ(1, downloads_count);
initial.commit09911bf2008-07-26 23:55:29918}
919
[email protected]e93d2822009-01-30 05:59:59920int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin,
921 const base::Time remove_end) {
[email protected]82f37b02010-07-29 22:04:57922 download_history_->RemoveEntriesBetween(remove_begin, remove_end);
initial.commit09911bf2008-07-26 23:55:29923
[email protected]a312a442010-12-15 23:40:33924 // All downloads visible to the user will be in the history,
925 // so scan that map.
[email protected]6d0146c2011-08-04 19:13:04926 DownloadVector pending_deletes;
927 for (DownloadMap::const_iterator it = history_downloads_.begin();
928 it != history_downloads_.end();
929 ++it) {
initial.commit09911bf2008-07-26 23:55:29930 DownloadItem* download = it->second;
initial.commit09911bf2008-07-26 23:55:29931 if (download->start_time() >= remove_begin &&
932 (remove_end.is_null() || download->start_time() < remove_end) &&
[email protected]6d0146c2011-08-04 19:13:04933 (download->IsComplete() || download->IsCancelled())) {
[email protected]7d413112011-06-16 18:50:17934 AssertQueueStateConsistent(download);
[email protected]78b8fcc92009-03-31 17:36:28935 pending_deletes.push_back(download);
initial.commit09911bf2008-07-26 23:55:29936 }
initial.commit09911bf2008-07-26 23:55:29937 }
[email protected]6d0146c2011-08-04 19:13:04938 return RemoveDownloadItems(pending_deletes);
initial.commit09911bf2008-07-26 23:55:29939}
940
[email protected]e93d2822009-01-30 05:59:59941int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
942 return RemoveDownloadsBetween(remove_begin, base::Time());
initial.commit09911bf2008-07-26 23:55:29943}
944
[email protected]d41355e6f2009-04-07 21:21:12945int DownloadManager::RemoveAllDownloads() {
[email protected]024f2f02010-04-30 22:51:46946 if (this != profile_->GetOriginalProfile()->GetDownloadManager()) {
947 // This is an incognito downloader. Clear All should clear main download
948 // manager as well.
949 profile_->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads();
950 }
[email protected]d41355e6f2009-04-07 21:21:12951 // The null times make the date range unbounded.
952 return RemoveDownloadsBetween(base::Time(), base::Time());
953}
954
initial.commit09911bf2008-07-26 23:55:29955// Initiate a download of a specific URL. We send the request to the
956// ResourceDispatcherHost, and let it send us responses like a regular
957// download.
958void DownloadManager::DownloadUrl(const GURL& url,
959 const GURL& referrer,
[email protected]c9825a42009-05-01 22:51:50960 const std::string& referrer_charset,
[email protected]57c6a652009-05-04 07:58:34961 TabContents* tab_contents) {
[email protected]ae8945192010-07-20 16:56:26962 DownloadUrlToFile(url, referrer, referrer_charset, DownloadSaveInfo(),
963 tab_contents);
[email protected]6aa4a1c02010-01-15 18:49:58964}
965
966void DownloadManager::DownloadUrlToFile(const GURL& url,
967 const GURL& referrer,
968 const std::string& referrer_charset,
[email protected]8af9d032010-02-10 00:00:32969 const DownloadSaveInfo& save_info,
[email protected]6aa4a1c02010-01-15 18:49:58970 TabContents* tab_contents) {
[email protected]57c6a652009-05-04 07:58:34971 DCHECK(tab_contents);
[email protected]ed24fad2011-05-10 22:44:01972 // We send a pointer to content::ResourceContext, instead of the usual
973 // reference, so that a copy of the object isn't made.
[email protected]ca4b5fa32010-10-09 12:42:18974 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
[email protected]ae8945192010-07-20 16:56:26975 NewRunnableFunction(&download_util::DownloadUrl,
976 url,
977 referrer,
978 referrer_charset,
979 save_info,
980 g_browser_process->resource_dispatcher_host(),
981 tab_contents->GetRenderProcessHost()->id(),
982 tab_contents->render_view_host()->routing_id(),
[email protected]cafe4ad2011-07-28 18:34:56983 &tab_contents->browser_context()->
984 GetResourceContext()));
initial.commit09911bf2008-07-26 23:55:29985}
986
initial.commit09911bf2008-07-26 23:55:29987void DownloadManager::AddObserver(Observer* observer) {
988 observers_.AddObserver(observer);
989 observer->ModelChanged();
990}
991
992void DownloadManager::RemoveObserver(Observer* observer) {
993 observers_.RemoveObserver(observer);
994}
995
[email protected]eccb9d12009-10-28 05:40:09996bool DownloadManager::ShouldOpenFileBasedOnExtension(
997 const FilePath& path) const {
[email protected]eccb9d12009-10-28 05:40:09998 FilePath::StringType extension = path.Extension();
999 if (extension.empty())
1000 return false;
[email protected]92e11c82010-01-13 06:39:561001 if (Extension::IsExtension(path))
1002 return false;
[email protected]eccb9d12009-10-28 05:40:091003 DCHECK(extension[0] == FilePath::kExtensionSeparator);
1004 extension.erase(0, 1);
[email protected]e5dc4222010-08-30 22:16:321005 return download_prefs_->IsAutoOpenEnabledForExtension(extension);
initial.commit09911bf2008-07-26 23:55:291006}
1007
[email protected]073ed7b2010-09-27 09:20:021008bool DownloadManager::IsDownloadProgressKnown() {
1009 for (DownloadMap::iterator i = in_progress_.begin();
1010 i != in_progress_.end(); ++i) {
1011 if (i->second->total_bytes() <= 0)
1012 return false;
1013 }
1014
1015 return true;
1016}
1017
1018int64 DownloadManager::GetInProgressDownloadCount() {
1019 return in_progress_.size();
1020}
1021
1022int64 DownloadManager::GetReceivedDownloadBytes() {
1023 DCHECK(IsDownloadProgressKnown());
1024 int64 received_bytes = 0;
1025 for (DownloadMap::iterator i = in_progress_.begin();
1026 i != in_progress_.end(); ++i) {
1027 received_bytes += i->second->received_bytes();
1028 }
1029 return received_bytes;
1030}
1031
1032int64 DownloadManager::GetTotalDownloadBytes() {
1033 DCHECK(IsDownloadProgressKnown());
1034 int64 total_bytes = 0;
1035 for (DownloadMap::iterator i = in_progress_.begin();
1036 i != in_progress_.end(); ++i) {
1037 total_bytes += i->second->total_bytes();
1038 }
1039 return total_bytes;
1040}
1041
[email protected]99cb7f82011-07-28 17:27:261042void DownloadManager::FileSelected(const FilePath& path, void* params) {
[email protected]4cd82f72011-05-23 19:15:011043 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1044
1045 int32* id_ptr = reinterpret_cast<int32*>(params);
1046 DCHECK(id_ptr != NULL);
1047 int32 download_id = *id_ptr;
1048 delete id_ptr;
1049
1050 DownloadItem* download = GetActiveDownloadItem(download_id);
1051 if (!download)
1052 return;
1053 VLOG(20) << __FUNCTION__ << "()" << " path = \"" << path.value() << "\""
1054 << " download = " << download->DebugString(true);
1055
[email protected]da1a27b2011-07-29 23:16:331056 if (download->prompt_user_for_save_location())
[email protected]7ae7c2cb2009-01-06 23:31:411057 last_download_path_ = path.DirName();
[email protected]287b86b2011-02-26 00:11:351058
[email protected]4cd82f72011-05-23 19:15:011059 // Make sure the initial file name is set only once.
1060 ContinueDownloadWithPath(download, path);
initial.commit09911bf2008-07-26 23:55:291061}
1062
1063void DownloadManager::FileSelectionCanceled(void* params) {
1064 // The user didn't pick a place to save the file, so need to cancel the
1065 // download that's already in progress to the temporary location.
[email protected]4cd82f72011-05-23 19:15:011066 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1067 int32* id_ptr = reinterpret_cast<int32*>(params);
1068 DCHECK(id_ptr != NULL);
1069 int32 download_id = *id_ptr;
1070 delete id_ptr;
1071
1072 DownloadItem* download = GetActiveDownloadItem(download_id);
1073 if (!download)
1074 return;
1075
1076 VLOG(20) << __FUNCTION__ << "()"
1077 << " download = " << download->DebugString(true);
1078
[email protected]54610672011-07-18 18:24:431079 DownloadCancelledInternal(download_id, download->request_handle());
[email protected]4cd82f72011-05-23 19:15:011080}
1081
1082// TODO(phajdan.jr): This is apparently not being exercised in tests.
[email protected]da1a27b2011-07-29 23:16:331083bool DownloadManager::IsDangerousFile(const DownloadItem& download,
1084 const DownloadStateInfo& state,
1085 bool visited_referrer_before) {
[email protected]4cd82f72011-05-23 19:15:011086 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1087
1088 bool auto_open = ShouldOpenFileBasedOnExtension(state.suggested_path);
1089 download_util::DownloadDangerLevel danger_level =
1090 download_util::GetFileDangerLevel(state.suggested_path.BaseName());
1091
1092 if (danger_level == download_util::Dangerous)
1093 return !(auto_open && state.has_user_gesture);
1094
1095 if (danger_level == download_util::AllowOnUserGesture &&
[email protected]88008002011-05-24 23:14:151096 (!state.has_user_gesture || !visited_referrer_before))
[email protected]4cd82f72011-05-23 19:15:011097 return true;
1098
1099 if (state.is_extension_install) {
1100 // Extensions that are not from the gallery are considered dangerous.
1101 ExtensionService* service = profile()->GetExtensionService();
1102 if (!service || !service->IsDownloadFromGallery(download.GetURL(),
1103 download.referrer_url()))
1104 return true;
1105 }
1106 return false;
initial.commit09911bf2008-07-26 23:55:291107}
1108
1109// Operations posted to us from the history service ----------------------------
1110
1111// The history service has retrieved all download entries. 'entries' contains
[email protected]4cd82f72011-05-23 19:15:011112// 'DownloadHistoryInfo's in sorted order (by ascending start_time).
initial.commit09911bf2008-07-26 23:55:291113void DownloadManager::OnQueryDownloadEntriesComplete(
[email protected]4cd82f72011-05-23 19:15:011114 std::vector<DownloadHistoryInfo>* entries) {
initial.commit09911bf2008-07-26 23:55:291115 for (size_t i = 0; i < entries->size(); ++i) {
[email protected]aa033af2010-07-27 18:16:391116 DownloadItem* download = new DownloadItem(this, entries->at(i));
[email protected]f04182f32010-12-10 19:12:071117 DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
1118 downloads_.insert(download);
1119 history_downloads_[download->db_handle()] = download;
[email protected]da6e3922010-11-24 21:45:501120 VLOG(20) << __FUNCTION__ << "()" << i << ">"
1121 << " download = " << download->DebugString(true);
initial.commit09911bf2008-07-26 23:55:291122 }
[email protected]b0ab1d42010-02-24 19:29:281123 NotifyModelChanged();
[email protected]9fc114672011-06-15 08:17:481124 CheckForHistoryFilesRemoval();
initial.commit09911bf2008-07-26 23:55:291125}
1126
[email protected]f9a45b02011-06-30 23:49:191127void DownloadManager::AddDownloadItemToHistory(DownloadItem* download,
1128 int64 db_handle) {
[email protected]70850c72011-01-11 17:31:271129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]d2a8fb72010-01-21 05:31:421130
[email protected]5bcd73eb2011-03-23 21:14:021131 // It's not immediately obvious, but HistoryBackend::CreateDownload() can
1132 // call this function with an invalid |db_handle|. For instance, this can
1133 // happen when the history database is offline. We cannot have multiple
1134 // DownloadItems with the same invalid db_handle, so we need to assign a
1135 // unique |db_handle| here.
1136 if (db_handle == DownloadHistory::kUninitializedHandle)
1137 db_handle = download_history_->GetNextFakeDbHandle();
1138
[email protected]1e9fe7ff2011-06-24 17:37:331139 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/84508
1140 // is fixed.
[email protected]54610672011-07-18 18:24:431141 CHECK_NE(DownloadHistory::kUninitializedHandle, db_handle);
[email protected]1e9fe7ff2011-06-24 17:37:331142
[email protected]5bcd73eb2011-03-23 21:14:021143 DCHECK(download->db_handle() == DownloadHistory::kUninitializedHandle);
1144 download->set_db_handle(db_handle);
1145
[email protected]5bcd73eb2011-03-23 21:14:021146 DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
1147 history_downloads_[download->db_handle()] = download;
[email protected]6d0146c2011-08-04 19:13:041148
1149 // Show in the appropriate browser UI.
1150 // This includes buttons to save or cancel, for a dangerous download.
1151 ShowDownloadInBrowser(download);
1152
1153 // Inform interested objects about the new download.
1154 NotifyModelChanged();
[email protected]f9a45b02011-06-30 23:49:191155}
1156
1157// Once the new DownloadItem's creation info has been committed to the history
1158// service, we associate the DownloadItem with the db handle, update our
1159// 'history_downloads_' map and inform observers.
1160void DownloadManager::OnCreateDownloadEntryComplete(int32 download_id,
1161 int64 db_handle) {
1162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]19420cc2011-07-18 17:43:451163 DownloadItem* download = GetActiveDownloadItem(download_id);
[email protected]54610672011-07-18 18:24:431164 if (!download)
[email protected]19420cc2011-07-18 17:43:451165 return;
[email protected]54610672011-07-18 18:24:431166
1167 VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle
1168 << " download_id = " << download_id
1169 << " download = " << download->DebugString(true);
[email protected]f9a45b02011-06-30 23:49:191170
1171 AddDownloadItemToHistory(download, db_handle);
initial.commit09911bf2008-07-26 23:55:291172
[email protected]54610672011-07-18 18:24:431173 // If the download is still in progress, try to complete it.
1174 //
1175 // Otherwise, download has been cancelled or interrupted before we've
1176 // received the DB handle. We post one final message to the history
1177 // service so that it can be properly in sync with the DownloadItem's
1178 // completion status, and also inform any observers so that they get
1179 // more than just the start notification.
1180 if (download->IsInProgress()) {
1181 MaybeCompleteDownload(download);
1182 } else {
1183 DCHECK(download->IsCancelled())
1184 << " download = " << download->DebugString(true);
1185 in_progress_.erase(download_id);
1186 active_downloads_.erase(download_id);
1187 download_history_->UpdateEntry(download);
1188 download->UpdateObservers();
1189 }
initial.commit09911bf2008-07-26 23:55:291190}
1191
[email protected]4cd82f72011-05-23 19:15:011192void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) {
[email protected]8ddbd66a2010-05-21 16:38:341193 // The 'contents' may no longer exist if the user closed the tab before we
[email protected]99cb7f82011-07-28 17:27:261194 // get this start completion event.
[email protected]db6831a2011-06-09 21:08:281195 DownloadRequestHandle request_handle = download->request_handle();
[email protected]686493142011-07-15 21:47:221196 TabContents* content = request_handle.GetTabContents();
[email protected]99cb7f82011-07-28 17:27:261197
1198 // If the contents no longer exists, we ask the embedder to suggest another
1199 // tab.
[email protected]da1a27b2011-07-29 23:16:331200 if (!content)
1201 content = delegate_->GetAlternativeTabContentsToNotifyForDownload(this);
[email protected]5e595482009-05-06 20:16:531202
[email protected]99cb7f82011-07-28 17:27:261203 if (content)
1204 content->OnStartDownload(download);
[email protected]5e595482009-05-06 20:16:531205}
1206
[email protected]6cade212008-12-03 00:32:221207// Clears the last download path, used to initialize "save as" dialogs.
[email protected]905a08d2008-11-19 07:24:121208void DownloadManager::ClearLastDownloadPath() {
[email protected]7ae7c2cb2009-01-06 23:31:411209 last_download_path_ = FilePath();
[email protected]eea46622009-07-15 20:49:381210}
[email protected]b0ab1d42010-02-24 19:29:281211
1212void DownloadManager::NotifyModelChanged() {
1213 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
1214}
1215
[email protected]4cd82f72011-05-23 19:15:011216DownloadItem* DownloadManager::GetDownloadItem(int download_id) {
1217 // The |history_downloads_| map is indexed by the download's db_handle,
1218 // not its id, so we have to iterate.
[email protected]f04182f32010-12-10 19:12:071219 for (DownloadMap::iterator it = history_downloads_.begin();
1220 it != history_downloads_.end(); ++it) {
[email protected]2e030682010-07-23 19:45:361221 DownloadItem* item = it->second;
[email protected]4cd82f72011-05-23 19:15:011222 if (item->id() == download_id)
[email protected]2e030682010-07-23 19:45:361223 return item;
1224 }
1225 return NULL;
1226}
1227
[email protected]4cd82f72011-05-23 19:15:011228DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) {
1229 DCHECK(ContainsKey(active_downloads_, download_id));
1230 DownloadItem* download = active_downloads_[download_id];
1231 DCHECK(download != NULL);
1232 return download;
1233}
1234
[email protected]57fd1252010-12-23 17:24:091235// Confirm that everything in all maps is also in |downloads_|, and that
1236// everything in |downloads_| is also in some other map.
[email protected]f04182f32010-12-10 19:12:071237void DownloadManager::AssertContainersConsistent() const {
1238#if !defined(NDEBUG)
[email protected]57fd1252010-12-23 17:24:091239 // Turn everything into sets.
[email protected]6d0146c2011-08-04 19:13:041240 const DownloadMap* input_maps[] = {&active_downloads_,
1241 &history_downloads_,
1242 &save_page_downloads_};
1243 DownloadSet active_set, history_set, save_page_set;
1244 DownloadSet* all_sets[] = {&active_set, &history_set, &save_page_set};
1245 DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(all_sets));
[email protected]57fd1252010-12-23 17:24:091246 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input_maps); i++) {
1247 for (DownloadMap::const_iterator it = input_maps[i]->begin();
[email protected]6d0146c2011-08-04 19:13:041248 it != input_maps[i]->end(); ++it) {
1249 all_sets[i]->insert(&*it->second);
[email protected]f04182f32010-12-10 19:12:071250 }
1251 }
[email protected]57fd1252010-12-23 17:24:091252
1253 // Check if each set is fully present in downloads, and create a union.
[email protected]57fd1252010-12-23 17:24:091254 DownloadSet downloads_union;
1255 for (int i = 0; i < static_cast<int>(ARRAYSIZE_UNSAFE(all_sets)); i++) {
1256 DownloadSet remainder;
1257 std::insert_iterator<DownloadSet> insert_it(remainder, remainder.begin());
1258 std::set_difference(all_sets[i]->begin(), all_sets[i]->end(),
1259 downloads_.begin(), downloads_.end(),
1260 insert_it);
1261 DCHECK(remainder.empty());
1262 std::insert_iterator<DownloadSet>
1263 insert_union(downloads_union, downloads_union.end());
1264 std::set_union(downloads_union.begin(), downloads_union.end(),
1265 all_sets[i]->begin(), all_sets[i]->end(),
1266 insert_union);
1267 }
1268
1269 // Is everything in downloads_ present in one of the other sets?
1270 DownloadSet remainder;
1271 std::insert_iterator<DownloadSet>
1272 insert_remainder(remainder, remainder.begin());
1273 std::set_difference(downloads_.begin(), downloads_.end(),
1274 downloads_union.begin(), downloads_union.end(),
1275 insert_remainder);
1276 DCHECK(remainder.empty());
[email protected]f04182f32010-12-10 19:12:071277#endif
1278}
1279
[email protected]b0ab1d42010-02-24 19:29:281280// DownloadManager::OtherDownloadManagerObserver implementation ----------------
1281
1282DownloadManager::OtherDownloadManagerObserver::OtherDownloadManagerObserver(
1283 DownloadManager* observing_download_manager)
1284 : observing_download_manager_(observing_download_manager),
1285 observed_download_manager_(NULL) {
1286 if (observing_download_manager->profile_->GetOriginalProfile() ==
1287 observing_download_manager->profile_) {
1288 return;
1289 }
1290
1291 observed_download_manager_ = observing_download_manager_->
1292 profile_->GetOriginalProfile()->GetDownloadManager();
1293 observed_download_manager_->AddObserver(this);
1294}
1295
1296DownloadManager::OtherDownloadManagerObserver::~OtherDownloadManagerObserver() {
1297 if (observed_download_manager_)
1298 observed_download_manager_->RemoveObserver(this);
1299}
1300
1301void DownloadManager::OtherDownloadManagerObserver::ModelChanged() {
1302 observing_download_manager_->NotifyModelChanged();
1303}
1304
[email protected]b0ab1d42010-02-24 19:29:281305void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() {
1306 observed_download_manager_ = NULL;
1307}
[email protected]6d0146c2011-08-04 19:13:041308
1309void DownloadManager::SavePageDownloadStarted(DownloadItem* download) {
1310 DCHECK(!ContainsKey(save_page_downloads_, download->id()));
1311 downloads_.insert(download);
1312 save_page_downloads_[download->id()] = download;
1313
1314 // Add this entry to the history service.
1315 // Additionally, the UI is notified in the callback.
1316 download_history_->AddEntry(download,
1317 NewCallback(this, &DownloadManager::OnSavePageDownloadEntryAdded));
1318}
1319
1320// SavePackage will call SavePageDownloadFinished upon completion/cancellation.
1321// The history callback will call OnSavePageDownloadEntryAdded.
1322// If the download finishes before the history callback,
1323// OnSavePageDownloadEntryAdded calls SavePageDownloadFinished, ensuring that
1324// the history event is update regardless of the order in which these two events
1325// complete.
1326// If something removes the download item from the download manager (Remove,
1327// Shutdown) the result will be that the SavePage system will not be able to
1328// properly update the download item (which no longer exists) or the download
1329// history, but the action will complete properly anyway. This may lead to the
1330// history entry being wrong on a reload of chrome (specifically in the case of
1331// Initiation -> History Callback -> Removal -> Completion), but there's no way
1332// to solve that without canceling on Remove (which would then update the DB).
1333
1334void DownloadManager::OnSavePageDownloadEntryAdded(int32 download_id,
1335 int64 db_handle) {
1336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1337
1338 DownloadMap::const_iterator it = save_page_downloads_.find(download_id);
1339 // This can happen if the download manager is shutting down and all maps
1340 // have been cleared.
1341 if (it == save_page_downloads_.end())
1342 return;
1343
1344 DownloadItem* download = it->second;
1345 if (!download) {
1346 NOTREACHED();
1347 return;
1348 }
1349
1350 AddDownloadItemToHistory(download, db_handle);
1351
1352 // Finalize this download if it finished before the history callback.
1353 if (!download->IsInProgress())
1354 SavePageDownloadFinished(download);
1355}
1356
1357void DownloadManager::SavePageDownloadFinished(DownloadItem* download) {
1358 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
1359 download_history_->UpdateEntry(download);
1360 DCHECK(ContainsKey(save_page_downloads_, download->id()));
1361 save_page_downloads_.erase(download->id());
1362
1363 if (download->IsComplete())
1364 NotificationService::current()->Notify(
1365 content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
1366 Source<DownloadManager>(this),
1367 Details<DownloadItem>(download));
1368 }
1369}
1370
1371int32 DownloadManager::GetNextSavePageId() {
1372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1373 return next_save_page_id_++;
1374}
1375