blob: f1a730925229a790d46fe26b074be90ae79889b1 [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]5656f8a2011-11-17 16:12:585#include "content/browser/download/download_manager_impl.h"
initial.commit09911bf2008-07-26 23:55:296
[email protected]e7557f172011-08-19 23:42:017#include <iterator>
8
[email protected]eda58402011-09-21 19:32:029#include "base/bind.h"
[email protected]2041cf342010-02-19 03:15:5910#include "base/callback.h"
initial.commit09911bf2008-07-26 23:55:2911#include "base/file_util.h"
[email protected]503d03872011-05-06 08:36:2612#include "base/i18n/case_conversion.h"
initial.commit09911bf2008-07-26 23:55:2913#include "base/logging.h"
[email protected]7286e3fc2011-07-19 22:13:2414#include "base/stl_util.h"
[email protected]eda58402011-09-21 19:32:0215#include "base/stringprintf.h"
16#include "base/synchronization/lock.h"
17#include "base/sys_string_conversions.h"
[email protected]d2a8fb72010-01-21 05:31:4218#include "build/build_config.h"
[email protected]6d0c9fb2011-08-22 19:26:0319#include "content/browser/browser_context.h"
[email protected]71bf3f5e2011-08-15 21:05:2220#include "content/browser/download/download_create_info.h"
21#include "content/browser/download/download_file_manager.h"
[email protected]2909e342011-10-29 00:46:5322#include "content/browser/download/download_id_factory.h"
[email protected]71bf3f5e2011-08-15 21:05:2223#include "content/browser/download/download_item.h"
[email protected]bb1a4212011-08-22 22:38:2524#include "content/browser/download/download_persistent_store_info.h"
[email protected]da4a5582011-10-17 19:08:0625#include "content/browser/download/download_stats.h"
[email protected]71bf3f5e2011-08-15 21:05:2226#include "content/browser/download/download_status_updater.h"
[email protected]be76b7e2011-10-13 12:57:5727#include "content/browser/download/interrupt_reasons.h"
[email protected]7324d1d2011-03-01 05:02:1628#include "content/browser/renderer_host/render_view_host.h"
29#include "content/browser/renderer_host/resource_dispatcher_host.h"
30#include "content/browser/tab_contents/tab_contents.h"
[email protected]c38831a12011-10-28 12:44:4931#include "content/public/browser/browser_thread.h"
[email protected]87f3c082011-10-19 18:07:4432#include "content/public/browser/content_browser_client.h"
[email protected]1bd0ef12011-10-20 05:24:1733#include "content/public/browser/download_manager_delegate.h"
[email protected]ad50def52011-10-19 23:17:0734#include "content/public/browser/notification_service.h"
[email protected]0d6e9bd2011-10-18 04:29:1635#include "content/public/browser/notification_types.h"
[email protected]f3b1a082011-11-18 00:34:3036#include "content/public/browser/render_process_host.h"
initial.commit09911bf2008-07-26 23:55:2937
[email protected]631bb742011-11-02 11:29:3938using content::BrowserThread;
39
[email protected]a0ce3282011-08-19 20:49:5240namespace {
41
[email protected]fabf36d22011-10-28 21:50:1742// Param structs exist because base::Bind can only handle 6 args.
43struct URLParams {
44 URLParams(const GURL& url, const GURL& referrer)
45 : url_(url), referrer_(referrer) {}
46 GURL url_;
47 GURL referrer_;
48};
49
50struct RenderParams {
51 RenderParams(int rpi, int rvi)
52 : render_process_id_(rpi), render_view_id_(rvi) {}
53 int render_process_id_;
54 int render_view_id_;
55};
56
57void BeginDownload(const URLParams& url_params,
58 const DownloadSaveInfo& save_info,
59 ResourceDispatcherHost* resource_dispatcher_host,
60 const RenderParams& render_params,
61 const content::ResourceContext* context) {
62 net::URLRequest* request = new net::URLRequest(url_params.url_,
63 resource_dispatcher_host);
64 request->set_referrer(url_params.referrer_.spec());
[email protected]c79a0c02011-08-22 22:37:3765 resource_dispatcher_host->BeginDownload(
[email protected]fabf36d22011-10-28 21:50:1766 request, save_info, true,
[email protected]8e3ae68c2011-09-16 22:15:4767 DownloadResourceHandler::OnStartedCallback(),
[email protected]fabf36d22011-10-28 21:50:1768 render_params.render_process_id_,
69 render_params.render_view_id_,
[email protected]c79a0c02011-08-22 22:37:3770 *context);
[email protected]a0ce3282011-08-19 20:49:5271}
72
73} // namespace
74
[email protected]5656f8a2011-11-17 16:12:5875DownloadManagerImpl::DownloadManagerImpl(
76 content::DownloadManagerDelegate* delegate,
77 DownloadIdFactory* id_factory,
78 DownloadStatusUpdater* status_updater)
79 : shutdown_needed_(false),
80 browser_context_(NULL),
81 file_manager_(NULL),
82 status_updater_((status_updater != NULL)
83 ? status_updater->AsWeakPtr()
84 : base::WeakPtr<DownloadStatusUpdater>()),
85 delegate_(delegate),
86 id_factory_(id_factory),
87 largest_db_handle_in_history_(DownloadItem::kUninitializedHandle) {
[email protected]eda58402011-09-21 19:32:0288 // NOTE(benjhayden): status_updater may be NULL when using
89 // TestingBrowserProcess.
90 if (status_updater_.get() != NULL)
[email protected]073ed7b2010-09-27 09:20:0291 status_updater_->AddDelegate(this);
initial.commit09911bf2008-07-26 23:55:2992}
93
[email protected]5656f8a2011-11-17 16:12:5894DownloadManagerImpl::~DownloadManagerImpl() {
[email protected]326a6a92010-09-10 20:21:1395 DCHECK(!shutdown_needed_);
[email protected]eda58402011-09-21 19:32:0296 if (status_updater_.get() != NULL)
[email protected]073ed7b2010-09-27 09:20:0297 status_updater_->RemoveDelegate(this);
initial.commit09911bf2008-07-26 23:55:2998}
99
[email protected]5656f8a2011-11-17 16:12:58100DownloadId DownloadManagerImpl::GetNextId() {
[email protected]2909e342011-10-29 00:46:53101 return id_factory_->GetNextId();
102}
103
[email protected]5656f8a2011-11-17 16:12:58104void DownloadManagerImpl::Shutdown() {
[email protected]da6e3922010-11-24 21:45:50105 VLOG(20) << __FUNCTION__ << "()"
106 << " shutdown_needed_ = " << shutdown_needed_;
[email protected]326a6a92010-09-10 20:21:13107 if (!shutdown_needed_)
108 return;
109 shutdown_needed_ = false;
initial.commit09911bf2008-07-26 23:55:29110
[email protected]326a6a92010-09-10 20:21:13111 FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown());
[email protected]fb4f8d902011-09-16 06:07:08112 // TODO(benjhayden): Consider clearing observers_.
[email protected]326a6a92010-09-10 20:21:13113
114 if (file_manager_) {
[email protected]ca4b5fa32010-10-09 12:42:18115 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
[email protected]fabf36d22011-10-28 21:50:17116 base::Bind(&DownloadFileManager::OnDownloadManagerShutdown,
117 file_manager_, make_scoped_refptr(this)));
[email protected]326a6a92010-09-10 20:21:13118 }
initial.commit09911bf2008-07-26 23:55:29119
[email protected]f04182f32010-12-10 19:12:07120 AssertContainersConsistent();
121
122 // Go through all downloads in downloads_. Dangerous ones we need to
123 // remove on disk, and in progress ones we need to cancel.
[email protected]57fd1252010-12-23 17:24:09124 for (DownloadSet::iterator it = downloads_.begin(); it != downloads_.end();) {
[email protected]f04182f32010-12-10 19:12:07125 DownloadItem* download = *it;
126
127 // Save iterator from potential erases in this set done by called code.
128 // Iterators after an erasure point are still valid for lists and
129 // associative containers such as sets.
130 it++;
131
132 if (download->safety_state() == DownloadItem::DANGEROUS &&
[email protected]48837962011-04-19 17:03:29133 download->IsPartialDownload()) {
[email protected]f04182f32010-12-10 19:12:07134 // The user hasn't accepted it, so we need to remove it
135 // from the disk. This may or may not result in it being
136 // removed from the DownloadManager queues and deleted
137 // (specifically, DownloadManager::RemoveDownload only
138 // removes and deletes it if it's known to the history service)
139 // so the only thing we know after calling this function is that
140 // the download was deleted if-and-only-if it was removed
141 // from all queues.
[email protected]303077002011-04-19 23:21:01142 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN);
[email protected]bf68a00b2011-04-07 17:28:26143 } else if (download->IsPartialDownload()) {
[email protected]93af2272011-09-21 18:29:17144 download->Cancel(false);
145 delegate_->UpdateItemInPersistentStore(download);
initial.commit09911bf2008-07-26 23:55:29146 }
147 }
148
[email protected]f04182f32010-12-10 19:12:07149 // At this point, all dangerous downloads have had their files removed
150 // and all in progress downloads have been cancelled. We can now delete
151 // anything left.
[email protected]9ccbb372008-10-10 18:50:32152
[email protected]5cd11b6e2011-06-10 20:30:59153 // Copy downloads_ to separate container so as not to set off checks
154 // in DownloadItem destruction.
155 DownloadSet downloads_to_delete;
156 downloads_to_delete.swap(downloads_);
157
initial.commit09911bf2008-07-26 23:55:29158 in_progress_.clear();
[email protected]70850c72011-01-11 17:31:27159 active_downloads_.clear();
[email protected]5cd11b6e2011-06-10 20:30:59160 history_downloads_.clear();
[email protected]5cd11b6e2011-06-10 20:30:59161 STLDeleteElements(&downloads_to_delete);
initial.commit09911bf2008-07-26 23:55:29162
[email protected]6d0146c2011-08-04 19:13:04163 DCHECK(save_page_downloads_.empty());
164
initial.commit09911bf2008-07-26 23:55:29165 file_manager_ = NULL;
[email protected]2588ea9d2011-08-22 20:59:53166 delegate_->Shutdown();
[email protected]82f37b02010-07-29 22:04:57167
initial.commit09911bf2008-07-26 23:55:29168 shutdown_needed_ = false;
169}
170
[email protected]5656f8a2011-11-17 16:12:58171void DownloadManagerImpl::GetTemporaryDownloads(
[email protected]6d0146c2011-08-04 19:13:04172 const FilePath& dir_path, DownloadVector* result) {
[email protected]82f37b02010-07-29 22:04:57173 DCHECK(result);
[email protected]6aa4a1c02010-01-15 18:49:58174
[email protected]f04182f32010-12-10 19:12:07175 for (DownloadMap::iterator it = history_downloads_.begin();
176 it != history_downloads_.end(); ++it) {
[email protected]6aa4a1c02010-01-15 18:49:58177 if (it->second->is_temporary() &&
178 it->second->full_path().DirName() == dir_path)
[email protected]82f37b02010-07-29 22:04:57179 result->push_back(it->second);
[email protected]6aa4a1c02010-01-15 18:49:58180 }
[email protected]6aa4a1c02010-01-15 18:49:58181}
182
[email protected]5656f8a2011-11-17 16:12:58183void DownloadManagerImpl::GetAllDownloads(
[email protected]6d0146c2011-08-04 19:13:04184 const FilePath& dir_path, DownloadVector* result) {
[email protected]82f37b02010-07-29 22:04:57185 DCHECK(result);
[email protected]8ddbd66a2010-05-21 16:38:34186
[email protected]f04182f32010-12-10 19:12:07187 for (DownloadMap::iterator it = history_downloads_.begin();
188 it != history_downloads_.end(); ++it) {
[email protected]8ddbd66a2010-05-21 16:38:34189 if (!it->second->is_temporary() &&
190 (dir_path.empty() || it->second->full_path().DirName() == dir_path))
[email protected]82f37b02010-07-29 22:04:57191 result->push_back(it->second);
[email protected]8ddbd66a2010-05-21 16:38:34192 }
[email protected]8ddbd66a2010-05-21 16:38:34193}
194
[email protected]5656f8a2011-11-17 16:12:58195void DownloadManagerImpl::SearchDownloads(const string16& query,
196 DownloadVector* result) {
[email protected]503d03872011-05-06 08:36:26197 string16 query_lower(base::i18n::ToLower(query));
[email protected]d3b12902010-08-16 23:39:42198
[email protected]f04182f32010-12-10 19:12:07199 for (DownloadMap::iterator it = history_downloads_.begin();
200 it != history_downloads_.end(); ++it) {
[email protected]d3b12902010-08-16 23:39:42201 DownloadItem* download_item = it->second;
202
[email protected]8a282712011-08-23 19:28:00203 if (download_item->is_temporary())
[email protected]d3b12902010-08-16 23:39:42204 continue;
205
206 // Display Incognito downloads only in Incognito window, and vice versa.
207 // The Incognito Downloads page will get the list of non-Incognito downloads
208 // from its parent profile.
[email protected]6d0c9fb2011-08-22 19:26:03209 if (browser_context_->IsOffTheRecord() != download_item->is_otr())
[email protected]d3b12902010-08-16 23:39:42210 continue;
211
212 if (download_item->MatchesQuery(query_lower))
213 result->push_back(download_item);
214 }
[email protected]d3b12902010-08-16 23:39:42215}
216
initial.commit09911bf2008-07-26 23:55:29217// Query the history service for information about all persisted downloads.
[email protected]5656f8a2011-11-17 16:12:58218bool DownloadManagerImpl::Init(content::BrowserContext* browser_context) {
[email protected]6d0c9fb2011-08-22 19:26:03219 DCHECK(browser_context);
initial.commit09911bf2008-07-26 23:55:29220 DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
221 shutdown_needed_ = true;
222
[email protected]6d0c9fb2011-08-22 19:26:03223 browser_context_ = browser_context;
[email protected]024f2f02010-04-30 22:51:46224
[email protected]2941c2392010-07-15 22:54:30225 // In test mode, there may be no ResourceDispatcherHost. In this case it's
226 // safe to avoid setting |file_manager_| because we only call a small set of
227 // functions, none of which need it.
[email protected]a0ce3282011-08-19 20:49:52228 ResourceDispatcherHost* rdh =
229 content::GetContentClient()->browser()->GetResourceDispatcherHost();
[email protected]2941c2392010-07-15 22:54:30230 if (rdh) {
231 file_manager_ = rdh->download_file_manager();
232 DCHECK(file_manager_);
initial.commit09911bf2008-07-26 23:55:29233 }
234
initial.commit09911bf2008-07-26 23:55:29235 return true;
236}
237
[email protected]aa9881c2011-08-15 18:01:12238// We have received a message from DownloadFileManager about a new download.
[email protected]5656f8a2011-11-17 16:12:58239void DownloadManagerImpl::StartDownload(int32 download_id) {
[email protected]ca4b5fa32010-10-09 12:42:18240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]287b86b2011-02-26 00:11:35241
[email protected]aa9881c2011-08-15 18:01:12242 if (delegate_->ShouldStartDownload(download_id))
243 RestartDownload(download_id);
[email protected]287b86b2011-02-26 00:11:35244}
245
[email protected]5656f8a2011-11-17 16:12:58246void DownloadManagerImpl::CheckForHistoryFilesRemoval() {
[email protected]9fc114672011-06-15 08:17:48247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
248 for (DownloadMap::iterator it = history_downloads_.begin();
249 it != history_downloads_.end(); ++it) {
250 CheckForFileRemoval(it->second);
251 }
252}
253
[email protected]5656f8a2011-11-17 16:12:58254void DownloadManagerImpl::CheckForFileRemoval(DownloadItem* download_item) {
[email protected]9fc114672011-06-15 08:17:48255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256 if (download_item->IsComplete() &&
257 !download_item->file_externally_removed()) {
258 BrowserThread::PostTask(
259 BrowserThread::FILE, FROM_HERE,
[email protected]5656f8a2011-11-17 16:12:58260 base::Bind(&DownloadManagerImpl::CheckForFileRemovalOnFileThread,
[email protected]fabf36d22011-10-28 21:50:17261 this, download_item->db_handle(),
262 download_item->GetTargetFilePath()));
[email protected]9fc114672011-06-15 08:17:48263 }
264}
265
[email protected]5656f8a2011-11-17 16:12:58266void DownloadManagerImpl::CheckForFileRemovalOnFileThread(
[email protected]9fc114672011-06-15 08:17:48267 int64 db_handle, const FilePath& path) {
268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
269 if (!file_util::PathExists(path)) {
270 BrowserThread::PostTask(
271 BrowserThread::UI, FROM_HERE,
[email protected]5656f8a2011-11-17 16:12:58272 base::Bind(&DownloadManagerImpl::OnFileRemovalDetected,
273 this,
274 db_handle));
[email protected]9fc114672011-06-15 08:17:48275 }
276}
277
[email protected]5656f8a2011-11-17 16:12:58278void DownloadManagerImpl::OnFileRemovalDetected(int64 db_handle) {
[email protected]9fc114672011-06-15 08:17:48279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
280 DownloadMap::iterator it = history_downloads_.find(db_handle);
281 if (it != history_downloads_.end()) {
282 DownloadItem* download_item = it->second;
283 download_item->OnDownloadedFileRemoved();
284 }
285}
286
[email protected]5656f8a2011-11-17 16:12:58287void DownloadManagerImpl::RestartDownload(
[email protected]aa9881c2011-08-15 18:01:12288 int32 download_id) {
[email protected]ca4b5fa32010-10-09 12:42:18289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
initial.commit09911bf2008-07-26 23:55:29290
[email protected]4cd82f72011-05-23 19:15:01291 DownloadItem* download = GetActiveDownloadItem(download_id);
292 if (!download)
293 return;
294
295 VLOG(20) << __FUNCTION__ << "()"
296 << " download = " << download->DebugString(true);
297
[email protected]4cd82f72011-05-23 19:15:01298 FilePath suggested_path = download->suggested_path();
299
[email protected]da1a27b2011-07-29 23:16:33300 if (download->prompt_user_for_save_location()) {
initial.commit09911bf2008-07-26 23:55:29301 // We must ask the user for the place to put the download.
[email protected]ae77da82011-11-01 19:17:29302 TabContents* contents = download->GetTabContents();
[email protected]99cb7f82011-07-28 17:27:26303
[email protected]4cd82f72011-05-23 19:15:01304 // |id_ptr| will be deleted in either FileSelected() or
[email protected]93af2272011-09-21 18:29:17305 // FileSelectionCancelled().
[email protected]4cd82f72011-05-23 19:15:01306 int32* id_ptr = new int32;
307 *id_ptr = download_id;
[email protected]99cb7f82011-07-28 17:27:26308
[email protected]da1a27b2011-07-29 23:16:33309 delegate_->ChooseDownloadPath(
[email protected]aa9881c2011-08-15 18:01:12310 contents, suggested_path, reinterpret_cast<void*>(id_ptr));
[email protected]99cb7f82011-07-28 17:27:26311
[email protected]f5920322011-03-24 20:34:16312 FOR_EACH_OBSERVER(Observer, observers_,
[email protected]fed38252011-07-08 17:26:50313 SelectFileDialogDisplayed(download_id));
initial.commit09911bf2008-07-26 23:55:29314 } else {
315 // No prompting for download, just continue with the suggested name.
[email protected]4cd82f72011-05-23 19:15:01316 ContinueDownloadWithPath(download, suggested_path);
initial.commit09911bf2008-07-26 23:55:29317 }
318}
319
[email protected]5656f8a2011-11-17 16:12:58320content::BrowserContext* DownloadManagerImpl::BrowserContext() {
321 return browser_context_;
322}
323
324FilePath DownloadManagerImpl::LastDownloadPath() {
325 return last_download_path_;
326}
327
328void DownloadManagerImpl::CreateDownloadItem(
[email protected]594e66fe2011-10-25 22:49:41329 DownloadCreateInfo* info, const DownloadRequestHandle& request_handle) {
[email protected]c2e76012010-12-23 21:10:29330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
331
[email protected]ae77da82011-11-01 19:17:29332 DownloadItem* download = new DownloadItem(
333 this, *info, new DownloadRequestHandle(request_handle),
334 browser_context_->IsOffTheRecord());
[email protected]2909e342011-10-29 00:46:53335 int32 download_id = info->download_id.local();
[email protected]4cd82f72011-05-23 19:15:01336 DCHECK(!ContainsKey(in_progress_, download_id));
[email protected]d8472d92011-08-26 20:15:20337
338 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved.
[email protected]821960a2011-08-23 20:40:03339 CHECK(!ContainsKey(active_downloads_, download_id));
[email protected]c2e76012010-12-23 21:10:29340 downloads_.insert(download);
[email protected]4cd82f72011-05-23 19:15:01341 active_downloads_[download_id] = download;
[email protected]c2e76012010-12-23 21:10:29342}
343
[email protected]5656f8a2011-11-17 16:12:58344void DownloadManagerImpl::ContinueDownloadWithPath(
345 DownloadItem* download, const FilePath& chosen_file) {
[email protected]ca4b5fa32010-10-09 12:42:18346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]4cd82f72011-05-23 19:15:01347 DCHECK(download);
[email protected]aa033af2010-07-27 18:16:39348
[email protected]4cd82f72011-05-23 19:15:01349 int32 download_id = download->id();
initial.commit09911bf2008-07-26 23:55:29350
[email protected]70850c72011-01-11 17:31:27351 // NOTE(ahendrickson) Eventually |active_downloads_| will replace
352 // |in_progress_|, but we don't want to change the semantics yet.
[email protected]4cd82f72011-05-23 19:15:01353 DCHECK(!ContainsKey(in_progress_, download_id));
[email protected]70850c72011-01-11 17:31:27354 DCHECK(ContainsKey(downloads_, download));
[email protected]4cd82f72011-05-23 19:15:01355 DCHECK(ContainsKey(active_downloads_, download_id));
[email protected]70850c72011-01-11 17:31:27356
[email protected]4cd82f72011-05-23 19:15:01357 // Make sure the initial file name is set only once.
[email protected]4cd82f72011-05-23 19:15:01358 download->OnPathDetermined(chosen_file);
[email protected]4cd82f72011-05-23 19:15:01359
360 VLOG(20) << __FUNCTION__ << "()"
361 << " download = " << download->DebugString(true);
362
363 in_progress_[download_id] = download;
[email protected]5f8589fe2011-08-17 20:58:39364 UpdateDownloadProgress(); // Reflect entry into in_progress_.
initial.commit09911bf2008-07-26 23:55:29365
[email protected]adb2f3d12011-01-23 16:24:54366 // Rename to intermediate name.
[email protected]f5920322011-03-24 20:34:16367 FilePath download_path;
[email protected]ec865262011-08-23 20:01:48368 if (!delegate_->OverrideIntermediatePath(download, &download_path))
[email protected]4cd82f72011-05-23 19:15:01369 download_path = download->full_path();
[email protected]594cd7d2010-07-21 03:23:56370
[email protected]f5920322011-03-24 20:34:16371 BrowserThread::PostTask(
372 BrowserThread::FILE, FROM_HERE,
[email protected]fabf36d22011-10-28 21:50:17373 base::Bind(&DownloadFileManager::RenameInProgressDownloadFile,
374 file_manager_, download->global_id(), download_path));
[email protected]f5920322011-03-24 20:34:16375
376 download->Rename(download_path);
377
[email protected]2588ea9d2011-08-22 20:59:53378 delegate_->AddItemToPersistentStore(download);
initial.commit09911bf2008-07-26 23:55:29379}
380
[email protected]5656f8a2011-11-17 16:12:58381void DownloadManagerImpl::UpdateDownload(int32 download_id, int64 size) {
[email protected]70850c72011-01-11 17:31:27382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
383 DownloadMap::iterator it = active_downloads_.find(download_id);
384 if (it != active_downloads_.end()) {
initial.commit09911bf2008-07-26 23:55:29385 DownloadItem* download = it->second;
[email protected]bf68a00b2011-04-07 17:28:26386 if (download->IsInProgress()) {
[email protected]70850c72011-01-11 17:31:27387 download->Update(size);
[email protected]5f8589fe2011-08-17 20:58:39388 UpdateDownloadProgress(); // Reflect size updates.
[email protected]2588ea9d2011-08-22 20:59:53389 delegate_->UpdateItemInPersistentStore(download);
[email protected]70850c72011-01-11 17:31:27390 }
initial.commit09911bf2008-07-26 23:55:29391 }
392}
393
[email protected]5656f8a2011-11-17 16:12:58394void DownloadManagerImpl::OnResponseCompleted(int32 download_id,
395 int64 size,
396 const std::string& hash) {
[email protected]33c6d3f12011-09-04 00:00:54397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]da6e3922010-11-24 21:45:50398 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
399 << " size = " << size;
[email protected]9d7ef802011-02-25 19:03:35400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]9ccbb372008-10-10 18:50:32401
[email protected]c4f02c42011-01-24 21:55:06402 // If it's not in active_downloads_, that means it was cancelled; just
403 // ignore the notification.
404 if (active_downloads_.count(download_id) == 0)
405 return;
406
[email protected]adb2f3d12011-01-23 16:24:54407 DownloadItem* download = active_downloads_[download_id];
[email protected]ac4af82f2011-11-10 19:09:37408 download->OnAllDataSaved(size, hash);
409 delegate_->OnResponseCompleted(download);
[email protected]b09f1282011-09-14 00:37:45410
[email protected]adb2f3d12011-01-23 16:24:54411 MaybeCompleteDownload(download);
412}
[email protected]9ccbb372008-10-10 18:50:32413
[email protected]5656f8a2011-11-17 16:12:58414void DownloadManagerImpl::AssertQueueStateConsistent(DownloadItem* download) {
[email protected]5cd11b6e2011-06-10 20:30:59415 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
[email protected]7d413112011-06-16 18:50:17416 if (download->state() == DownloadItem::REMOVING) {
417 CHECK(!ContainsKey(downloads_, download));
418 CHECK(!ContainsKey(active_downloads_, download->id()));
419 CHECK(!ContainsKey(in_progress_, download->id()));
420 CHECK(!ContainsKey(history_downloads_, download->db_handle()));
421 return;
422 }
423
424 // Should be in downloads_ if we're not REMOVING.
425 CHECK(ContainsKey(downloads_, download));
426
427 // Check history_downloads_ consistency.
[email protected]2588ea9d2011-08-22 20:59:53428 if (download->db_handle() != DownloadItem::kUninitializedHandle) {
[email protected]7d413112011-06-16 18:50:17429 CHECK(ContainsKey(history_downloads_, download->db_handle()));
430 } else {
431 // TODO(rdsmith): Somewhat painful; make sure to disable in
432 // release builds after resolution of http://crbug.com/85408.
433 for (DownloadMap::iterator it = history_downloads_.begin();
434 it != history_downloads_.end(); ++it) {
435 CHECK(it->second != download);
436 }
437 }
438
[email protected]61b75a52011-08-29 16:34:34439 int64 state = download->state();
440 base::debug::Alias(&state);
[email protected]f9a2997f2011-09-23 16:54:07441 if (ContainsKey(active_downloads_, download->id())) {
442 if (download->db_handle() != DownloadItem::kUninitializedHandle)
443 CHECK_EQ(DownloadItem::IN_PROGRESS, download->state());
444 if (DownloadItem::IN_PROGRESS != download->state())
445 CHECK_EQ(DownloadItem::kUninitializedHandle, download->db_handle());
446 }
[email protected]821960a2011-08-23 20:40:03447 if (DownloadItem::IN_PROGRESS == download->state())
448 CHECK(ContainsKey(active_downloads_, download->id()));
[email protected]5cd11b6e2011-06-10 20:30:59449}
450
[email protected]5656f8a2011-11-17 16:12:58451bool DownloadManagerImpl::IsDownloadReadyForCompletion(DownloadItem* download) {
[email protected]adb2f3d12011-01-23 16:24:54452 // If we don't have all the data, the download is not ready for
453 // completion.
454 if (!download->all_data_saved())
455 return false;
[email protected]6a7fb042010-02-01 16:30:47456
[email protected]9d7ef802011-02-25 19:03:35457 // If the download is dangerous, but not yet validated, it's not ready for
458 // completion.
459 if (download->safety_state() == DownloadItem::DANGEROUS)
460 return false;
461
[email protected]adb2f3d12011-01-23 16:24:54462 // If the download isn't active (e.g. has been cancelled) it's not
463 // ready for completion.
464 if (active_downloads_.count(download->id()) == 0)
465 return false;
466
467 // If the download hasn't been inserted into the history system
468 // (which occurs strictly after file name determination, intermediate
469 // file rename, and UI display) then it's not ready for completion.
[email protected]2588ea9d2011-08-22 20:59:53470 if (download->db_handle() == DownloadItem::kUninitializedHandle)
[email protected]7054b592011-06-22 14:46:42471 return false;
472
473 return true;
[email protected]adb2f3d12011-01-23 16:24:54474}
475
[email protected]5656f8a2011-11-17 16:12:58476void DownloadManagerImpl::MaybeCompleteDownload(DownloadItem* download) {
[email protected]adb2f3d12011-01-23 16:24:54477 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
478 VLOG(20) << __FUNCTION__ << "()" << " download = "
479 << download->DebugString(false);
480
481 if (!IsDownloadReadyForCompletion(download))
[email protected]9ccbb372008-10-10 18:50:32482 return;
[email protected]9ccbb372008-10-10 18:50:32483
[email protected]adb2f3d12011-01-23 16:24:54484 // TODO(rdsmith): DCHECK that we only pass through this point
485 // once per download. The natural way to do this is by a state
486 // transition on the DownloadItem.
[email protected]594cd7d2010-07-21 03:23:56487
[email protected]adb2f3d12011-01-23 16:24:54488 // Confirm we're in the proper set of states to be here;
[email protected]9d7ef802011-02-25 19:03:35489 // in in_progress_, have all data, have a history handle, (validated or safe).
490 DCHECK_NE(DownloadItem::DANGEROUS, download->safety_state());
[email protected]adb2f3d12011-01-23 16:24:54491 DCHECK_EQ(1u, in_progress_.count(download->id()));
492 DCHECK(download->all_data_saved());
[email protected]2588ea9d2011-08-22 20:59:53493 DCHECK(download->db_handle() != DownloadItem::kUninitializedHandle);
[email protected]adb2f3d12011-01-23 16:24:54494 DCHECK_EQ(1u, history_downloads_.count(download->db_handle()));
495
[email protected]c2918652011-11-01 18:50:23496 // Give the delegate a chance to override.
497 if (!delegate_->ShouldCompleteDownload(download))
498 return;
499
[email protected]adb2f3d12011-01-23 16:24:54500 VLOG(20) << __FUNCTION__ << "()" << " executing: download = "
501 << download->DebugString(false);
502
503 // Remove the id from in_progress
504 in_progress_.erase(download->id());
[email protected]5f8589fe2011-08-17 20:58:39505 UpdateDownloadProgress(); // Reflect removal from in_progress_.
[email protected]adb2f3d12011-01-23 16:24:54506
[email protected]2588ea9d2011-08-22 20:59:53507 delegate_->UpdateItemInPersistentStore(download);
[email protected]adb2f3d12011-01-23 16:24:54508
[email protected]f5920322011-03-24 20:34:16509 // Finish the download.
[email protected]48837962011-04-19 17:03:29510 download->OnDownloadCompleting(file_manager_);
[email protected]9ccbb372008-10-10 18:50:32511}
512
[email protected]5656f8a2011-11-17 16:12:58513void DownloadManagerImpl::DownloadCompleted(int32 download_id) {
[email protected]70850c72011-01-11 17:31:27514 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cc3c7c092011-05-09 18:40:21515 DownloadItem* download = GetDownloadItem(download_id);
516 DCHECK(download);
[email protected]2588ea9d2011-08-22 20:59:53517 delegate_->UpdateItemInPersistentStore(download);
[email protected]70850c72011-01-11 17:31:27518 active_downloads_.erase(download_id);
[email protected]821960a2011-08-23 20:40:03519 AssertQueueStateConsistent(download);
[email protected]70850c72011-01-11 17:31:27520}
521
[email protected]5656f8a2011-11-17 16:12:58522void DownloadManagerImpl::OnDownloadRenamedToFinalName(
523 int download_id,
524 const FilePath& full_path,
525 int uniquifier) {
[email protected]da6e3922010-11-24 21:45:50526 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
[email protected]f5920322011-03-24 20:34:16527 << " full_path = \"" << full_path.value() << "\""
528 << " uniquifier = " << uniquifier;
[email protected]ca4b5fa32010-10-09 12:42:18529 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]f5920322011-03-24 20:34:16530
[email protected]2e030682010-07-23 19:45:36531 DownloadItem* item = GetDownloadItem(download_id);
532 if (!item)
533 return;
[email protected]6cade212008-12-03 00:32:22534
[email protected]8fa1eeb52011-04-13 14:18:02535 if (item->safety_state() == DownloadItem::SAFE) {
536 DCHECK_EQ(0, uniquifier) << "We should not uniquify SAFE downloads twice";
537 }
538
[email protected]fabf36d22011-10-28 21:50:17539 BrowserThread::PostTask(
540 BrowserThread::FILE, FROM_HERE,
541 base::Bind(&DownloadFileManager::CompleteDownload,
542 file_manager_, item->global_id()));
[email protected]9ccbb372008-10-10 18:50:32543
[email protected]f5920322011-03-24 20:34:16544 if (uniquifier)
545 item->set_path_uniquifier(uniquifier);
[email protected]9ccbb372008-10-10 18:50:32546
[email protected]f5920322011-03-24 20:34:16547 item->OnDownloadRenamedToFinalName(full_path);
[email protected]2588ea9d2011-08-22 20:59:53548 delegate_->UpdatePathForItemInPersistentStore(item, full_path);
initial.commit09911bf2008-07-26 23:55:29549}
550
[email protected]5656f8a2011-11-17 16:12:58551void DownloadManagerImpl::CancelDownload(int32 download_id) {
[email protected]93af2272011-09-21 18:29:17552 DownloadItem* download = GetActiveDownload(download_id);
553 // A cancel at the right time could remove the download from the
554 // |active_downloads_| map before we get here.
555 if (!download)
556 return;
557
558 download->Cancel(true);
559}
560
[email protected]5656f8a2011-11-17 16:12:58561void DownloadManagerImpl::DownloadCancelledInternal(DownloadItem* download) {
[email protected]d8472d92011-08-26 20:15:20562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]d8472d92011-08-26 20:15:20563
564 VLOG(20) << __FUNCTION__ << "()"
[email protected]da6e3922010-11-24 21:45:50565 << " download = " << download->DebugString(true);
566
[email protected]93af2272011-09-21 18:29:17567 RemoveFromActiveList(download);
[email protected]47a881b2011-08-29 22:59:21568 // This function is called from the DownloadItem, so DI state
569 // should already have been updated.
570 AssertQueueStateConsistent(download);
initial.commit09911bf2008-07-26 23:55:29571
[email protected]15d90ba2011-11-03 03:41:55572 if (file_manager_)
573 download->OffThreadCancel(file_manager_);
initial.commit09911bf2008-07-26 23:55:29574}
575
[email protected]5656f8a2011-11-17 16:12:58576void DownloadManagerImpl::OnDownloadInterrupted(int32 download_id,
577 int64 size,
578 InterruptReason reason) {
[email protected]47a881b2011-08-29 22:59:21579 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
580
581 DownloadItem* download = GetActiveDownload(download_id);
582 if (!download)
583 return;
584
[email protected]be76b7e2011-10-13 12:57:57585 VLOG(20) << __FUNCTION__ << "()"
586 << " reason " << InterruptReasonDebugString(reason)
[email protected]47a881b2011-08-29 22:59:21587 << " at offset " << download->received_bytes()
588 << " size = " << size
589 << " download = " << download->DebugString(true);
590
[email protected]93af2272011-09-21 18:29:17591 RemoveFromActiveList(download);
[email protected]be76b7e2011-10-13 12:57:57592 download->Interrupted(size, reason);
[email protected]93af2272011-09-21 18:29:17593 download->OffThreadCancel(file_manager_);
[email protected]47a881b2011-08-29 22:59:21594}
595
[email protected]5656f8a2011-11-17 16:12:58596DownloadItem* DownloadManagerImpl::GetActiveDownload(int32 download_id) {
[email protected]bf68a00b2011-04-07 17:28:26597 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
598 DownloadMap::iterator it = active_downloads_.find(download_id);
[email protected]bf68a00b2011-04-07 17:28:26599 if (it == active_downloads_.end())
[email protected]47a881b2011-08-29 22:59:21600 return NULL;
[email protected]bf68a00b2011-04-07 17:28:26601
602 DownloadItem* download = it->second;
603
[email protected]47a881b2011-08-29 22:59:21604 DCHECK(download);
605 DCHECK_EQ(download_id, download->id());
[email protected]4cd82f72011-05-23 19:15:01606
[email protected]47a881b2011-08-29 22:59:21607 return download;
608}
[email protected]54610672011-07-18 18:24:43609
[email protected]5656f8a2011-11-17 16:12:58610void DownloadManagerImpl::RemoveFromActiveList(DownloadItem* download) {
[email protected]93af2272011-09-21 18:29:17611 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
612 DCHECK(download);
613
614 // Clean up will happen when the history system create callback runs if we
615 // don't have a valid db_handle yet.
616 if (download->db_handle() != DownloadItem::kUninitializedHandle) {
617 in_progress_.erase(download->id());
618 active_downloads_.erase(download->id());
619 UpdateDownloadProgress(); // Reflect removal from in_progress_.
620 delegate_->UpdateItemInPersistentStore(download);
621 }
622}
623
[email protected]5656f8a2011-11-17 16:12:58624content::DownloadManagerDelegate* DownloadManagerImpl::delegate() const {
625 return delegate_;
626}
627
628void DownloadManagerImpl::SetDownloadManagerDelegate(
[email protected]1bd0ef12011-10-20 05:24:17629 content::DownloadManagerDelegate* delegate) {
[email protected]9bb54ee2011-10-12 17:43:35630 delegate_ = delegate;
631}
632
[email protected]5656f8a2011-11-17 16:12:58633void DownloadManagerImpl::UpdateDownloadProgress() {
[email protected]5f8589fe2011-08-17 20:58:39634 delegate_->DownloadProgressUpdated();
[email protected]6a7fb042010-02-01 16:30:47635}
636
[email protected]5656f8a2011-11-17 16:12:58637int DownloadManagerImpl::RemoveDownloadItems(
[email protected]6d0146c2011-08-04 19:13:04638 const DownloadVector& pending_deletes) {
639 if (pending_deletes.empty())
640 return 0;
641
642 // Delete from internal maps.
643 for (DownloadVector::const_iterator it = pending_deletes.begin();
644 it != pending_deletes.end();
645 ++it) {
646 DownloadItem* download = *it;
647 DCHECK(download);
648 history_downloads_.erase(download->db_handle());
649 save_page_downloads_.erase(download->id());
650 downloads_.erase(download);
651 }
652
653 // Tell observers to refresh their views.
654 NotifyModelChanged();
655
656 // Delete the download items themselves.
657 const int num_deleted = static_cast<int>(pending_deletes.size());
658 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
659 return num_deleted;
660}
661
[email protected]5656f8a2011-11-17 16:12:58662void DownloadManagerImpl::RemoveDownload(int64 download_handle) {
[email protected]93af2272011-09-21 18:29:17663 DownloadMap::iterator it = history_downloads_.find(download_handle);
664 if (it == history_downloads_.end())
665 return;
666
667 // Make history update.
668 DownloadItem* download = it->second;
669 delegate_->RemoveItemFromPersistentStore(download);
initial.commit09911bf2008-07-26 23:55:29670
671 // Remove from our tables and delete.
[email protected]6d0146c2011-08-04 19:13:04672 int downloads_count = RemoveDownloadItems(DownloadVector(1, download));
[email protected]f04182f32010-12-10 19:12:07673 DCHECK_EQ(1, downloads_count);
initial.commit09911bf2008-07-26 23:55:29674}
675
[email protected]5656f8a2011-11-17 16:12:58676int DownloadManagerImpl::RemoveDownloadsBetween(const base::Time remove_begin,
677 const base::Time remove_end) {
[email protected]2588ea9d2011-08-22 20:59:53678 delegate_->RemoveItemsFromPersistentStoreBetween(remove_begin, remove_end);
initial.commit09911bf2008-07-26 23:55:29679
[email protected]a312a442010-12-15 23:40:33680 // All downloads visible to the user will be in the history,
681 // so scan that map.
[email protected]6d0146c2011-08-04 19:13:04682 DownloadVector pending_deletes;
683 for (DownloadMap::const_iterator it = history_downloads_.begin();
684 it != history_downloads_.end();
685 ++it) {
initial.commit09911bf2008-07-26 23:55:29686 DownloadItem* download = it->second;
initial.commit09911bf2008-07-26 23:55:29687 if (download->start_time() >= remove_begin &&
688 (remove_end.is_null() || download->start_time() < remove_end) &&
[email protected]6d0146c2011-08-04 19:13:04689 (download->IsComplete() || download->IsCancelled())) {
[email protected]7d413112011-06-16 18:50:17690 AssertQueueStateConsistent(download);
[email protected]78b8fcc92009-03-31 17:36:28691 pending_deletes.push_back(download);
initial.commit09911bf2008-07-26 23:55:29692 }
initial.commit09911bf2008-07-26 23:55:29693 }
[email protected]6d0146c2011-08-04 19:13:04694 return RemoveDownloadItems(pending_deletes);
initial.commit09911bf2008-07-26 23:55:29695}
696
[email protected]5656f8a2011-11-17 16:12:58697int DownloadManagerImpl::RemoveDownloads(const base::Time remove_begin) {
[email protected]e93d2822009-01-30 05:59:59698 return RemoveDownloadsBetween(remove_begin, base::Time());
initial.commit09911bf2008-07-26 23:55:29699}
700
[email protected]5656f8a2011-11-17 16:12:58701int DownloadManagerImpl::RemoveAllDownloads() {
[email protected]da4a5582011-10-17 19:08:06702 download_stats::RecordClearAllSize(history_downloads_.size());
[email protected]d41355e6f2009-04-07 21:21:12703 // The null times make the date range unbounded.
704 return RemoveDownloadsBetween(base::Time(), base::Time());
705}
706
initial.commit09911bf2008-07-26 23:55:29707// Initiate a download of a specific URL. We send the request to the
708// ResourceDispatcherHost, and let it send us responses like a regular
709// download.
[email protected]5656f8a2011-11-17 16:12:58710void DownloadManagerImpl::DownloadUrl(const GURL& url,
711 const GURL& referrer,
712 const std::string& referrer_charset,
713 TabContents* tab_contents) {
[email protected]ae8945192010-07-20 16:56:26714 DownloadUrlToFile(url, referrer, referrer_charset, DownloadSaveInfo(),
715 tab_contents);
[email protected]6aa4a1c02010-01-15 18:49:58716}
717
[email protected]5656f8a2011-11-17 16:12:58718void DownloadManagerImpl::DownloadUrlToFile(const GURL& url,
719 const GURL& referrer,
720 const std::string& referrer_charset,
721 const DownloadSaveInfo& save_info,
722 TabContents* tab_contents) {
[email protected]57c6a652009-05-04 07:58:34723 DCHECK(tab_contents);
[email protected]c79a0c02011-08-22 22:37:37724 ResourceDispatcherHost* resource_dispatcher_host =
725 content::GetContentClient()->browser()->GetResourceDispatcherHost();
[email protected]ed24fad2011-05-10 22:44:01726 // We send a pointer to content::ResourceContext, instead of the usual
727 // reference, so that a copy of the object isn't made.
[email protected]fabf36d22011-10-28 21:50:17728 // base::Bind can't handle 7 args, so we use URLParams and RenderParams.
729 BrowserThread::PostTask(
730 BrowserThread::IO, FROM_HERE,
731 base::Bind(&BeginDownload,
732 URLParams(url, referrer), save_info, resource_dispatcher_host,
[email protected]f3b1a082011-11-18 00:34:30733 RenderParams(tab_contents->GetRenderProcessHost()->GetID(),
[email protected]fabf36d22011-10-28 21:50:17734 tab_contents->render_view_host()->routing_id()),
735 &tab_contents->browser_context()->GetResourceContext()));
initial.commit09911bf2008-07-26 23:55:29736}
737
[email protected]5656f8a2011-11-17 16:12:58738void DownloadManagerImpl::AddObserver(Observer* observer) {
initial.commit09911bf2008-07-26 23:55:29739 observers_.AddObserver(observer);
740 observer->ModelChanged();
741}
742
[email protected]5656f8a2011-11-17 16:12:58743void DownloadManagerImpl::RemoveObserver(Observer* observer) {
initial.commit09911bf2008-07-26 23:55:29744 observers_.RemoveObserver(observer);
745}
746
[email protected]5656f8a2011-11-17 16:12:58747bool DownloadManagerImpl::IsDownloadProgressKnown() const {
[email protected]45f432e942011-10-25 18:17:22748 for (DownloadMap::const_iterator i = in_progress_.begin();
[email protected]073ed7b2010-09-27 09:20:02749 i != in_progress_.end(); ++i) {
750 if (i->second->total_bytes() <= 0)
751 return false;
752 }
753
754 return true;
755}
756
[email protected]5656f8a2011-11-17 16:12:58757int64 DownloadManagerImpl::GetInProgressDownloadCount() const {
[email protected]073ed7b2010-09-27 09:20:02758 return in_progress_.size();
759}
760
[email protected]5656f8a2011-11-17 16:12:58761int64 DownloadManagerImpl::GetReceivedDownloadBytes() const {
[email protected]073ed7b2010-09-27 09:20:02762 DCHECK(IsDownloadProgressKnown());
763 int64 received_bytes = 0;
[email protected]45f432e942011-10-25 18:17:22764 for (DownloadMap::const_iterator i = in_progress_.begin();
[email protected]073ed7b2010-09-27 09:20:02765 i != in_progress_.end(); ++i) {
766 received_bytes += i->second->received_bytes();
767 }
768 return received_bytes;
769}
770
[email protected]5656f8a2011-11-17 16:12:58771int64 DownloadManagerImpl::GetTotalDownloadBytes() const {
[email protected]073ed7b2010-09-27 09:20:02772 DCHECK(IsDownloadProgressKnown());
773 int64 total_bytes = 0;
[email protected]45f432e942011-10-25 18:17:22774 for (DownloadMap::const_iterator i = in_progress_.begin();
[email protected]073ed7b2010-09-27 09:20:02775 i != in_progress_.end(); ++i) {
776 total_bytes += i->second->total_bytes();
777 }
778 return total_bytes;
779}
780
[email protected]5656f8a2011-11-17 16:12:58781void DownloadManagerImpl::FileSelected(const FilePath& path, void* params) {
[email protected]4cd82f72011-05-23 19:15:01782 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
783
784 int32* id_ptr = reinterpret_cast<int32*>(params);
785 DCHECK(id_ptr != NULL);
786 int32 download_id = *id_ptr;
787 delete id_ptr;
788
789 DownloadItem* download = GetActiveDownloadItem(download_id);
790 if (!download)
791 return;
792 VLOG(20) << __FUNCTION__ << "()" << " path = \"" << path.value() << "\""
793 << " download = " << download->DebugString(true);
794
[email protected]da1a27b2011-07-29 23:16:33795 if (download->prompt_user_for_save_location())
[email protected]7ae7c2cb2009-01-06 23:31:41796 last_download_path_ = path.DirName();
[email protected]287b86b2011-02-26 00:11:35797
[email protected]4cd82f72011-05-23 19:15:01798 // Make sure the initial file name is set only once.
799 ContinueDownloadWithPath(download, path);
initial.commit09911bf2008-07-26 23:55:29800}
801
[email protected]5656f8a2011-11-17 16:12:58802void DownloadManagerImpl::FileSelectionCanceled(void* params) {
initial.commit09911bf2008-07-26 23:55:29803 // The user didn't pick a place to save the file, so need to cancel the
804 // download that's already in progress to the temporary location.
[email protected]4cd82f72011-05-23 19:15:01805 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
806 int32* id_ptr = reinterpret_cast<int32*>(params);
807 DCHECK(id_ptr != NULL);
808 int32 download_id = *id_ptr;
809 delete id_ptr;
810
811 DownloadItem* download = GetActiveDownloadItem(download_id);
812 if (!download)
813 return;
814
815 VLOG(20) << __FUNCTION__ << "()"
816 << " download = " << download->DebugString(true);
817
[email protected]93af2272011-09-21 18:29:17818 // TODO(ahendrickson) -- This currently has no effect, as the download is
819 // not put on the active list until the file selection is complete. Need
820 // to put it on the active list earlier in the process.
821 RemoveFromActiveList(download);
822
823 download->OffThreadCancel(file_manager_);
[email protected]4cd82f72011-05-23 19:15:01824}
825
initial.commit09911bf2008-07-26 23:55:29826// Operations posted to us from the history service ----------------------------
827
828// The history service has retrieved all download entries. 'entries' contains
[email protected]bb1a4212011-08-22 22:38:25829// 'DownloadPersistentStoreInfo's in sorted order (by ascending start_time).
[email protected]5656f8a2011-11-17 16:12:58830void DownloadManagerImpl::OnPersistentStoreQueryComplete(
[email protected]bb1a4212011-08-22 22:38:25831 std::vector<DownloadPersistentStoreInfo>* entries) {
[email protected]d8472d92011-08-26 20:15:20832 // TODO(rdsmith): Remove this and related logic when
[email protected]639ddad2011-10-05 02:53:47833 // http://crbug.com/85408 is fixed.
[email protected]d8472d92011-08-26 20:15:20834 largest_db_handle_in_history_ = 0;
835
initial.commit09911bf2008-07-26 23:55:29836 for (size_t i = 0; i < entries->size(); ++i) {
[email protected]aa033af2010-07-27 18:16:39837 DownloadItem* download = new DownloadItem(this, entries->at(i));
[email protected]d8472d92011-08-26 20:15:20838 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved.
[email protected]821960a2011-08-23 20:40:03839 CHECK(!ContainsKey(history_downloads_, download->db_handle()));
[email protected]f04182f32010-12-10 19:12:07840 downloads_.insert(download);
841 history_downloads_[download->db_handle()] = download;
[email protected]da6e3922010-11-24 21:45:50842 VLOG(20) << __FUNCTION__ << "()" << i << ">"
843 << " download = " << download->DebugString(true);
[email protected]d8472d92011-08-26 20:15:20844
845 if (download->db_handle() > largest_db_handle_in_history_)
846 largest_db_handle_in_history_ = download->db_handle();
initial.commit09911bf2008-07-26 23:55:29847 }
[email protected]b0ab1d42010-02-24 19:29:28848 NotifyModelChanged();
[email protected]9fc114672011-06-15 08:17:48849 CheckForHistoryFilesRemoval();
initial.commit09911bf2008-07-26 23:55:29850}
851
[email protected]5656f8a2011-11-17 16:12:58852void DownloadManagerImpl::AddDownloadItemToHistory(DownloadItem* download,
853 int64 db_handle) {
[email protected]70850c72011-01-11 17:31:27854 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]d2a8fb72010-01-21 05:31:42855
[email protected]639ddad2011-10-05 02:53:47856 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/85408
[email protected]1e9fe7ff2011-06-24 17:37:33857 // is fixed.
[email protected]2588ea9d2011-08-22 20:59:53858 CHECK_NE(DownloadItem::kUninitializedHandle, db_handle);
[email protected]1e9fe7ff2011-06-24 17:37:33859
[email protected]da4a5582011-10-17 19:08:06860 download_stats::RecordHistorySize(history_downloads_.size());
861
[email protected]2588ea9d2011-08-22 20:59:53862 DCHECK(download->db_handle() == DownloadItem::kUninitializedHandle);
[email protected]5bcd73eb2011-03-23 21:14:02863 download->set_db_handle(db_handle);
864
[email protected]639ddad2011-10-05 02:53:47865 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/85408
[email protected]d8472d92011-08-26 20:15:20866 // is fixed.
[email protected]821960a2011-08-23 20:40:03867 CHECK(!ContainsKey(history_downloads_, download->db_handle()));
[email protected]5bcd73eb2011-03-23 21:14:02868 history_downloads_[download->db_handle()] = download;
[email protected]6d0146c2011-08-04 19:13:04869
870 // Show in the appropriate browser UI.
871 // This includes buttons to save or cancel, for a dangerous download.
872 ShowDownloadInBrowser(download);
873
874 // Inform interested objects about the new download.
875 NotifyModelChanged();
[email protected]f9a45b02011-06-30 23:49:19876}
877
[email protected]2588ea9d2011-08-22 20:59:53878
[email protected]5656f8a2011-11-17 16:12:58879void DownloadManagerImpl::OnItemAddedToPersistentStore(int32 download_id,
880 int64 db_handle) {
[email protected]2588ea9d2011-08-22 20:59:53881 if (save_page_downloads_.count(download_id)) {
882 OnSavePageItemAddedToPersistentStore(download_id, db_handle);
883 } else if (active_downloads_.count(download_id)) {
884 OnDownloadItemAddedToPersistentStore(download_id, db_handle);
885 }
886 // It's valid that we don't find a matching item, i.e. on shutdown.
887}
888
[email protected]f9a45b02011-06-30 23:49:19889// Once the new DownloadItem's creation info has been committed to the history
890// service, we associate the DownloadItem with the db handle, update our
891// 'history_downloads_' map and inform observers.
[email protected]5656f8a2011-11-17 16:12:58892void DownloadManagerImpl::OnDownloadItemAddedToPersistentStore(
893 int32 download_id, int64 db_handle) {
[email protected]f9a45b02011-06-30 23:49:19894 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]19420cc2011-07-18 17:43:45895 DownloadItem* download = GetActiveDownloadItem(download_id);
[email protected]93af2272011-09-21 18:29:17896 if (!download)
[email protected]19420cc2011-07-18 17:43:45897 return;
[email protected]54610672011-07-18 18:24:43898
899 VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle
900 << " download_id = " << download_id
901 << " download = " << download->DebugString(true);
[email protected]f9a45b02011-06-30 23:49:19902
[email protected]d8472d92011-08-26 20:15:20903 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved.
[email protected]d8472d92011-08-26 20:15:20904 int64 largest_handle = largest_db_handle_in_history_;
905 base::debug::Alias(&largest_handle);
[email protected]e5107ce2011-09-19 20:36:13906 int32 matching_item_download_id
907 = (ContainsKey(history_downloads_, db_handle) ?
908 history_downloads_[db_handle]->id() : 0);
909 base::debug::Alias(&matching_item_download_id);
910
[email protected]61b75a52011-08-29 16:34:34911 CHECK(!ContainsKey(history_downloads_, db_handle));
[email protected]d8472d92011-08-26 20:15:20912
[email protected]f9a45b02011-06-30 23:49:19913 AddDownloadItemToHistory(download, db_handle);
initial.commit09911bf2008-07-26 23:55:29914
[email protected]93af2272011-09-21 18:29:17915 // If the download is still in progress, try to complete it.
916 //
917 // Otherwise, download has been cancelled or interrupted before we've
918 // received the DB handle. We post one final message to the history
919 // service so that it can be properly in sync with the DownloadItem's
920 // completion status, and also inform any observers so that they get
921 // more than just the start notification.
922 if (download->IsInProgress()) {
923 MaybeCompleteDownload(download);
924 } else {
[email protected]639ddad2011-10-05 02:53:47925 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/85408
[email protected]93af2272011-09-21 18:29:17926 // is fixed.
927 CHECK(download->IsCancelled())
928 << " download = " << download->DebugString(true);
929 in_progress_.erase(download_id);
930 active_downloads_.erase(download_id);
931 delegate_->UpdateItemInPersistentStore(download);
932 download->UpdateObservers();
933 }
initial.commit09911bf2008-07-26 23:55:29934}
935
[email protected]5656f8a2011-11-17 16:12:58936void DownloadManagerImpl::ShowDownloadInBrowser(DownloadItem* download) {
[email protected]8ddbd66a2010-05-21 16:38:34937 // The 'contents' may no longer exist if the user closed the tab before we
[email protected]99cb7f82011-07-28 17:27:26938 // get this start completion event.
[email protected]ae77da82011-11-01 19:17:29939 TabContents* content = download->GetTabContents();
[email protected]99cb7f82011-07-28 17:27:26940
941 // If the contents no longer exists, we ask the embedder to suggest another
942 // tab.
[email protected]da1a27b2011-07-29 23:16:33943 if (!content)
[email protected]aa9881c2011-08-15 18:01:12944 content = delegate_->GetAlternativeTabContentsToNotifyForDownload();
[email protected]5e595482009-05-06 20:16:53945
[email protected]99cb7f82011-07-28 17:27:26946 if (content)
947 content->OnStartDownload(download);
[email protected]5e595482009-05-06 20:16:53948}
949
[email protected]5656f8a2011-11-17 16:12:58950int DownloadManagerImpl::InProgressCount() const {
951 return static_cast<int>(in_progress_.size());
952}
953
[email protected]6cade212008-12-03 00:32:22954// Clears the last download path, used to initialize "save as" dialogs.
[email protected]5656f8a2011-11-17 16:12:58955void DownloadManagerImpl::ClearLastDownloadPath() {
[email protected]7ae7c2cb2009-01-06 23:31:41956 last_download_path_ = FilePath();
[email protected]eea46622009-07-15 20:49:38957}
[email protected]b0ab1d42010-02-24 19:29:28958
[email protected]5656f8a2011-11-17 16:12:58959void DownloadManagerImpl::NotifyModelChanged() {
[email protected]b0ab1d42010-02-24 19:29:28960 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
961}
962
[email protected]5656f8a2011-11-17 16:12:58963DownloadItem* DownloadManagerImpl::GetDownloadItem(int download_id) {
[email protected]4cd82f72011-05-23 19:15:01964 // The |history_downloads_| map is indexed by the download's db_handle,
965 // not its id, so we have to iterate.
[email protected]f04182f32010-12-10 19:12:07966 for (DownloadMap::iterator it = history_downloads_.begin();
967 it != history_downloads_.end(); ++it) {
[email protected]2e030682010-07-23 19:45:36968 DownloadItem* item = it->second;
[email protected]4cd82f72011-05-23 19:15:01969 if (item->id() == download_id)
[email protected]2e030682010-07-23 19:45:36970 return item;
971 }
972 return NULL;
973}
974
[email protected]5656f8a2011-11-17 16:12:58975DownloadItem* DownloadManagerImpl::GetActiveDownloadItem(int download_id) {
[email protected]93af2272011-09-21 18:29:17976 DCHECK(ContainsKey(active_downloads_, download_id));
[email protected]4cd82f72011-05-23 19:15:01977 DownloadItem* download = active_downloads_[download_id];
978 DCHECK(download != NULL);
979 return download;
980}
981
[email protected]57fd1252010-12-23 17:24:09982// Confirm that everything in all maps is also in |downloads_|, and that
983// everything in |downloads_| is also in some other map.
[email protected]5656f8a2011-11-17 16:12:58984void DownloadManagerImpl::AssertContainersConsistent() const {
[email protected]f04182f32010-12-10 19:12:07985#if !defined(NDEBUG)
[email protected]57fd1252010-12-23 17:24:09986 // Turn everything into sets.
[email protected]6d0146c2011-08-04 19:13:04987 const DownloadMap* input_maps[] = {&active_downloads_,
988 &history_downloads_,
989 &save_page_downloads_};
990 DownloadSet active_set, history_set, save_page_set;
991 DownloadSet* all_sets[] = {&active_set, &history_set, &save_page_set};
992 DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(all_sets));
[email protected]57fd1252010-12-23 17:24:09993 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input_maps); i++) {
994 for (DownloadMap::const_iterator it = input_maps[i]->begin();
[email protected]6d0146c2011-08-04 19:13:04995 it != input_maps[i]->end(); ++it) {
996 all_sets[i]->insert(&*it->second);
[email protected]f04182f32010-12-10 19:12:07997 }
998 }
[email protected]57fd1252010-12-23 17:24:09999
1000 // Check if each set is fully present in downloads, and create a union.
[email protected]57fd1252010-12-23 17:24:091001 DownloadSet downloads_union;
1002 for (int i = 0; i < static_cast<int>(ARRAYSIZE_UNSAFE(all_sets)); i++) {
1003 DownloadSet remainder;
1004 std::insert_iterator<DownloadSet> insert_it(remainder, remainder.begin());
1005 std::set_difference(all_sets[i]->begin(), all_sets[i]->end(),
1006 downloads_.begin(), downloads_.end(),
1007 insert_it);
1008 DCHECK(remainder.empty());
1009 std::insert_iterator<DownloadSet>
1010 insert_union(downloads_union, downloads_union.end());
1011 std::set_union(downloads_union.begin(), downloads_union.end(),
1012 all_sets[i]->begin(), all_sets[i]->end(),
1013 insert_union);
1014 }
1015
1016 // Is everything in downloads_ present in one of the other sets?
1017 DownloadSet remainder;
1018 std::insert_iterator<DownloadSet>
1019 insert_remainder(remainder, remainder.begin());
1020 std::set_difference(downloads_.begin(), downloads_.end(),
1021 downloads_union.begin(), downloads_union.end(),
1022 insert_remainder);
1023 DCHECK(remainder.empty());
[email protected]f04182f32010-12-10 19:12:071024#endif
1025}
1026
[email protected]5656f8a2011-11-17 16:12:581027void DownloadManagerImpl::SavePageDownloadStarted(DownloadItem* download) {
[email protected]6d0146c2011-08-04 19:13:041028 DCHECK(!ContainsKey(save_page_downloads_, download->id()));
1029 downloads_.insert(download);
1030 save_page_downloads_[download->id()] = download;
1031
1032 // Add this entry to the history service.
1033 // Additionally, the UI is notified in the callback.
[email protected]2588ea9d2011-08-22 20:59:531034 delegate_->AddItemToPersistentStore(download);
[email protected]6d0146c2011-08-04 19:13:041035}
1036
1037// SavePackage will call SavePageDownloadFinished upon completion/cancellation.
[email protected]2588ea9d2011-08-22 20:59:531038// The history callback will call OnSavePageItemAddedToPersistentStore.
[email protected]6d0146c2011-08-04 19:13:041039// If the download finishes before the history callback,
[email protected]2588ea9d2011-08-22 20:59:531040// OnSavePageItemAddedToPersistentStore calls SavePageDownloadFinished, ensuring
1041// that the history event is update regardless of the order in which these two
1042// events complete.
[email protected]6d0146c2011-08-04 19:13:041043// If something removes the download item from the download manager (Remove,
1044// Shutdown) the result will be that the SavePage system will not be able to
1045// properly update the download item (which no longer exists) or the download
1046// history, but the action will complete properly anyway. This may lead to the
1047// history entry being wrong on a reload of chrome (specifically in the case of
1048// Initiation -> History Callback -> Removal -> Completion), but there's no way
1049// to solve that without canceling on Remove (which would then update the DB).
1050
[email protected]5656f8a2011-11-17 16:12:581051void DownloadManagerImpl::OnSavePageItemAddedToPersistentStore(
1052 int32 download_id, int64 db_handle) {
[email protected]6d0146c2011-08-04 19:13:041053 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1054
1055 DownloadMap::const_iterator it = save_page_downloads_.find(download_id);
1056 // This can happen if the download manager is shutting down and all maps
1057 // have been cleared.
1058 if (it == save_page_downloads_.end())
1059 return;
1060
1061 DownloadItem* download = it->second;
1062 if (!download) {
1063 NOTREACHED();
1064 return;
1065 }
1066
[email protected]d8472d92011-08-26 20:15:201067 // TODO(rdsmith): Remove after http://crbug.com/85408 resolved.
[email protected]d8472d92011-08-26 20:15:201068 int64 largest_handle = largest_db_handle_in_history_;
1069 base::debug::Alias(&largest_handle);
[email protected]61b75a52011-08-29 16:34:341070 CHECK(!ContainsKey(history_downloads_, db_handle));
[email protected]d8472d92011-08-26 20:15:201071
[email protected]6d0146c2011-08-04 19:13:041072 AddDownloadItemToHistory(download, db_handle);
1073
1074 // Finalize this download if it finished before the history callback.
1075 if (!download->IsInProgress())
1076 SavePageDownloadFinished(download);
1077}
1078
[email protected]5656f8a2011-11-17 16:12:581079void DownloadManagerImpl::SavePageDownloadFinished(DownloadItem* download) {
[email protected]2588ea9d2011-08-22 20:59:531080 if (download->db_handle() != DownloadItem::kUninitializedHandle) {
1081 delegate_->UpdateItemInPersistentStore(download);
[email protected]6d0146c2011-08-04 19:13:041082 DCHECK(ContainsKey(save_page_downloads_, download->id()));
1083 save_page_downloads_.erase(download->id());
1084
1085 if (download->IsComplete())
[email protected]ad50def52011-10-19 23:17:071086 content::NotificationService::current()->Notify(
[email protected]6d0146c2011-08-04 19:13:041087 content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
[email protected]6c2381d2011-10-19 02:52:531088 content::Source<DownloadManager>(this),
1089 content::Details<DownloadItem>(download));
[email protected]6d0146c2011-08-04 19:13:041090 }
1091}
[email protected]da4a5582011-10-17 19:08:061092
[email protected]5656f8a2011-11-17 16:12:581093void DownloadManagerImpl::MarkDownloadOpened(DownloadItem* download) {
[email protected]da4a5582011-10-17 19:08:061094 delegate_->UpdateItemInPersistentStore(download);
1095 int num_unopened = 0;
1096 for (DownloadMap::iterator it = history_downloads_.begin();
1097 it != history_downloads_.end(); ++it) {
1098 if (it->second->IsComplete() && !it->second->opened())
1099 ++num_unopened;
1100 }
1101 download_stats::RecordOpensOutstanding(num_unopened);
1102}
[email protected]5656f8a2011-11-17 16:12:581103
1104void DownloadManagerImpl::SetFileManager(DownloadFileManager* file_manager) {
1105 file_manager_ = file_manager;
1106}