blob: f1c52d4f2bd7bff618c163e00b55dee0dbedb442 [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]71bf3f5e2011-08-15 21:05:225#include "content/browser/download/download_manager.h"
initial.commit09911bf2008-07-26 23:55:296
[email protected]e7557f172011-08-19 23:42:017#include <iterator>
8
[email protected]2041cf342010-02-19 03:15:599#include "base/callback.h"
initial.commit09911bf2008-07-26 23:55:2910#include "base/file_util.h"
[email protected]503d03872011-05-06 08:36:2611#include "base/i18n/case_conversion.h"
initial.commit09911bf2008-07-26 23:55:2912#include "base/logging.h"
[email protected]7286e3fc2011-07-19 22:13:2413#include "base/stl_util.h"
initial.commit09911bf2008-07-26 23:55:2914#include "base/task.h"
[email protected]d2a8fb72010-01-21 05:31:4215#include "build/build_config.h"
[email protected]6d0c9fb2011-08-22 19:26:0316#include "content/browser/browser_context.h"
[email protected]7324d1d2011-03-01 05:02:1617#include "content/browser/browser_thread.h"
[email protected]a0ce3282011-08-19 20:49:5218#include "content/browser/content_browser_client.h"
[email protected]71bf3f5e2011-08-15 21:05:2219#include "content/browser/download/download_create_info.h"
20#include "content/browser/download/download_file_manager.h"
21#include "content/browser/download/download_item.h"
[email protected]d6b7fd1e2011-08-16 22:42:0022#include "content/browser/download/download_manager_delegate.h"
[email protected]bb1a4212011-08-22 22:38:2523#include "content/browser/download/download_persistent_store_info.h"
[email protected]71bf3f5e2011-08-15 21:05:2224#include "content/browser/download/download_status_updater.h"
[email protected]7324d1d2011-03-01 05:02:1625#include "content/browser/renderer_host/render_process_host.h"
26#include "content/browser/renderer_host/render_view_host.h"
27#include "content/browser/renderer_host/resource_dispatcher_host.h"
28#include "content/browser/tab_contents/tab_contents.h"
[email protected]432115822011-07-10 15:52:2729#include "content/common/content_notification_types.h"
[email protected]6d0146c2011-08-04 19:13:0430#include "content/common/notification_service.h"
initial.commit09911bf2008-07-26 23:55:2931
[email protected]a0ce3282011-08-19 20:49:5232namespace {
33
34void BeginDownload(
35 const GURL& url,
36 const GURL& referrer,
37 const DownloadSaveInfo& save_info,
[email protected]c79a0c02011-08-22 22:37:3738 ResourceDispatcherHost* resource_dispatcher_host,
39 int render_process_id,
[email protected]a0ce3282011-08-19 20:49:5240 int render_view_id,
41 const content::ResourceContext* context) {
[email protected]8e3ae68c2011-09-16 22:15:4742 net::URLRequest* request = new net::URLRequest(url, resource_dispatcher_host);
43 request->set_referrer(referrer.spec());
[email protected]c79a0c02011-08-22 22:37:3744 resource_dispatcher_host->BeginDownload(
[email protected]8e3ae68c2011-09-16 22:15:4745 request,
46 save_info,
47 true,
48 DownloadResourceHandler::OnStartedCallback(),
49 render_process_id,
50 render_view_id,
[email protected]c79a0c02011-08-22 22:37:3751 *context);
[email protected]a0ce3282011-08-19 20:49:5252}
53
54} // namespace
55
[email protected]da1a27b2011-07-29 23:16:3356DownloadManager::DownloadManager(DownloadManagerDelegate* delegate,
57 DownloadStatusUpdater* status_updater)
initial.commit09911bf2008-07-26 23:55:2958 : shutdown_needed_(false),
[email protected]6d0c9fb2011-08-22 19:26:0359 browser_context_(NULL),
[email protected]073ed7b2010-09-27 09:20:0260 file_manager_(NULL),
[email protected]c2475df2011-09-07 23:52:2261 status_updater_(status_updater->AsWeakPtr()),
[email protected]d8472d92011-08-26 20:15:2062 delegate_(delegate),
63 largest_db_handle_in_history_(DownloadItem::kUninitializedHandle) {
[email protected]c2475df2011-09-07 23:52:2264 if (status_updater_)
[email protected]073ed7b2010-09-27 09:20:0265 status_updater_->AddDelegate(this);
initial.commit09911bf2008-07-26 23:55:2966}
67
68DownloadManager::~DownloadManager() {
[email protected]326a6a92010-09-10 20:21:1369 DCHECK(!shutdown_needed_);
[email protected]c2475df2011-09-07 23:52:2270 if (status_updater_)
[email protected]073ed7b2010-09-27 09:20:0271 status_updater_->RemoveDelegate(this);
initial.commit09911bf2008-07-26 23:55:2972}
73
74void DownloadManager::Shutdown() {
[email protected]da6e3922010-11-24 21:45:5075 VLOG(20) << __FUNCTION__ << "()"
76 << " shutdown_needed_ = " << shutdown_needed_;
[email protected]326a6a92010-09-10 20:21:1377 if (!shutdown_needed_)
78 return;
79 shutdown_needed_ = false;
initial.commit09911bf2008-07-26 23:55:2980
[email protected]326a6a92010-09-10 20:21:1381 FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown());
[email protected]fb4f8d902011-09-16 06:07:0882 // TODO(benjhayden): Consider clearing observers_.
[email protected]326a6a92010-09-10 20:21:1383
84 if (file_manager_) {
[email protected]ca4b5fa32010-10-09 12:42:1885 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
[email protected]326a6a92010-09-10 20:21:1386 NewRunnableMethod(file_manager_,
87 &DownloadFileManager::OnDownloadManagerShutdown,
[email protected]dc7cdcb92010-12-14 06:40:5488 make_scoped_refptr(this)));
[email protected]326a6a92010-09-10 20:21:1389 }
initial.commit09911bf2008-07-26 23:55:2990
[email protected]f04182f32010-12-10 19:12:0791 AssertContainersConsistent();
92
93 // Go through all downloads in downloads_. Dangerous ones we need to
94 // remove on disk, and in progress ones we need to cancel.
[email protected]57fd1252010-12-23 17:24:0995 for (DownloadSet::iterator it = downloads_.begin(); it != downloads_.end();) {
[email protected]f04182f32010-12-10 19:12:0796 DownloadItem* download = *it;
97
98 // Save iterator from potential erases in this set done by called code.
99 // Iterators after an erasure point are still valid for lists and
100 // associative containers such as sets.
101 it++;
102
103 if (download->safety_state() == DownloadItem::DANGEROUS &&
[email protected]48837962011-04-19 17:03:29104 download->IsPartialDownload()) {
[email protected]f04182f32010-12-10 19:12:07105 // The user hasn't accepted it, so we need to remove it
106 // from the disk. This may or may not result in it being
107 // removed from the DownloadManager queues and deleted
108 // (specifically, DownloadManager::RemoveDownload only
109 // removes and deletes it if it's known to the history service)
110 // so the only thing we know after calling this function is that
111 // the download was deleted if-and-only-if it was removed
112 // from all queues.
[email protected]303077002011-04-19 23:21:01113 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN);
[email protected]bf68a00b2011-04-07 17:28:26114 } else if (download->IsPartialDownload()) {
[email protected]93af2272011-09-21 18:29:17115 download->Cancel(false);
116 delegate_->UpdateItemInPersistentStore(download);
initial.commit09911bf2008-07-26 23:55:29117 }
118 }
119
[email protected]f04182f32010-12-10 19:12:07120 // At this point, all dangerous downloads have had their files removed
121 // and all in progress downloads have been cancelled. We can now delete
122 // anything left.
[email protected]9ccbb372008-10-10 18:50:32123
[email protected]5cd11b6e2011-06-10 20:30:59124 // Copy downloads_ to separate container so as not to set off checks
125 // in DownloadItem destruction.
126 DownloadSet downloads_to_delete;
127 downloads_to_delete.swap(downloads_);
128
initial.commit09911bf2008-07-26 23:55:29129 in_progress_.clear();
[email protected]70850c72011-01-11 17:31:27130 active_downloads_.clear();
[email protected]5cd11b6e2011-06-10 20:30:59131 history_downloads_.clear();
[email protected]5cd11b6e2011-06-10 20:30:59132 STLDeleteElements(&downloads_to_delete);
initial.commit09911bf2008-07-26 23:55:29133
[email protected]6d0146c2011-08-04 19:13:04134 DCHECK(save_page_downloads_.empty());
135
initial.commit09911bf2008-07-26 23:55:29136 file_manager_ = NULL;
[email protected]2588ea9d2011-08-22 20:59:53137 delegate_->Shutdown();
[email protected]82f37b02010-07-29 22:04:57138
initial.commit09911bf2008-07-26 23:55:29139 shutdown_needed_ = false;
140}
141
[email protected]82f37b02010-07-29 22:04:57142void DownloadManager::GetTemporaryDownloads(
[email protected]6d0146c2011-08-04 19:13:04143 const FilePath& dir_path, DownloadVector* result) {
[email protected]82f37b02010-07-29 22:04:57144 DCHECK(result);
[email protected]6aa4a1c02010-01-15 18:49:58145
[email protected]f04182f32010-12-10 19:12:07146 for (DownloadMap::iterator it = history_downloads_.begin();
147 it != history_downloads_.end(); ++it) {
[email protected]6aa4a1c02010-01-15 18:49:58148 if (it->second->is_temporary() &&
149 it->second->full_path().DirName() == dir_path)
[email protected]82f37b02010-07-29 22:04:57150 result->push_back(it->second);
[email protected]6aa4a1c02010-01-15 18:49:58151 }
[email protected]6aa4a1c02010-01-15 18:49:58152}
153
[email protected]82f37b02010-07-29 22:04:57154void DownloadManager::GetAllDownloads(
[email protected]6d0146c2011-08-04 19:13:04155 const FilePath& dir_path, DownloadVector* result) {
[email protected]82f37b02010-07-29 22:04:57156 DCHECK(result);
[email protected]8ddbd66a2010-05-21 16:38:34157
[email protected]f04182f32010-12-10 19:12:07158 for (DownloadMap::iterator it = history_downloads_.begin();
159 it != history_downloads_.end(); ++it) {
[email protected]8ddbd66a2010-05-21 16:38:34160 if (!it->second->is_temporary() &&
161 (dir_path.empty() || it->second->full_path().DirName() == dir_path))
[email protected]82f37b02010-07-29 22:04:57162 result->push_back(it->second);
[email protected]8ddbd66a2010-05-21 16:38:34163 }
[email protected]8ddbd66a2010-05-21 16:38:34164}
165
[email protected]d3b12902010-08-16 23:39:42166void DownloadManager::SearchDownloads(const string16& query,
[email protected]6d0146c2011-08-04 19:13:04167 DownloadVector* result) {
[email protected]503d03872011-05-06 08:36:26168 string16 query_lower(base::i18n::ToLower(query));
[email protected]d3b12902010-08-16 23:39:42169
[email protected]f04182f32010-12-10 19:12:07170 for (DownloadMap::iterator it = history_downloads_.begin();
171 it != history_downloads_.end(); ++it) {
[email protected]d3b12902010-08-16 23:39:42172 DownloadItem* download_item = it->second;
173
[email protected]8a282712011-08-23 19:28:00174 if (download_item->is_temporary())
[email protected]d3b12902010-08-16 23:39:42175 continue;
176
177 // Display Incognito downloads only in Incognito window, and vice versa.
178 // The Incognito Downloads page will get the list of non-Incognito downloads
179 // from its parent profile.
[email protected]6d0c9fb2011-08-22 19:26:03180 if (browser_context_->IsOffTheRecord() != download_item->is_otr())
[email protected]d3b12902010-08-16 23:39:42181 continue;
182
183 if (download_item->MatchesQuery(query_lower))
184 result->push_back(download_item);
185 }
[email protected]d3b12902010-08-16 23:39:42186}
187
initial.commit09911bf2008-07-26 23:55:29188// Query the history service for information about all persisted downloads.
[email protected]6d0c9fb2011-08-22 19:26:03189bool DownloadManager::Init(content::BrowserContext* browser_context) {
190 DCHECK(browser_context);
initial.commit09911bf2008-07-26 23:55:29191 DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
192 shutdown_needed_ = true;
193
[email protected]6d0c9fb2011-08-22 19:26:03194 browser_context_ = browser_context;
[email protected]024f2f02010-04-30 22:51:46195
[email protected]2941c2392010-07-15 22:54:30196 // In test mode, there may be no ResourceDispatcherHost. In this case it's
197 // safe to avoid setting |file_manager_| because we only call a small set of
198 // functions, none of which need it.
[email protected]a0ce3282011-08-19 20:49:52199 ResourceDispatcherHost* rdh =
200 content::GetContentClient()->browser()->GetResourceDispatcherHost();
[email protected]2941c2392010-07-15 22:54:30201 if (rdh) {
202 file_manager_ = rdh->download_file_manager();
203 DCHECK(file_manager_);
initial.commit09911bf2008-07-26 23:55:29204 }
205
initial.commit09911bf2008-07-26 23:55:29206 return true;
207}
208
[email protected]aa9881c2011-08-15 18:01:12209// We have received a message from DownloadFileManager about a new download.
[email protected]4cd82f72011-05-23 19:15:01210void DownloadManager::StartDownload(int32 download_id) {
[email protected]ca4b5fa32010-10-09 12:42:18211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]287b86b2011-02-26 00:11:35212
[email protected]aa9881c2011-08-15 18:01:12213 if (delegate_->ShouldStartDownload(download_id))
214 RestartDownload(download_id);
[email protected]287b86b2011-02-26 00:11:35215}
216
[email protected]9fc114672011-06-15 08:17:48217void DownloadManager::CheckForHistoryFilesRemoval() {
218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
219 for (DownloadMap::iterator it = history_downloads_.begin();
220 it != history_downloads_.end(); ++it) {
221 CheckForFileRemoval(it->second);
222 }
223}
224
225void DownloadManager::CheckForFileRemoval(DownloadItem* download_item) {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
227 if (download_item->IsComplete() &&
228 !download_item->file_externally_removed()) {
229 BrowserThread::PostTask(
230 BrowserThread::FILE, FROM_HERE,
231 NewRunnableMethod(this,
232 &DownloadManager::CheckForFileRemovalOnFileThread,
233 download_item->db_handle(),
234 download_item->GetTargetFilePath()));
235 }
236}
237
238void DownloadManager::CheckForFileRemovalOnFileThread(
239 int64 db_handle, const FilePath& path) {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
241 if (!file_util::PathExists(path)) {
242 BrowserThread::PostTask(
243 BrowserThread::UI, FROM_HERE,
244 NewRunnableMethod(this,
245 &DownloadManager::OnFileRemovalDetected,
246 db_handle));
247 }
248}
249
250void DownloadManager::OnFileRemovalDetected(int64 db_handle) {
251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
252 DownloadMap::iterator it = history_downloads_.find(db_handle);
253 if (it != history_downloads_.end()) {
254 DownloadItem* download_item = it->second;
255 download_item->OnDownloadedFileRemoved();
256 }
257}
258
[email protected]aa9881c2011-08-15 18:01:12259void DownloadManager::RestartDownload(
260 int32 download_id) {
[email protected]ca4b5fa32010-10-09 12:42:18261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
initial.commit09911bf2008-07-26 23:55:29262
[email protected]4cd82f72011-05-23 19:15:01263 DownloadItem* download = GetActiveDownloadItem(download_id);
264 if (!download)
265 return;
266
267 VLOG(20) << __FUNCTION__ << "()"
268 << " download = " << download->DebugString(true);
269
[email protected]4cd82f72011-05-23 19:15:01270 FilePath suggested_path = download->suggested_path();
271
[email protected]da1a27b2011-07-29 23:16:33272 if (download->prompt_user_for_save_location()) {
initial.commit09911bf2008-07-26 23:55:29273 // We must ask the user for the place to put the download.
[email protected]db6831a2011-06-09 21:08:28274 DownloadRequestHandle request_handle = download->request_handle();
275 TabContents* contents = request_handle.GetTabContents();
[email protected]99cb7f82011-07-28 17:27:26276
[email protected]4cd82f72011-05-23 19:15:01277 // |id_ptr| will be deleted in either FileSelected() or
[email protected]93af2272011-09-21 18:29:17278 // FileSelectionCancelled().
[email protected]4cd82f72011-05-23 19:15:01279 int32* id_ptr = new int32;
280 *id_ptr = download_id;
[email protected]99cb7f82011-07-28 17:27:26281
[email protected]da1a27b2011-07-29 23:16:33282 delegate_->ChooseDownloadPath(
[email protected]aa9881c2011-08-15 18:01:12283 contents, suggested_path, reinterpret_cast<void*>(id_ptr));
[email protected]99cb7f82011-07-28 17:27:26284
[email protected]f5920322011-03-24 20:34:16285 FOR_EACH_OBSERVER(Observer, observers_,
[email protected]fed38252011-07-08 17:26:50286 SelectFileDialogDisplayed(download_id));
initial.commit09911bf2008-07-26 23:55:29287 } else {
288 // No prompting for download, just continue with the suggested name.
[email protected]4cd82f72011-05-23 19:15:01289 ContinueDownloadWithPath(download, suggested_path);
initial.commit09911bf2008-07-26 23:55:29290 }
291}
292
[email protected]c2e76012010-12-23 21:10:29293void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) {
294 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
295
296 DownloadItem* download = new DownloadItem(this, *info,
[email protected]6d0c9fb2011-08-22 19:26:03297 browser_context_->IsOffTheRecord());
[email protected]4cd82f72011-05-23 19:15:01298 int32 download_id = info->download_id;
299 DCHECK(!ContainsKey(in_progress_, download_id));
[email protected]d8472d92011-08-26 20:15:20300
301 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved.
[email protected]821960a2011-08-23 20:40:03302 CHECK(!ContainsKey(active_downloads_, download_id));
[email protected]c2e76012010-12-23 21:10:29303 downloads_.insert(download);
[email protected]4cd82f72011-05-23 19:15:01304 active_downloads_[download_id] = download;
[email protected]c2e76012010-12-23 21:10:29305}
306
[email protected]4cd82f72011-05-23 19:15:01307void DownloadManager::ContinueDownloadWithPath(DownloadItem* download,
308 const FilePath& chosen_file) {
[email protected]ca4b5fa32010-10-09 12:42:18309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]4cd82f72011-05-23 19:15:01310 DCHECK(download);
[email protected]aa033af2010-07-27 18:16:39311
[email protected]4cd82f72011-05-23 19:15:01312 int32 download_id = download->id();
initial.commit09911bf2008-07-26 23:55:29313
[email protected]70850c72011-01-11 17:31:27314 // NOTE(ahendrickson) Eventually |active_downloads_| will replace
315 // |in_progress_|, but we don't want to change the semantics yet.
[email protected]4cd82f72011-05-23 19:15:01316 DCHECK(!ContainsKey(in_progress_, download_id));
[email protected]70850c72011-01-11 17:31:27317 DCHECK(ContainsKey(downloads_, download));
[email protected]4cd82f72011-05-23 19:15:01318 DCHECK(ContainsKey(active_downloads_, download_id));
[email protected]70850c72011-01-11 17:31:27319
[email protected]4cd82f72011-05-23 19:15:01320 // Make sure the initial file name is set only once.
321 DCHECK(download->full_path().empty());
322 download->OnPathDetermined(chosen_file);
[email protected]4cd82f72011-05-23 19:15:01323
324 VLOG(20) << __FUNCTION__ << "()"
325 << " download = " << download->DebugString(true);
326
327 in_progress_[download_id] = download;
[email protected]5f8589fe2011-08-17 20:58:39328 UpdateDownloadProgress(); // Reflect entry into in_progress_.
initial.commit09911bf2008-07-26 23:55:29329
[email protected]adb2f3d12011-01-23 16:24:54330 // Rename to intermediate name.
[email protected]f5920322011-03-24 20:34:16331 FilePath download_path;
[email protected]ec865262011-08-23 20:01:48332 if (!delegate_->OverrideIntermediatePath(download, &download_path))
[email protected]4cd82f72011-05-23 19:15:01333 download_path = download->full_path();
[email protected]594cd7d2010-07-21 03:23:56334
[email protected]f5920322011-03-24 20:34:16335 BrowserThread::PostTask(
336 BrowserThread::FILE, FROM_HERE,
337 NewRunnableMethod(
338 file_manager_, &DownloadFileManager::RenameInProgressDownloadFile,
[email protected]c2475df2011-09-07 23:52:22339 download->id(), download_path));
[email protected]f5920322011-03-24 20:34:16340
341 download->Rename(download_path);
342
[email protected]2588ea9d2011-08-22 20:59:53343 delegate_->AddItemToPersistentStore(download);
initial.commit09911bf2008-07-26 23:55:29344}
345
initial.commit09911bf2008-07-26 23:55:29346void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
[email protected]70850c72011-01-11 17:31:27347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
348 DownloadMap::iterator it = active_downloads_.find(download_id);
349 if (it != active_downloads_.end()) {
initial.commit09911bf2008-07-26 23:55:29350 DownloadItem* download = it->second;
[email protected]bf68a00b2011-04-07 17:28:26351 if (download->IsInProgress()) {
[email protected]70850c72011-01-11 17:31:27352 download->Update(size);
[email protected]5f8589fe2011-08-17 20:58:39353 UpdateDownloadProgress(); // Reflect size updates.
[email protected]2588ea9d2011-08-22 20:59:53354 delegate_->UpdateItemInPersistentStore(download);
[email protected]70850c72011-01-11 17:31:27355 }
initial.commit09911bf2008-07-26 23:55:29356 }
357}
358
[email protected]bf68a00b2011-04-07 17:28:26359void DownloadManager::OnResponseCompleted(int32 download_id,
360 int64 size,
[email protected]bf68a00b2011-04-07 17:28:26361 const std::string& hash) {
[email protected]33c6d3f12011-09-04 00:00:54362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]da6e3922010-11-24 21:45:50363 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
364 << " size = " << size;
[email protected]9d7ef802011-02-25 19:03:35365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]9ccbb372008-10-10 18:50:32366
[email protected]c4f02c42011-01-24 21:55:06367 // If it's not in active_downloads_, that means it was cancelled; just
368 // ignore the notification.
369 if (active_downloads_.count(download_id) == 0)
370 return;
371
[email protected]adb2f3d12011-01-23 16:24:54372 DownloadItem* download = active_downloads_[download_id];
[email protected]a850ba42010-09-10 22:00:30373 download->OnAllDataSaved(size);
[email protected]9ccbb372008-10-10 18:50:32374
[email protected]b09f1282011-09-14 00:37:45375 delegate_->OnResponseCompleted(download, hash);
376
[email protected]adb2f3d12011-01-23 16:24:54377 MaybeCompleteDownload(download);
378}
[email protected]9ccbb372008-10-10 18:50:32379
[email protected]7d413112011-06-16 18:50:17380void DownloadManager::AssertQueueStateConsistent(DownloadItem* download) {
[email protected]5cd11b6e2011-06-10 20:30:59381 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
[email protected]7d413112011-06-16 18:50:17382 if (download->state() == DownloadItem::REMOVING) {
383 CHECK(!ContainsKey(downloads_, download));
384 CHECK(!ContainsKey(active_downloads_, download->id()));
385 CHECK(!ContainsKey(in_progress_, download->id()));
386 CHECK(!ContainsKey(history_downloads_, download->db_handle()));
387 return;
388 }
389
390 // Should be in downloads_ if we're not REMOVING.
391 CHECK(ContainsKey(downloads_, download));
392
393 // Check history_downloads_ consistency.
[email protected]2588ea9d2011-08-22 20:59:53394 if (download->db_handle() != DownloadItem::kUninitializedHandle) {
[email protected]7d413112011-06-16 18:50:17395 CHECK(ContainsKey(history_downloads_, download->db_handle()));
396 } else {
397 // TODO(rdsmith): Somewhat painful; make sure to disable in
398 // release builds after resolution of http://crbug.com/85408.
399 for (DownloadMap::iterator it = history_downloads_.begin();
400 it != history_downloads_.end(); ++it) {
401 CHECK(it->second != download);
402 }
403 }
404
[email protected]61b75a52011-08-29 16:34:34405 int64 state = download->state();
406 base::debug::Alias(&state);
[email protected]821960a2011-08-23 20:40:03407 if (ContainsKey(active_downloads_, download->id()))
408 CHECK_EQ(DownloadItem::IN_PROGRESS, download->state());
409 if (DownloadItem::IN_PROGRESS == download->state())
410 CHECK(ContainsKey(active_downloads_, download->id()));
[email protected]5cd11b6e2011-06-10 20:30:59411}
412
[email protected]adb2f3d12011-01-23 16:24:54413bool DownloadManager::IsDownloadReadyForCompletion(DownloadItem* download) {
414 // If we don't have all the data, the download is not ready for
415 // completion.
416 if (!download->all_data_saved())
417 return false;
[email protected]6a7fb042010-02-01 16:30:47418
[email protected]9d7ef802011-02-25 19:03:35419 // If the download is dangerous, but not yet validated, it's not ready for
420 // completion.
421 if (download->safety_state() == DownloadItem::DANGEROUS)
422 return false;
423
[email protected]adb2f3d12011-01-23 16:24:54424 // If the download isn't active (e.g. has been cancelled) it's not
425 // ready for completion.
426 if (active_downloads_.count(download->id()) == 0)
427 return false;
428
429 // If the download hasn't been inserted into the history system
430 // (which occurs strictly after file name determination, intermediate
431 // file rename, and UI display) then it's not ready for completion.
[email protected]2588ea9d2011-08-22 20:59:53432 if (download->db_handle() == DownloadItem::kUninitializedHandle)
[email protected]7054b592011-06-22 14:46:42433 return false;
434
435 return true;
[email protected]adb2f3d12011-01-23 16:24:54436}
437
438void DownloadManager::MaybeCompleteDownload(DownloadItem* download) {
439 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
440 VLOG(20) << __FUNCTION__ << "()" << " download = "
441 << download->DebugString(false);
442
443 if (!IsDownloadReadyForCompletion(download))
[email protected]9ccbb372008-10-10 18:50:32444 return;
[email protected]9ccbb372008-10-10 18:50:32445
[email protected]adb2f3d12011-01-23 16:24:54446 // TODO(rdsmith): DCHECK that we only pass through this point
447 // once per download. The natural way to do this is by a state
448 // transition on the DownloadItem.
[email protected]594cd7d2010-07-21 03:23:56449
[email protected]adb2f3d12011-01-23 16:24:54450 // Confirm we're in the proper set of states to be here;
[email protected]9d7ef802011-02-25 19:03:35451 // in in_progress_, have all data, have a history handle, (validated or safe).
452 DCHECK_NE(DownloadItem::DANGEROUS, download->safety_state());
[email protected]adb2f3d12011-01-23 16:24:54453 DCHECK_EQ(1u, in_progress_.count(download->id()));
454 DCHECK(download->all_data_saved());
[email protected]2588ea9d2011-08-22 20:59:53455 DCHECK(download->db_handle() != DownloadItem::kUninitializedHandle);
[email protected]adb2f3d12011-01-23 16:24:54456 DCHECK_EQ(1u, history_downloads_.count(download->db_handle()));
457
458 VLOG(20) << __FUNCTION__ << "()" << " executing: download = "
459 << download->DebugString(false);
460
461 // Remove the id from in_progress
462 in_progress_.erase(download->id());
[email protected]5f8589fe2011-08-17 20:58:39463 UpdateDownloadProgress(); // Reflect removal from in_progress_.
[email protected]adb2f3d12011-01-23 16:24:54464
[email protected]2588ea9d2011-08-22 20:59:53465 delegate_->UpdateItemInPersistentStore(download);
[email protected]adb2f3d12011-01-23 16:24:54466
[email protected]f5920322011-03-24 20:34:16467 // Finish the download.
[email protected]48837962011-04-19 17:03:29468 download->OnDownloadCompleting(file_manager_);
[email protected]9ccbb372008-10-10 18:50:32469}
470
[email protected]cc3c7c092011-05-09 18:40:21471void DownloadManager::DownloadCompleted(int32 download_id) {
[email protected]70850c72011-01-11 17:31:27472 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cc3c7c092011-05-09 18:40:21473 DownloadItem* download = GetDownloadItem(download_id);
474 DCHECK(download);
[email protected]2588ea9d2011-08-22 20:59:53475 delegate_->UpdateItemInPersistentStore(download);
[email protected]70850c72011-01-11 17:31:27476 active_downloads_.erase(download_id);
[email protected]821960a2011-08-23 20:40:03477 AssertQueueStateConsistent(download);
[email protected]70850c72011-01-11 17:31:27478}
479
[email protected]f5920322011-03-24 20:34:16480void DownloadManager::OnDownloadRenamedToFinalName(int download_id,
481 const FilePath& full_path,
482 int uniquifier) {
[email protected]da6e3922010-11-24 21:45:50483 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
[email protected]f5920322011-03-24 20:34:16484 << " full_path = \"" << full_path.value() << "\""
485 << " uniquifier = " << uniquifier;
[email protected]ca4b5fa32010-10-09 12:42:18486 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]f5920322011-03-24 20:34:16487
[email protected]2e030682010-07-23 19:45:36488 DownloadItem* item = GetDownloadItem(download_id);
489 if (!item)
490 return;
[email protected]6cade212008-12-03 00:32:22491
[email protected]8fa1eeb52011-04-13 14:18:02492 if (item->safety_state() == DownloadItem::SAFE) {
493 DCHECK_EQ(0, uniquifier) << "We should not uniquify SAFE downloads twice";
494 }
495
[email protected]c2475df2011-09-07 23:52:22496 BrowserThread::PostTask(
497 BrowserThread::FILE, FROM_HERE,
498 NewRunnableMethod(
499 file_manager_, &DownloadFileManager::CompleteDownload, download_id));
[email protected]9ccbb372008-10-10 18:50:32500
[email protected]f5920322011-03-24 20:34:16501 if (uniquifier)
502 item->set_path_uniquifier(uniquifier);
[email protected]9ccbb372008-10-10 18:50:32503
[email protected]f5920322011-03-24 20:34:16504 item->OnDownloadRenamedToFinalName(full_path);
[email protected]2588ea9d2011-08-22 20:59:53505 delegate_->UpdatePathForItemInPersistentStore(item, full_path);
initial.commit09911bf2008-07-26 23:55:29506}
507
[email protected]93af2272011-09-21 18:29:17508void DownloadManager::CancelDownload(int32 download_id) {
509 DownloadItem* download = GetActiveDownload(download_id);
510 // A cancel at the right time could remove the download from the
511 // |active_downloads_| map before we get here.
512 if (!download)
513 return;
514
515 download->Cancel(true);
516}
517
518void DownloadManager::DownloadCancelledInternal(DownloadItem* download) {
[email protected]d8472d92011-08-26 20:15:20519 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]d8472d92011-08-26 20:15:20520
521 VLOG(20) << __FUNCTION__ << "()"
[email protected]da6e3922010-11-24 21:45:50522 << " download = " << download->DebugString(true);
523
[email protected]93af2272011-09-21 18:29:17524 RemoveFromActiveList(download);
[email protected]47a881b2011-08-29 22:59:21525 // This function is called from the DownloadItem, so DI state
526 // should already have been updated.
527 AssertQueueStateConsistent(download);
initial.commit09911bf2008-07-26 23:55:29528
[email protected]d8472d92011-08-26 20:15:20529 download->OffThreadCancel(file_manager_);
initial.commit09911bf2008-07-26 23:55:29530}
531
[email protected]bf68a00b2011-04-07 17:28:26532void DownloadManager::OnDownloadError(int32 download_id,
533 int64 size,
[email protected]33c6d3f12011-09-04 00:00:54534 net::Error error) {
[email protected]47a881b2011-08-29 22:59:21535 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
536
537 DownloadItem* download = GetActiveDownload(download_id);
538 if (!download)
539 return;
540
541 VLOG(20) << __FUNCTION__ << "()" << " Error " << error
542 << " at offset " << download->received_bytes()
543 << " size = " << size
544 << " download = " << download->DebugString(true);
545
[email protected]93af2272011-09-21 18:29:17546 RemoveFromActiveList(download);
547 download->Interrupted(size, error);
548 download->OffThreadCancel(file_manager_);
[email protected]47a881b2011-08-29 22:59:21549}
550
551DownloadItem* DownloadManager::GetActiveDownload(int32 download_id) {
[email protected]bf68a00b2011-04-07 17:28:26552 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
553 DownloadMap::iterator it = active_downloads_.find(download_id);
[email protected]bf68a00b2011-04-07 17:28:26554 if (it == active_downloads_.end())
[email protected]47a881b2011-08-29 22:59:21555 return NULL;
[email protected]bf68a00b2011-04-07 17:28:26556
557 DownloadItem* download = it->second;
558
[email protected]47a881b2011-08-29 22:59:21559 DCHECK(download);
560 DCHECK_EQ(download_id, download->id());
[email protected]4cd82f72011-05-23 19:15:01561
[email protected]47a881b2011-08-29 22:59:21562 return download;
563}
[email protected]54610672011-07-18 18:24:43564
[email protected]93af2272011-09-21 18:29:17565void DownloadManager::RemoveFromActiveList(DownloadItem* download) {
566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
567 DCHECK(download);
568
569 // Clean up will happen when the history system create callback runs if we
570 // don't have a valid db_handle yet.
571 if (download->db_handle() != DownloadItem::kUninitializedHandle) {
572 in_progress_.erase(download->id());
573 active_downloads_.erase(download->id());
574 UpdateDownloadProgress(); // Reflect removal from in_progress_.
575 delegate_->UpdateItemInPersistentStore(download);
576 }
577}
578
[email protected]5f8589fe2011-08-17 20:58:39579void DownloadManager::UpdateDownloadProgress() {
580 delegate_->DownloadProgressUpdated();
[email protected]6a7fb042010-02-01 16:30:47581}
582
[email protected]6d0146c2011-08-04 19:13:04583int DownloadManager::RemoveDownloadItems(
584 const DownloadVector& pending_deletes) {
585 if (pending_deletes.empty())
586 return 0;
587
588 // Delete from internal maps.
589 for (DownloadVector::const_iterator it = pending_deletes.begin();
590 it != pending_deletes.end();
591 ++it) {
592 DownloadItem* download = *it;
593 DCHECK(download);
594 history_downloads_.erase(download->db_handle());
595 save_page_downloads_.erase(download->id());
596 downloads_.erase(download);
597 }
598
599 // Tell observers to refresh their views.
600 NotifyModelChanged();
601
602 // Delete the download items themselves.
603 const int num_deleted = static_cast<int>(pending_deletes.size());
604 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
605 return num_deleted;
606}
607
[email protected]93af2272011-09-21 18:29:17608void DownloadManager::RemoveDownload(int64 download_handle) {
609 DownloadMap::iterator it = history_downloads_.find(download_handle);
610 if (it == history_downloads_.end())
611 return;
612
613 // Make history update.
614 DownloadItem* download = it->second;
615 delegate_->RemoveItemFromPersistentStore(download);
initial.commit09911bf2008-07-26 23:55:29616
617 // Remove from our tables and delete.
[email protected]6d0146c2011-08-04 19:13:04618 int downloads_count = RemoveDownloadItems(DownloadVector(1, download));
[email protected]f04182f32010-12-10 19:12:07619 DCHECK_EQ(1, downloads_count);
initial.commit09911bf2008-07-26 23:55:29620}
621
[email protected]e93d2822009-01-30 05:59:59622int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin,
623 const base::Time remove_end) {
[email protected]2588ea9d2011-08-22 20:59:53624 delegate_->RemoveItemsFromPersistentStoreBetween(remove_begin, remove_end);
initial.commit09911bf2008-07-26 23:55:29625
[email protected]a312a442010-12-15 23:40:33626 // All downloads visible to the user will be in the history,
627 // so scan that map.
[email protected]6d0146c2011-08-04 19:13:04628 DownloadVector pending_deletes;
629 for (DownloadMap::const_iterator it = history_downloads_.begin();
630 it != history_downloads_.end();
631 ++it) {
initial.commit09911bf2008-07-26 23:55:29632 DownloadItem* download = it->second;
initial.commit09911bf2008-07-26 23:55:29633 if (download->start_time() >= remove_begin &&
634 (remove_end.is_null() || download->start_time() < remove_end) &&
[email protected]6d0146c2011-08-04 19:13:04635 (download->IsComplete() || download->IsCancelled())) {
[email protected]7d413112011-06-16 18:50:17636 AssertQueueStateConsistent(download);
[email protected]78b8fcc92009-03-31 17:36:28637 pending_deletes.push_back(download);
initial.commit09911bf2008-07-26 23:55:29638 }
initial.commit09911bf2008-07-26 23:55:29639 }
[email protected]6d0146c2011-08-04 19:13:04640 return RemoveDownloadItems(pending_deletes);
initial.commit09911bf2008-07-26 23:55:29641}
642
[email protected]e93d2822009-01-30 05:59:59643int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
644 return RemoveDownloadsBetween(remove_begin, base::Time());
initial.commit09911bf2008-07-26 23:55:29645}
646
[email protected]d41355e6f2009-04-07 21:21:12647int DownloadManager::RemoveAllDownloads() {
648 // The null times make the date range unbounded.
649 return RemoveDownloadsBetween(base::Time(), base::Time());
650}
651
initial.commit09911bf2008-07-26 23:55:29652// Initiate a download of a specific URL. We send the request to the
653// ResourceDispatcherHost, and let it send us responses like a regular
654// download.
655void DownloadManager::DownloadUrl(const GURL& url,
656 const GURL& referrer,
[email protected]c9825a42009-05-01 22:51:50657 const std::string& referrer_charset,
[email protected]57c6a652009-05-04 07:58:34658 TabContents* tab_contents) {
[email protected]ae8945192010-07-20 16:56:26659 DownloadUrlToFile(url, referrer, referrer_charset, DownloadSaveInfo(),
660 tab_contents);
[email protected]6aa4a1c02010-01-15 18:49:58661}
662
663void DownloadManager::DownloadUrlToFile(const GURL& url,
664 const GURL& referrer,
665 const std::string& referrer_charset,
[email protected]8af9d032010-02-10 00:00:32666 const DownloadSaveInfo& save_info,
[email protected]6aa4a1c02010-01-15 18:49:58667 TabContents* tab_contents) {
[email protected]57c6a652009-05-04 07:58:34668 DCHECK(tab_contents);
[email protected]c79a0c02011-08-22 22:37:37669 ResourceDispatcherHost* resource_dispatcher_host =
670 content::GetContentClient()->browser()->GetResourceDispatcherHost();
[email protected]ed24fad2011-05-10 22:44:01671 // We send a pointer to content::ResourceContext, instead of the usual
672 // reference, so that a copy of the object isn't made.
[email protected]ca4b5fa32010-10-09 12:42:18673 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
[email protected]a0ce3282011-08-19 20:49:52674 NewRunnableFunction(&BeginDownload,
[email protected]ae8945192010-07-20 16:56:26675 url,
676 referrer,
[email protected]ae8945192010-07-20 16:56:26677 save_info,
[email protected]c79a0c02011-08-22 22:37:37678 resource_dispatcher_host,
[email protected]ae8945192010-07-20 16:56:26679 tab_contents->GetRenderProcessHost()->id(),
680 tab_contents->render_view_host()->routing_id(),
[email protected]cafe4ad2011-07-28 18:34:56681 &tab_contents->browser_context()->
682 GetResourceContext()));
initial.commit09911bf2008-07-26 23:55:29683}
684
initial.commit09911bf2008-07-26 23:55:29685void DownloadManager::AddObserver(Observer* observer) {
686 observers_.AddObserver(observer);
687 observer->ModelChanged();
688}
689
690void DownloadManager::RemoveObserver(Observer* observer) {
691 observers_.RemoveObserver(observer);
692}
693
[email protected]073ed7b2010-09-27 09:20:02694bool DownloadManager::IsDownloadProgressKnown() {
695 for (DownloadMap::iterator i = in_progress_.begin();
696 i != in_progress_.end(); ++i) {
697 if (i->second->total_bytes() <= 0)
698 return false;
699 }
700
701 return true;
702}
703
704int64 DownloadManager::GetInProgressDownloadCount() {
705 return in_progress_.size();
706}
707
708int64 DownloadManager::GetReceivedDownloadBytes() {
709 DCHECK(IsDownloadProgressKnown());
710 int64 received_bytes = 0;
711 for (DownloadMap::iterator i = in_progress_.begin();
712 i != in_progress_.end(); ++i) {
713 received_bytes += i->second->received_bytes();
714 }
715 return received_bytes;
716}
717
718int64 DownloadManager::GetTotalDownloadBytes() {
719 DCHECK(IsDownloadProgressKnown());
720 int64 total_bytes = 0;
721 for (DownloadMap::iterator i = in_progress_.begin();
722 i != in_progress_.end(); ++i) {
723 total_bytes += i->second->total_bytes();
724 }
725 return total_bytes;
726}
727
[email protected]99cb7f82011-07-28 17:27:26728void DownloadManager::FileSelected(const FilePath& path, void* params) {
[email protected]4cd82f72011-05-23 19:15:01729 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
730
731 int32* id_ptr = reinterpret_cast<int32*>(params);
732 DCHECK(id_ptr != NULL);
733 int32 download_id = *id_ptr;
734 delete id_ptr;
735
736 DownloadItem* download = GetActiveDownloadItem(download_id);
737 if (!download)
738 return;
739 VLOG(20) << __FUNCTION__ << "()" << " path = \"" << path.value() << "\""
740 << " download = " << download->DebugString(true);
741
[email protected]da1a27b2011-07-29 23:16:33742 if (download->prompt_user_for_save_location())
[email protected]7ae7c2cb2009-01-06 23:31:41743 last_download_path_ = path.DirName();
[email protected]287b86b2011-02-26 00:11:35744
[email protected]4cd82f72011-05-23 19:15:01745 // Make sure the initial file name is set only once.
746 ContinueDownloadWithPath(download, path);
initial.commit09911bf2008-07-26 23:55:29747}
748
749void DownloadManager::FileSelectionCanceled(void* params) {
750 // The user didn't pick a place to save the file, so need to cancel the
751 // download that's already in progress to the temporary location.
[email protected]4cd82f72011-05-23 19:15:01752 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
753 int32* id_ptr = reinterpret_cast<int32*>(params);
754 DCHECK(id_ptr != NULL);
755 int32 download_id = *id_ptr;
756 delete id_ptr;
757
758 DownloadItem* download = GetActiveDownloadItem(download_id);
759 if (!download)
760 return;
761
762 VLOG(20) << __FUNCTION__ << "()"
763 << " download = " << download->DebugString(true);
764
[email protected]93af2272011-09-21 18:29:17765 // TODO(ahendrickson) -- This currently has no effect, as the download is
766 // not put on the active list until the file selection is complete. Need
767 // to put it on the active list earlier in the process.
768 RemoveFromActiveList(download);
769
770 download->OffThreadCancel(file_manager_);
[email protected]4cd82f72011-05-23 19:15:01771}
772
initial.commit09911bf2008-07-26 23:55:29773// Operations posted to us from the history service ----------------------------
774
775// The history service has retrieved all download entries. 'entries' contains
[email protected]bb1a4212011-08-22 22:38:25776// 'DownloadPersistentStoreInfo's in sorted order (by ascending start_time).
[email protected]2588ea9d2011-08-22 20:59:53777void DownloadManager::OnPersistentStoreQueryComplete(
[email protected]bb1a4212011-08-22 22:38:25778 std::vector<DownloadPersistentStoreInfo>* entries) {
[email protected]d8472d92011-08-26 20:15:20779 // TODO(rdsmith): Remove this and related logic when
780 // http://crbug.com/84508 is fixed.
781 largest_db_handle_in_history_ = 0;
782
initial.commit09911bf2008-07-26 23:55:29783 for (size_t i = 0; i < entries->size(); ++i) {
[email protected]aa033af2010-07-27 18:16:39784 DownloadItem* download = new DownloadItem(this, entries->at(i));
[email protected]d8472d92011-08-26 20:15:20785 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved.
[email protected]821960a2011-08-23 20:40:03786 CHECK(!ContainsKey(history_downloads_, download->db_handle()));
[email protected]f04182f32010-12-10 19:12:07787 downloads_.insert(download);
788 history_downloads_[download->db_handle()] = download;
[email protected]da6e3922010-11-24 21:45:50789 VLOG(20) << __FUNCTION__ << "()" << i << ">"
790 << " download = " << download->DebugString(true);
[email protected]d8472d92011-08-26 20:15:20791
792 if (download->db_handle() > largest_db_handle_in_history_)
793 largest_db_handle_in_history_ = download->db_handle();
initial.commit09911bf2008-07-26 23:55:29794 }
[email protected]b0ab1d42010-02-24 19:29:28795 NotifyModelChanged();
[email protected]9fc114672011-06-15 08:17:48796 CheckForHistoryFilesRemoval();
initial.commit09911bf2008-07-26 23:55:29797}
798
[email protected]f9a45b02011-06-30 23:49:19799void DownloadManager::AddDownloadItemToHistory(DownloadItem* download,
800 int64 db_handle) {
[email protected]70850c72011-01-11 17:31:27801 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]d2a8fb72010-01-21 05:31:42802
[email protected]1e9fe7ff2011-06-24 17:37:33803 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/84508
804 // is fixed.
[email protected]2588ea9d2011-08-22 20:59:53805 CHECK_NE(DownloadItem::kUninitializedHandle, db_handle);
[email protected]1e9fe7ff2011-06-24 17:37:33806
[email protected]2588ea9d2011-08-22 20:59:53807 DCHECK(download->db_handle() == DownloadItem::kUninitializedHandle);
[email protected]5bcd73eb2011-03-23 21:14:02808 download->set_db_handle(db_handle);
809
[email protected]d8472d92011-08-26 20:15:20810 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/84508
811 // is fixed.
[email protected]821960a2011-08-23 20:40:03812 CHECK(!ContainsKey(history_downloads_, download->db_handle()));
[email protected]5bcd73eb2011-03-23 21:14:02813 history_downloads_[download->db_handle()] = download;
[email protected]6d0146c2011-08-04 19:13:04814
815 // Show in the appropriate browser UI.
816 // This includes buttons to save or cancel, for a dangerous download.
817 ShowDownloadInBrowser(download);
818
819 // Inform interested objects about the new download.
820 NotifyModelChanged();
[email protected]f9a45b02011-06-30 23:49:19821}
822
[email protected]2588ea9d2011-08-22 20:59:53823
824void DownloadManager::OnItemAddedToPersistentStore(int32 download_id,
825 int64 db_handle) {
826 if (save_page_downloads_.count(download_id)) {
827 OnSavePageItemAddedToPersistentStore(download_id, db_handle);
828 } else if (active_downloads_.count(download_id)) {
829 OnDownloadItemAddedToPersistentStore(download_id, db_handle);
830 }
831 // It's valid that we don't find a matching item, i.e. on shutdown.
832}
833
[email protected]f9a45b02011-06-30 23:49:19834// Once the new DownloadItem's creation info has been committed to the history
835// service, we associate the DownloadItem with the db handle, update our
836// 'history_downloads_' map and inform observers.
[email protected]2588ea9d2011-08-22 20:59:53837void DownloadManager::OnDownloadItemAddedToPersistentStore(int32 download_id,
838 int64 db_handle) {
[email protected]f9a45b02011-06-30 23:49:19839 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]19420cc2011-07-18 17:43:45840 DownloadItem* download = GetActiveDownloadItem(download_id);
[email protected]93af2272011-09-21 18:29:17841 if (!download)
[email protected]19420cc2011-07-18 17:43:45842 return;
[email protected]54610672011-07-18 18:24:43843
844 VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle
845 << " download_id = " << download_id
846 << " download = " << download->DebugString(true);
[email protected]f9a45b02011-06-30 23:49:19847
[email protected]d8472d92011-08-26 20:15:20848 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved.
[email protected]d8472d92011-08-26 20:15:20849 int64 largest_handle = largest_db_handle_in_history_;
850 base::debug::Alias(&largest_handle);
[email protected]e5107ce2011-09-19 20:36:13851 int32 matching_item_download_id
852 = (ContainsKey(history_downloads_, db_handle) ?
853 history_downloads_[db_handle]->id() : 0);
854 base::debug::Alias(&matching_item_download_id);
855
[email protected]61b75a52011-08-29 16:34:34856 CHECK(!ContainsKey(history_downloads_, db_handle));
[email protected]d8472d92011-08-26 20:15:20857
[email protected]f9a45b02011-06-30 23:49:19858 AddDownloadItemToHistory(download, db_handle);
initial.commit09911bf2008-07-26 23:55:29859
[email protected]93af2272011-09-21 18:29:17860 // If the download is still in progress, try to complete it.
861 //
862 // Otherwise, download has been cancelled or interrupted before we've
863 // received the DB handle. We post one final message to the history
864 // service so that it can be properly in sync with the DownloadItem's
865 // completion status, and also inform any observers so that they get
866 // more than just the start notification.
867 if (download->IsInProgress()) {
868 MaybeCompleteDownload(download);
869 } else {
870 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/84508
871 // is fixed.
872 CHECK(download->IsCancelled())
873 << " download = " << download->DebugString(true);
874 in_progress_.erase(download_id);
875 active_downloads_.erase(download_id);
876 delegate_->UpdateItemInPersistentStore(download);
877 download->UpdateObservers();
878 }
initial.commit09911bf2008-07-26 23:55:29879}
880
[email protected]4cd82f72011-05-23 19:15:01881void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) {
[email protected]8ddbd66a2010-05-21 16:38:34882 // The 'contents' may no longer exist if the user closed the tab before we
[email protected]99cb7f82011-07-28 17:27:26883 // get this start completion event.
[email protected]db6831a2011-06-09 21:08:28884 DownloadRequestHandle request_handle = download->request_handle();
[email protected]686493142011-07-15 21:47:22885 TabContents* content = request_handle.GetTabContents();
[email protected]99cb7f82011-07-28 17:27:26886
887 // If the contents no longer exists, we ask the embedder to suggest another
888 // tab.
[email protected]da1a27b2011-07-29 23:16:33889 if (!content)
[email protected]aa9881c2011-08-15 18:01:12890 content = delegate_->GetAlternativeTabContentsToNotifyForDownload();
[email protected]5e595482009-05-06 20:16:53891
[email protected]99cb7f82011-07-28 17:27:26892 if (content)
893 content->OnStartDownload(download);
[email protected]5e595482009-05-06 20:16:53894}
895
[email protected]6cade212008-12-03 00:32:22896// Clears the last download path, used to initialize "save as" dialogs.
[email protected]905a08d2008-11-19 07:24:12897void DownloadManager::ClearLastDownloadPath() {
[email protected]7ae7c2cb2009-01-06 23:31:41898 last_download_path_ = FilePath();
[email protected]eea46622009-07-15 20:49:38899}
[email protected]b0ab1d42010-02-24 19:29:28900
901void DownloadManager::NotifyModelChanged() {
902 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
903}
904
[email protected]4cd82f72011-05-23 19:15:01905DownloadItem* DownloadManager::GetDownloadItem(int download_id) {
906 // The |history_downloads_| map is indexed by the download's db_handle,
907 // not its id, so we have to iterate.
[email protected]f04182f32010-12-10 19:12:07908 for (DownloadMap::iterator it = history_downloads_.begin();
909 it != history_downloads_.end(); ++it) {
[email protected]2e030682010-07-23 19:45:36910 DownloadItem* item = it->second;
[email protected]4cd82f72011-05-23 19:15:01911 if (item->id() == download_id)
[email protected]2e030682010-07-23 19:45:36912 return item;
913 }
914 return NULL;
915}
916
[email protected]4cd82f72011-05-23 19:15:01917DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) {
[email protected]93af2272011-09-21 18:29:17918 DCHECK(ContainsKey(active_downloads_, download_id));
[email protected]4cd82f72011-05-23 19:15:01919 DownloadItem* download = active_downloads_[download_id];
920 DCHECK(download != NULL);
921 return download;
922}
923
[email protected]57fd1252010-12-23 17:24:09924// Confirm that everything in all maps is also in |downloads_|, and that
925// everything in |downloads_| is also in some other map.
[email protected]f04182f32010-12-10 19:12:07926void DownloadManager::AssertContainersConsistent() const {
927#if !defined(NDEBUG)
[email protected]57fd1252010-12-23 17:24:09928 // Turn everything into sets.
[email protected]6d0146c2011-08-04 19:13:04929 const DownloadMap* input_maps[] = {&active_downloads_,
930 &history_downloads_,
931 &save_page_downloads_};
932 DownloadSet active_set, history_set, save_page_set;
933 DownloadSet* all_sets[] = {&active_set, &history_set, &save_page_set};
934 DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(all_sets));
[email protected]57fd1252010-12-23 17:24:09935 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input_maps); i++) {
936 for (DownloadMap::const_iterator it = input_maps[i]->begin();
[email protected]6d0146c2011-08-04 19:13:04937 it != input_maps[i]->end(); ++it) {
938 all_sets[i]->insert(&*it->second);
[email protected]f04182f32010-12-10 19:12:07939 }
940 }
[email protected]57fd1252010-12-23 17:24:09941
942 // Check if each set is fully present in downloads, and create a union.
[email protected]57fd1252010-12-23 17:24:09943 DownloadSet downloads_union;
944 for (int i = 0; i < static_cast<int>(ARRAYSIZE_UNSAFE(all_sets)); i++) {
945 DownloadSet remainder;
946 std::insert_iterator<DownloadSet> insert_it(remainder, remainder.begin());
947 std::set_difference(all_sets[i]->begin(), all_sets[i]->end(),
948 downloads_.begin(), downloads_.end(),
949 insert_it);
950 DCHECK(remainder.empty());
951 std::insert_iterator<DownloadSet>
952 insert_union(downloads_union, downloads_union.end());
953 std::set_union(downloads_union.begin(), downloads_union.end(),
954 all_sets[i]->begin(), all_sets[i]->end(),
955 insert_union);
956 }
957
958 // Is everything in downloads_ present in one of the other sets?
959 DownloadSet remainder;
960 std::insert_iterator<DownloadSet>
961 insert_remainder(remainder, remainder.begin());
962 std::set_difference(downloads_.begin(), downloads_.end(),
963 downloads_union.begin(), downloads_union.end(),
964 insert_remainder);
965 DCHECK(remainder.empty());
[email protected]f04182f32010-12-10 19:12:07966#endif
967}
968
[email protected]6d0146c2011-08-04 19:13:04969void DownloadManager::SavePageDownloadStarted(DownloadItem* download) {
970 DCHECK(!ContainsKey(save_page_downloads_, download->id()));
971 downloads_.insert(download);
972 save_page_downloads_[download->id()] = download;
973
974 // Add this entry to the history service.
975 // Additionally, the UI is notified in the callback.
[email protected]2588ea9d2011-08-22 20:59:53976 delegate_->AddItemToPersistentStore(download);
[email protected]6d0146c2011-08-04 19:13:04977}
978
979// SavePackage will call SavePageDownloadFinished upon completion/cancellation.
[email protected]2588ea9d2011-08-22 20:59:53980// The history callback will call OnSavePageItemAddedToPersistentStore.
[email protected]6d0146c2011-08-04 19:13:04981// If the download finishes before the history callback,
[email protected]2588ea9d2011-08-22 20:59:53982// OnSavePageItemAddedToPersistentStore calls SavePageDownloadFinished, ensuring
983// that the history event is update regardless of the order in which these two
984// events complete.
[email protected]6d0146c2011-08-04 19:13:04985// If something removes the download item from the download manager (Remove,
986// Shutdown) the result will be that the SavePage system will not be able to
987// properly update the download item (which no longer exists) or the download
988// history, but the action will complete properly anyway. This may lead to the
989// history entry being wrong on a reload of chrome (specifically in the case of
990// Initiation -> History Callback -> Removal -> Completion), but there's no way
991// to solve that without canceling on Remove (which would then update the DB).
992
[email protected]2588ea9d2011-08-22 20:59:53993void DownloadManager::OnSavePageItemAddedToPersistentStore(int32 download_id,
994 int64 db_handle) {
[email protected]6d0146c2011-08-04 19:13:04995 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
996
997 DownloadMap::const_iterator it = save_page_downloads_.find(download_id);
998 // This can happen if the download manager is shutting down and all maps
999 // have been cleared.
1000 if (it == save_page_downloads_.end())
1001 return;
1002
1003 DownloadItem* download = it->second;
1004 if (!download) {
1005 NOTREACHED();
1006 return;
1007 }
1008
[email protected]d8472d92011-08-26 20:15:201009 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved.
[email protected]d8472d92011-08-26 20:15:201010 int64 largest_handle = largest_db_handle_in_history_;
1011 base::debug::Alias(&largest_handle);
[email protected]61b75a52011-08-29 16:34:341012 CHECK(!ContainsKey(history_downloads_, db_handle));
[email protected]d8472d92011-08-26 20:15:201013
[email protected]6d0146c2011-08-04 19:13:041014 AddDownloadItemToHistory(download, db_handle);
1015
1016 // Finalize this download if it finished before the history callback.
1017 if (!download->IsInProgress())
1018 SavePageDownloadFinished(download);
1019}
1020
1021void DownloadManager::SavePageDownloadFinished(DownloadItem* download) {
[email protected]2588ea9d2011-08-22 20:59:531022 if (download->db_handle() != DownloadItem::kUninitializedHandle) {
1023 delegate_->UpdateItemInPersistentStore(download);
[email protected]6d0146c2011-08-04 19:13:041024 DCHECK(ContainsKey(save_page_downloads_, download->id()));
1025 save_page_downloads_.erase(download->id());
1026
1027 if (download->IsComplete())
1028 NotificationService::current()->Notify(
1029 content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
1030 Source<DownloadManager>(this),
1031 Details<DownloadItem>(download));
1032 }
1033}