blob: aee4d141de499357827169825a9c19a7c2ac42bc [file] [log] [blame]
[email protected]0afff032012-01-06 20:55:001// Copyright (c) 2012 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"
[email protected]c6944272012-01-06 22:12:2811#include "base/debug/alias.h"
initial.commit09911bf2008-07-26 23:55:2912#include "base/file_util.h"
[email protected]503d03872011-05-06 08:36:2613#include "base/i18n/case_conversion.h"
initial.commit09911bf2008-07-26 23:55:2914#include "base/logging.h"
[email protected]7286e3fc2011-07-19 22:13:2415#include "base/stl_util.h"
[email protected]eda58402011-09-21 19:32:0216#include "base/stringprintf.h"
17#include "base/synchronization/lock.h"
18#include "base/sys_string_conversions.h"
[email protected]d2a8fb72010-01-21 05:31:4219#include "build/build_config.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]c09a8fd2011-11-21 19:54:5022#include "content/browser/download/download_item_impl.h"
[email protected]bb1a4212011-08-22 22:38:2523#include "content/browser/download/download_persistent_store_info.h"
[email protected]da4a5582011-10-17 19:08:0624#include "content/browser/download/download_stats.h"
[email protected]be76b7e2011-10-13 12:57:5725#include "content/browser/download/interrupt_reasons.h"
[email protected]7324d1d2011-03-01 05:02:1626#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]ccb797302011-12-15 16:55:1129#include "content/public/browser/browser_context.h"
[email protected]c38831a12011-10-28 12:44:4930#include "content/public/browser/browser_thread.h"
[email protected]87f3c082011-10-19 18:07:4431#include "content/public/browser/content_browser_client.h"
[email protected]1bd0ef12011-10-20 05:24:1732#include "content/public/browser/download_manager_delegate.h"
[email protected]ad50def52011-10-19 23:17:0733#include "content/public/browser/notification_service.h"
[email protected]0d6e9bd2011-10-18 04:29:1634#include "content/public/browser/notification_types.h"
[email protected]f3b1a082011-11-18 00:34:3035#include "content/public/browser/render_process_host.h"
[email protected]0bfbf882011-12-22 18:19:2736#include "content/public/browser/web_contents_delegate.h"
initial.commit09911bf2008-07-26 23:55:2937
[email protected]a896ce32012-01-09 22:04:0738// TODO(benjhayden): Change this to DCHECK when we have more debugging
39// information from the next dev cycle, before the next stable/beta branch is
40// cut, in order to prevent unnecessary crashes on those channels. If we still
41// don't have root cause before the dev cycle after the next stable/beta
42// releases, uncomment it out to re-enable debugging checks. Whenever this macro
43// is toggled, the corresponding macro in download_database.cc should also
44// be toggled. When 96627 is fixed, this macro and all its usages can be
45// deleted or permanently changed to DCHECK as appropriate.
46#define CHECK_96627 CHECK
47
[email protected]631bb742011-11-02 11:29:3948using content::BrowserThread;
[email protected]98e814062012-01-27 00:35:4249using content::DownloadId;
[email protected]e582fdd2011-12-20 16:48:1750using content::DownloadItem;
[email protected]2a6bc3e2011-12-28 23:51:3351using content::WebContents;
[email protected]631bb742011-11-02 11:29:3952
[email protected]a0ce3282011-08-19 20:49:5253namespace {
54
[email protected]fabf36d22011-10-28 21:50:1755// Param structs exist because base::Bind can only handle 6 args.
56struct URLParams {
57 URLParams(const GURL& url, const GURL& referrer)
58 : url_(url), referrer_(referrer) {}
59 GURL url_;
60 GURL referrer_;
61};
62
63struct RenderParams {
64 RenderParams(int rpi, int rvi)
65 : render_process_id_(rpi), render_view_id_(rvi) {}
66 int render_process_id_;
67 int render_view_id_;
68};
69
70void BeginDownload(const URLParams& url_params,
[email protected]0d4e30c2012-01-28 00:47:5371 bool prefer_cache,
[email protected]fabf36d22011-10-28 21:50:1772 const DownloadSaveInfo& save_info,
73 ResourceDispatcherHost* resource_dispatcher_host,
74 const RenderParams& render_params,
75 const content::ResourceContext* context) {
[email protected]c1ba99842012-01-19 20:56:0576 scoped_ptr<net::URLRequest> request(
77 new net::URLRequest(url_params.url_, resource_dispatcher_host));
[email protected]fabf36d22011-10-28 21:50:1778 request->set_referrer(url_params.referrer_.spec());
[email protected]c79a0c02011-08-22 22:37:3779 resource_dispatcher_host->BeginDownload(
[email protected]0d4e30c2012-01-28 00:47:5380 request.Pass(), prefer_cache, save_info,
[email protected]8e3ae68c2011-09-16 22:15:4781 DownloadResourceHandler::OnStartedCallback(),
[email protected]fabf36d22011-10-28 21:50:1782 render_params.render_process_id_,
83 render_params.render_view_id_,
[email protected]c79a0c02011-08-22 22:37:3784 *context);
[email protected]a0ce3282011-08-19 20:49:5285}
86
[email protected]33d22102012-01-25 17:46:5387class MapValueIteratorAdapter {
88 public:
89 explicit MapValueIteratorAdapter(
90 base::hash_map<int64, DownloadItem*>::const_iterator iter)
91 : iter_(iter) {
92 }
93 ~MapValueIteratorAdapter() {}
94
95 DownloadItem* operator*() { return iter_->second; }
96
97 MapValueIteratorAdapter& operator++() {
98 ++iter_;
99 return *this;
100 }
101
102 bool operator!=(const MapValueIteratorAdapter& that) const {
103 return iter_ != that.iter_;
104 }
105
106 private:
107 base::hash_map<int64, DownloadItem*>::const_iterator iter_;
108 // Allow copy and assign.
109};
110
[email protected]a0ce3282011-08-19 20:49:52111} // namespace
112
[email protected]99907362012-01-11 05:41:40113namespace content {
114
115// static
116DownloadManager* DownloadManager::Create(
[email protected]75e51b52012-02-04 16:57:54117 content::DownloadManagerDelegate* delegate) {
118 return new DownloadManagerImpl(delegate);
[email protected]99907362012-01-11 05:41:40119}
120
121} // namespace content
122
[email protected]5656f8a2011-11-17 16:12:58123DownloadManagerImpl::DownloadManagerImpl(
[email protected]75e51b52012-02-04 16:57:54124 content::DownloadManagerDelegate* delegate)
[email protected]5656f8a2011-11-17 16:12:58125 : shutdown_needed_(false),
126 browser_context_(NULL),
127 file_manager_(NULL),
[email protected]5656f8a2011-11-17 16:12:58128 delegate_(delegate),
[email protected]5656f8a2011-11-17 16:12:58129 largest_db_handle_in_history_(DownloadItem::kUninitializedHandle) {
initial.commit09911bf2008-07-26 23:55:29130}
131
[email protected]5656f8a2011-11-17 16:12:58132DownloadManagerImpl::~DownloadManagerImpl() {
[email protected]326a6a92010-09-10 20:21:13133 DCHECK(!shutdown_needed_);
initial.commit09911bf2008-07-26 23:55:29134}
135
[email protected]5656f8a2011-11-17 16:12:58136DownloadId DownloadManagerImpl::GetNextId() {
[email protected]98e814062012-01-27 00:35:42137 return delegate_->GetNextId();
[email protected]2909e342011-10-29 00:46:53138}
139
[email protected]fc03de22011-12-06 23:28:12140bool DownloadManagerImpl::ShouldOpenDownload(DownloadItem* item) {
141 return delegate_->ShouldOpenDownload(item);
142}
143
144bool DownloadManagerImpl::ShouldOpenFileBasedOnExtension(const FilePath& path) {
145 return delegate_->ShouldOpenFileBasedOnExtension(path);
146}
147
[email protected]5656f8a2011-11-17 16:12:58148void DownloadManagerImpl::Shutdown() {
[email protected]da6e3922010-11-24 21:45:50149 VLOG(20) << __FUNCTION__ << "()"
150 << " shutdown_needed_ = " << shutdown_needed_;
[email protected]326a6a92010-09-10 20:21:13151 if (!shutdown_needed_)
152 return;
153 shutdown_needed_ = false;
initial.commit09911bf2008-07-26 23:55:29154
[email protected]75e51b52012-02-04 16:57:54155 FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown(this));
[email protected]fb4f8d902011-09-16 06:07:08156 // TODO(benjhayden): Consider clearing observers_.
[email protected]326a6a92010-09-10 20:21:13157
158 if (file_manager_) {
[email protected]ca4b5fa32010-10-09 12:42:18159 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
[email protected]fabf36d22011-10-28 21:50:17160 base::Bind(&DownloadFileManager::OnDownloadManagerShutdown,
161 file_manager_, make_scoped_refptr(this)));
[email protected]326a6a92010-09-10 20:21:13162 }
initial.commit09911bf2008-07-26 23:55:29163
[email protected]f04182f32010-12-10 19:12:07164 AssertContainersConsistent();
165
166 // Go through all downloads in downloads_. Dangerous ones we need to
167 // remove on disk, and in progress ones we need to cancel.
[email protected]57fd1252010-12-23 17:24:09168 for (DownloadSet::iterator it = downloads_.begin(); it != downloads_.end();) {
[email protected]f04182f32010-12-10 19:12:07169 DownloadItem* download = *it;
170
171 // Save iterator from potential erases in this set done by called code.
172 // Iterators after an erasure point are still valid for lists and
173 // associative containers such as sets.
174 it++;
175
[email protected]c09a8fd2011-11-21 19:54:50176 if (download->GetSafetyState() == DownloadItem::DANGEROUS &&
[email protected]48837962011-04-19 17:03:29177 download->IsPartialDownload()) {
[email protected]f04182f32010-12-10 19:12:07178 // The user hasn't accepted it, so we need to remove it
179 // from the disk. This may or may not result in it being
180 // removed from the DownloadManager queues and deleted
[email protected]fc03de22011-12-06 23:28:12181 // (specifically, DownloadManager::DownloadRemoved only
[email protected]f04182f32010-12-10 19:12:07182 // removes and deletes it if it's known to the history service)
183 // so the only thing we know after calling this function is that
184 // the download was deleted if-and-only-if it was removed
185 // from all queues.
[email protected]303077002011-04-19 23:21:01186 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN);
[email protected]bf68a00b2011-04-07 17:28:26187 } else if (download->IsPartialDownload()) {
[email protected]93af2272011-09-21 18:29:17188 download->Cancel(false);
189 delegate_->UpdateItemInPersistentStore(download);
initial.commit09911bf2008-07-26 23:55:29190 }
191 }
192
[email protected]f04182f32010-12-10 19:12:07193 // At this point, all dangerous downloads have had their files removed
194 // and all in progress downloads have been cancelled. We can now delete
195 // anything left.
[email protected]9ccbb372008-10-10 18:50:32196
[email protected]5cd11b6e2011-06-10 20:30:59197 // Copy downloads_ to separate container so as not to set off checks
198 // in DownloadItem destruction.
199 DownloadSet downloads_to_delete;
200 downloads_to_delete.swap(downloads_);
201
initial.commit09911bf2008-07-26 23:55:29202 in_progress_.clear();
[email protected]70850c72011-01-11 17:31:27203 active_downloads_.clear();
[email protected]5cd11b6e2011-06-10 20:30:59204 history_downloads_.clear();
[email protected]5cd11b6e2011-06-10 20:30:59205 STLDeleteElements(&downloads_to_delete);
initial.commit09911bf2008-07-26 23:55:29206
[email protected]41f558fb2012-01-09 22:59:58207 // We'll have nothing more to report to the observers after this point.
208 observers_.Clear();
209
[email protected]6d0146c2011-08-04 19:13:04210 DCHECK(save_page_downloads_.empty());
211
initial.commit09911bf2008-07-26 23:55:29212 file_manager_ = NULL;
[email protected]2588ea9d2011-08-22 20:59:53213 delegate_->Shutdown();
initial.commit09911bf2008-07-26 23:55:29214}
215
[email protected]5656f8a2011-11-17 16:12:58216void DownloadManagerImpl::GetTemporaryDownloads(
[email protected]6d0146c2011-08-04 19:13:04217 const FilePath& dir_path, DownloadVector* result) {
[email protected]82f37b02010-07-29 22:04:57218 DCHECK(result);
[email protected]6aa4a1c02010-01-15 18:49:58219
[email protected]f04182f32010-12-10 19:12:07220 for (DownloadMap::iterator it = history_downloads_.begin();
221 it != history_downloads_.end(); ++it) {
[email protected]c09a8fd2011-11-21 19:54:50222 if (it->second->IsTemporary() &&
[email protected]fdd2715c2011-12-09 22:24:20223 (dir_path.empty() || it->second->GetFullPath().DirName() == dir_path))
[email protected]82f37b02010-07-29 22:04:57224 result->push_back(it->second);
[email protected]6aa4a1c02010-01-15 18:49:58225 }
[email protected]6aa4a1c02010-01-15 18:49:58226}
227
[email protected]5656f8a2011-11-17 16:12:58228void DownloadManagerImpl::GetAllDownloads(
[email protected]6d0146c2011-08-04 19:13:04229 const FilePath& dir_path, DownloadVector* result) {
[email protected]82f37b02010-07-29 22:04:57230 DCHECK(result);
[email protected]8ddbd66a2010-05-21 16:38:34231
[email protected]f04182f32010-12-10 19:12:07232 for (DownloadMap::iterator it = history_downloads_.begin();
233 it != history_downloads_.end(); ++it) {
[email protected]c09a8fd2011-11-21 19:54:50234 if (!it->second->IsTemporary() &&
235 (dir_path.empty() || it->second->GetFullPath().DirName() == dir_path))
[email protected]82f37b02010-07-29 22:04:57236 result->push_back(it->second);
[email protected]8ddbd66a2010-05-21 16:38:34237 }
[email protected]8ddbd66a2010-05-21 16:38:34238}
239
[email protected]5656f8a2011-11-17 16:12:58240void DownloadManagerImpl::SearchDownloads(const string16& query,
241 DownloadVector* result) {
[email protected]503d03872011-05-06 08:36:26242 string16 query_lower(base::i18n::ToLower(query));
[email protected]d3b12902010-08-16 23:39:42243
[email protected]f04182f32010-12-10 19:12:07244 for (DownloadMap::iterator it = history_downloads_.begin();
245 it != history_downloads_.end(); ++it) {
[email protected]d3b12902010-08-16 23:39:42246 DownloadItem* download_item = it->second;
247
[email protected]c09a8fd2011-11-21 19:54:50248 if (download_item->IsTemporary())
[email protected]d3b12902010-08-16 23:39:42249 continue;
250
251 // Display Incognito downloads only in Incognito window, and vice versa.
252 // The Incognito Downloads page will get the list of non-Incognito downloads
253 // from its parent profile.
[email protected]c09a8fd2011-11-21 19:54:50254 if (browser_context_->IsOffTheRecord() != download_item->IsOtr())
[email protected]d3b12902010-08-16 23:39:42255 continue;
256
257 if (download_item->MatchesQuery(query_lower))
258 result->push_back(download_item);
259 }
[email protected]d3b12902010-08-16 23:39:42260}
261
initial.commit09911bf2008-07-26 23:55:29262// Query the history service for information about all persisted downloads.
[email protected]5656f8a2011-11-17 16:12:58263bool DownloadManagerImpl::Init(content::BrowserContext* browser_context) {
[email protected]6d0c9fb2011-08-22 19:26:03264 DCHECK(browser_context);
initial.commit09911bf2008-07-26 23:55:29265 DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
266 shutdown_needed_ = true;
267
[email protected]6d0c9fb2011-08-22 19:26:03268 browser_context_ = browser_context;
[email protected]024f2f02010-04-30 22:51:46269
[email protected]b39e7a88b2012-01-10 21:43:17270 // In test mode, there may be no ResourceDispatcherHost. In this case it's
271 // safe to avoid setting |file_manager_| because we only call a small set of
272 // functions, none of which need it.
[email protected]99907362012-01-11 05:41:40273 ResourceDispatcherHost* rdh = ResourceDispatcherHost::Get();
[email protected]b39e7a88b2012-01-10 21:43:17274 if (rdh) {
275 file_manager_ = rdh->download_file_manager();
276 DCHECK(file_manager_);
277 }
initial.commit09911bf2008-07-26 23:55:29278
initial.commit09911bf2008-07-26 23:55:29279 return true;
280}
281
[email protected]aa9881c2011-08-15 18:01:12282// We have received a message from DownloadFileManager about a new download.
[email protected]5656f8a2011-11-17 16:12:58283void DownloadManagerImpl::StartDownload(int32 download_id) {
[email protected]ca4b5fa32010-10-09 12:42:18284 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]287b86b2011-02-26 00:11:35285
[email protected]aa9881c2011-08-15 18:01:12286 if (delegate_->ShouldStartDownload(download_id))
287 RestartDownload(download_id);
[email protected]287b86b2011-02-26 00:11:35288}
289
[email protected]5656f8a2011-11-17 16:12:58290void DownloadManagerImpl::CheckForHistoryFilesRemoval() {
[email protected]9fc114672011-06-15 08:17:48291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292 for (DownloadMap::iterator it = history_downloads_.begin();
293 it != history_downloads_.end(); ++it) {
294 CheckForFileRemoval(it->second);
295 }
296}
297
[email protected]5656f8a2011-11-17 16:12:58298void DownloadManagerImpl::CheckForFileRemoval(DownloadItem* download_item) {
[email protected]9fc114672011-06-15 08:17:48299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
300 if (download_item->IsComplete() &&
[email protected]c09a8fd2011-11-21 19:54:50301 !download_item->GetFileExternallyRemoved()) {
[email protected]9fc114672011-06-15 08:17:48302 BrowserThread::PostTask(
303 BrowserThread::FILE, FROM_HERE,
[email protected]5656f8a2011-11-17 16:12:58304 base::Bind(&DownloadManagerImpl::CheckForFileRemovalOnFileThread,
[email protected]c09a8fd2011-11-21 19:54:50305 this, download_item->GetDbHandle(),
[email protected]fabf36d22011-10-28 21:50:17306 download_item->GetTargetFilePath()));
[email protected]9fc114672011-06-15 08:17:48307 }
308}
309
[email protected]5656f8a2011-11-17 16:12:58310void DownloadManagerImpl::CheckForFileRemovalOnFileThread(
[email protected]9fc114672011-06-15 08:17:48311 int64 db_handle, const FilePath& path) {
312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
313 if (!file_util::PathExists(path)) {
314 BrowserThread::PostTask(
315 BrowserThread::UI, FROM_HERE,
[email protected]5656f8a2011-11-17 16:12:58316 base::Bind(&DownloadManagerImpl::OnFileRemovalDetected,
317 this,
318 db_handle));
[email protected]9fc114672011-06-15 08:17:48319 }
320}
321
[email protected]5656f8a2011-11-17 16:12:58322void DownloadManagerImpl::OnFileRemovalDetected(int64 db_handle) {
[email protected]9fc114672011-06-15 08:17:48323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324 DownloadMap::iterator it = history_downloads_.find(db_handle);
325 if (it != history_downloads_.end()) {
326 DownloadItem* download_item = it->second;
327 download_item->OnDownloadedFileRemoved();
328 }
329}
330
[email protected]443853c62011-12-22 19:22:41331void DownloadManagerImpl::RestartDownload(int32 download_id) {
[email protected]ca4b5fa32010-10-09 12:42:18332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
initial.commit09911bf2008-07-26 23:55:29333
[email protected]4cd82f72011-05-23 19:15:01334 DownloadItem* download = GetActiveDownloadItem(download_id);
335 if (!download)
336 return;
337
338 VLOG(20) << __FUNCTION__ << "()"
339 << " download = " << download->DebugString(true);
340
[email protected]c09a8fd2011-11-21 19:54:50341 FilePath suggested_path = download->GetSuggestedPath();
[email protected]4cd82f72011-05-23 19:15:01342
[email protected]c09a8fd2011-11-21 19:54:50343 if (download->PromptUserForSaveLocation()) {
initial.commit09911bf2008-07-26 23:55:29344 // We must ask the user for the place to put the download.
[email protected]a62d42902012-01-24 17:24:38345 WebContents* contents = download->GetWebContents();
[email protected]99cb7f82011-07-28 17:27:26346
[email protected]4cd82f72011-05-23 19:15:01347 // |id_ptr| will be deleted in either FileSelected() or
[email protected]93af2272011-09-21 18:29:17348 // FileSelectionCancelled().
[email protected]4cd82f72011-05-23 19:15:01349 int32* id_ptr = new int32;
350 *id_ptr = download_id;
[email protected]795b76a2011-12-14 16:52:53351 FilePath target_path;
352 // If |download| is a potentially dangerous file, then |suggested_path|
353 // contains the intermediate name instead of the final download
354 // filename. The latter is GetTargetName().
[email protected]a62d42902012-01-24 17:24:38355 if (download->GetDangerType() !=
356 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)
[email protected]795b76a2011-12-14 16:52:53357 target_path = suggested_path.DirName().Append(download->GetTargetName());
358 else
359 target_path = suggested_path;
[email protected]99cb7f82011-07-28 17:27:26360
[email protected]795b76a2011-12-14 16:52:53361 delegate_->ChooseDownloadPath(contents, target_path,
362 reinterpret_cast<void*>(id_ptr));
[email protected]f5920322011-03-24 20:34:16363 FOR_EACH_OBSERVER(Observer, observers_,
[email protected]75e51b52012-02-04 16:57:54364 SelectFileDialogDisplayed(this, download_id));
initial.commit09911bf2008-07-26 23:55:29365 } else {
366 // No prompting for download, just continue with the suggested name.
[email protected]4cd82f72011-05-23 19:15:01367 ContinueDownloadWithPath(download, suggested_path);
initial.commit09911bf2008-07-26 23:55:29368 }
369}
370
[email protected]37757c62011-12-20 20:07:12371content::BrowserContext* DownloadManagerImpl::GetBrowserContext() const {
[email protected]5656f8a2011-11-17 16:12:58372 return browser_context_;
373}
374
375FilePath DownloadManagerImpl::LastDownloadPath() {
376 return last_download_path_;
377}
378
379void DownloadManagerImpl::CreateDownloadItem(
[email protected]594e66fe2011-10-25 22:49:41380 DownloadCreateInfo* info, const DownloadRequestHandle& request_handle) {
[email protected]c2e76012010-12-23 21:10:29381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
382
[email protected]c09a8fd2011-11-21 19:54:50383 DownloadItem* download = new DownloadItemImpl(
[email protected]ae77da82011-11-01 19:17:29384 this, *info, new DownloadRequestHandle(request_handle),
385 browser_context_->IsOffTheRecord());
[email protected]2909e342011-10-29 00:46:53386 int32 download_id = info->download_id.local();
[email protected]4cd82f72011-05-23 19:15:01387 DCHECK(!ContainsKey(in_progress_, download_id));
[email protected]d8472d92011-08-26 20:15:20388
[email protected]a896ce32012-01-09 22:04:07389 CHECK_96627(!ContainsKey(active_downloads_, download_id));
[email protected]c2e76012010-12-23 21:10:29390 downloads_.insert(download);
[email protected]4cd82f72011-05-23 19:15:01391 active_downloads_[download_id] = download;
[email protected]c2e76012010-12-23 21:10:29392}
393
[email protected]fc03de22011-12-06 23:28:12394DownloadItem* DownloadManagerImpl::CreateSavePackageDownloadItem(
395 const FilePath& main_file_path,
396 const GURL& page_url,
397 bool is_otr,
398 DownloadItem::Observer* observer) {
399 DownloadItem* download = new DownloadItemImpl(
400 this, main_file_path, page_url, is_otr, GetNextId());
401
402 download->AddObserver(observer);
403
404 DCHECK(!ContainsKey(save_page_downloads_, download->GetId()));
405 downloads_.insert(download);
406 save_page_downloads_[download->GetId()] = download;
407
408 // Will notify the observer in the callback.
409 delegate_->AddItemToPersistentStore(download);
410
411 return download;
412}
413
[email protected]795b76a2011-12-14 16:52:53414// For non-safe downloads with no prompting, |chosen_file| is the intermediate
415// path for saving the in-progress download. The final target filename for these
416// is |download->GetTargetName()|. For all other downloads (non-safe downloads
417// for which we have prompted for a save location, and all safe downloads),
418// |chosen_file| is the final target download path.
[email protected]5656f8a2011-11-17 16:12:58419void DownloadManagerImpl::ContinueDownloadWithPath(
420 DownloadItem* download, const FilePath& chosen_file) {
[email protected]ca4b5fa32010-10-09 12:42:18421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]4cd82f72011-05-23 19:15:01422 DCHECK(download);
[email protected]aa033af2010-07-27 18:16:39423
[email protected]c09a8fd2011-11-21 19:54:50424 int32 download_id = download->GetId();
initial.commit09911bf2008-07-26 23:55:29425
[email protected]70850c72011-01-11 17:31:27426 // NOTE(ahendrickson) Eventually |active_downloads_| will replace
427 // |in_progress_|, but we don't want to change the semantics yet.
[email protected]4cd82f72011-05-23 19:15:01428 DCHECK(!ContainsKey(in_progress_, download_id));
[email protected]70850c72011-01-11 17:31:27429 DCHECK(ContainsKey(downloads_, download));
[email protected]4cd82f72011-05-23 19:15:01430 DCHECK(ContainsKey(active_downloads_, download_id));
[email protected]70850c72011-01-11 17:31:27431
[email protected]4cd82f72011-05-23 19:15:01432 // Make sure the initial file name is set only once.
[email protected]fdd2715c2011-12-09 22:24:20433 DCHECK(download->GetFullPath().empty());
[email protected]4cd82f72011-05-23 19:15:01434 download->OnPathDetermined(chosen_file);
[email protected]4cd82f72011-05-23 19:15:01435
436 VLOG(20) << __FUNCTION__ << "()"
437 << " download = " << download->DebugString(true);
438
439 in_progress_[download_id] = download;
[email protected]5f8589fe2011-08-17 20:58:39440 UpdateDownloadProgress(); // Reflect entry into in_progress_.
initial.commit09911bf2008-07-26 23:55:29441
[email protected]adb2f3d12011-01-23 16:24:54442 // Rename to intermediate name.
[email protected]f5920322011-03-24 20:34:16443 FilePath download_path;
[email protected]385e93182012-01-30 17:11:03444 if (download->GetDangerType() !=
445 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) {
446 if (download->PromptUserForSaveLocation()) {
447 // When we prompt the user, we overwrite the FullPath with what the user
448 // wanted to use. Construct a file path using the previously determined
449 // intermediate filename and the new path.
450 // TODO(asanka): This can trample an in-progress download in the new
451 // target directory if it was using the same intermediate name.
452 FilePath file_name = download->GetSuggestedPath().BaseName();
453 download_path = download->GetFullPath().DirName().Append(file_name);
454 } else {
455 // The download's name is already set to an intermediate name, so no need
456 // to override.
457 download_path = download->GetFullPath();
458 }
459 } else {
460 // The download is a safe download. We need to rename it to its
461 // intermediate path. The final name after user confirmation will be set
462 // from DownloadItem::OnDownloadCompleting.
463 download_path = delegate_->GetIntermediatePath(download->GetFullPath());
464 }
[email protected]594cd7d2010-07-21 03:23:56465
[email protected]f5920322011-03-24 20:34:16466 BrowserThread::PostTask(
467 BrowserThread::FILE, FROM_HERE,
[email protected]fabf36d22011-10-28 21:50:17468 base::Bind(&DownloadFileManager::RenameInProgressDownloadFile,
[email protected]fc03de22011-12-06 23:28:12469 file_manager_, download->GetGlobalId(),
470 download_path));
[email protected]f5920322011-03-24 20:34:16471
472 download->Rename(download_path);
473
[email protected]2588ea9d2011-08-22 20:59:53474 delegate_->AddItemToPersistentStore(download);
initial.commit09911bf2008-07-26 23:55:29475}
476
[email protected]443853c62011-12-22 19:22:41477void DownloadManagerImpl::UpdateDownload(int32 download_id,
478 int64 bytes_so_far,
479 int64 bytes_per_sec,
[email protected]0afff032012-01-06 20:55:00480 const std::string& hash_state) {
[email protected]70850c72011-01-11 17:31:27481 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
482 DownloadMap::iterator it = active_downloads_.find(download_id);
483 if (it != active_downloads_.end()) {
initial.commit09911bf2008-07-26 23:55:29484 DownloadItem* download = it->second;
[email protected]bf68a00b2011-04-07 17:28:26485 if (download->IsInProgress()) {
[email protected]443853c62011-12-22 19:22:41486 download->UpdateProgress(bytes_so_far, bytes_per_sec, hash_state);
[email protected]5f8589fe2011-08-17 20:58:39487 UpdateDownloadProgress(); // Reflect size updates.
[email protected]2588ea9d2011-08-22 20:59:53488 delegate_->UpdateItemInPersistentStore(download);
[email protected]70850c72011-01-11 17:31:27489 }
initial.commit09911bf2008-07-26 23:55:29490 }
491}
492
[email protected]5656f8a2011-11-17 16:12:58493void DownloadManagerImpl::OnResponseCompleted(int32 download_id,
494 int64 size,
495 const std::string& hash) {
[email protected]33c6d3f12011-09-04 00:00:54496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]da6e3922010-11-24 21:45:50497 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
498 << " size = " << size;
[email protected]9d7ef802011-02-25 19:03:35499 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]9ccbb372008-10-10 18:50:32500
[email protected]c4f02c42011-01-24 21:55:06501 // If it's not in active_downloads_, that means it was cancelled; just
502 // ignore the notification.
503 if (active_downloads_.count(download_id) == 0)
504 return;
505
[email protected]adb2f3d12011-01-23 16:24:54506 DownloadItem* download = active_downloads_[download_id];
[email protected]ac4af82f2011-11-10 19:09:37507 download->OnAllDataSaved(size, hash);
[email protected]b09f1282011-09-14 00:37:45508
[email protected]fc03de22011-12-06 23:28:12509 download->MaybeCompleteDownload();
[email protected]adb2f3d12011-01-23 16:24:54510}
[email protected]9ccbb372008-10-10 18:50:32511
[email protected]fc03de22011-12-06 23:28:12512void DownloadManagerImpl::AssertStateConsistent(DownloadItem* download) const {
[email protected]a896ce32012-01-09 22:04:07513 // TODO(rdsmith): Change to DCHECK after http://crbug.com/96627 resolved.
[email protected]c09a8fd2011-11-21 19:54:50514 if (download->GetState() == DownloadItem::REMOVING) {
[email protected]7d413112011-06-16 18:50:17515 CHECK(!ContainsKey(downloads_, download));
[email protected]c09a8fd2011-11-21 19:54:50516 CHECK(!ContainsKey(active_downloads_, download->GetId()));
517 CHECK(!ContainsKey(in_progress_, download->GetId()));
518 CHECK(!ContainsKey(history_downloads_, download->GetDbHandle()));
[email protected]7d413112011-06-16 18:50:17519 return;
520 }
521
522 // Should be in downloads_ if we're not REMOVING.
523 CHECK(ContainsKey(downloads_, download));
524
525 // Check history_downloads_ consistency.
[email protected]c09a8fd2011-11-21 19:54:50526 if (download->GetDbHandle() != DownloadItem::kUninitializedHandle) {
527 CHECK(ContainsKey(history_downloads_, download->GetDbHandle()));
[email protected]7d413112011-06-16 18:50:17528 } else {
[email protected]fc03de22011-12-06 23:28:12529 for (DownloadMap::const_iterator it = history_downloads_.begin();
[email protected]7d413112011-06-16 18:50:17530 it != history_downloads_.end(); ++it) {
[email protected]a896ce32012-01-09 22:04:07531 CHECK_96627(it->second != download);
[email protected]7d413112011-06-16 18:50:17532 }
533 }
534
[email protected]c09a8fd2011-11-21 19:54:50535 int64 state = download->GetState();
[email protected]61b75a52011-08-29 16:34:34536 base::debug::Alias(&state);
[email protected]c09a8fd2011-11-21 19:54:50537 if (ContainsKey(active_downloads_, download->GetId())) {
538 if (download->GetDbHandle() != DownloadItem::kUninitializedHandle)
539 CHECK_EQ(DownloadItem::IN_PROGRESS, download->GetState());
540 if (DownloadItem::IN_PROGRESS != download->GetState())
541 CHECK_EQ(DownloadItem::kUninitializedHandle, download->GetDbHandle());
[email protected]f9a2997f2011-09-23 16:54:07542 }
[email protected]c09a8fd2011-11-21 19:54:50543 if (DownloadItem::IN_PROGRESS == download->GetState())
544 CHECK(ContainsKey(active_downloads_, download->GetId()));
[email protected]5cd11b6e2011-06-10 20:30:59545}
546
[email protected]5656f8a2011-11-17 16:12:58547bool DownloadManagerImpl::IsDownloadReadyForCompletion(DownloadItem* download) {
[email protected]adb2f3d12011-01-23 16:24:54548 // If we don't have all the data, the download is not ready for
549 // completion.
[email protected]c09a8fd2011-11-21 19:54:50550 if (!download->AllDataSaved())
[email protected]adb2f3d12011-01-23 16:24:54551 return false;
[email protected]6a7fb042010-02-01 16:30:47552
[email protected]9d7ef802011-02-25 19:03:35553 // If the download is dangerous, but not yet validated, it's not ready for
554 // completion.
[email protected]c09a8fd2011-11-21 19:54:50555 if (download->GetSafetyState() == DownloadItem::DANGEROUS)
[email protected]9d7ef802011-02-25 19:03:35556 return false;
557
[email protected]adb2f3d12011-01-23 16:24:54558 // If the download isn't active (e.g. has been cancelled) it's not
559 // ready for completion.
[email protected]c09a8fd2011-11-21 19:54:50560 if (active_downloads_.count(download->GetId()) == 0)
[email protected]adb2f3d12011-01-23 16:24:54561 return false;
562
563 // If the download hasn't been inserted into the history system
564 // (which occurs strictly after file name determination, intermediate
565 // file rename, and UI display) then it's not ready for completion.
[email protected]c09a8fd2011-11-21 19:54:50566 if (download->GetDbHandle() == DownloadItem::kUninitializedHandle)
[email protected]7054b592011-06-22 14:46:42567 return false;
568
569 return true;
[email protected]adb2f3d12011-01-23 16:24:54570}
571
[email protected]5656f8a2011-11-17 16:12:58572void DownloadManagerImpl::MaybeCompleteDownload(DownloadItem* download) {
[email protected]adb2f3d12011-01-23 16:24:54573 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
574 VLOG(20) << __FUNCTION__ << "()" << " download = "
575 << download->DebugString(false);
576
577 if (!IsDownloadReadyForCompletion(download))
[email protected]9ccbb372008-10-10 18:50:32578 return;
[email protected]9ccbb372008-10-10 18:50:32579
[email protected]adb2f3d12011-01-23 16:24:54580 // TODO(rdsmith): DCHECK that we only pass through this point
581 // once per download. The natural way to do this is by a state
582 // transition on the DownloadItem.
[email protected]594cd7d2010-07-21 03:23:56583
[email protected]adb2f3d12011-01-23 16:24:54584 // Confirm we're in the proper set of states to be here;
[email protected]9d7ef802011-02-25 19:03:35585 // in in_progress_, have all data, have a history handle, (validated or safe).
[email protected]c09a8fd2011-11-21 19:54:50586 DCHECK_NE(DownloadItem::DANGEROUS, download->GetSafetyState());
587 DCHECK_EQ(1u, in_progress_.count(download->GetId()));
588 DCHECK(download->AllDataSaved());
589 DCHECK(download->GetDbHandle() != DownloadItem::kUninitializedHandle);
590 DCHECK_EQ(1u, history_downloads_.count(download->GetDbHandle()));
[email protected]adb2f3d12011-01-23 16:24:54591
[email protected]c2918652011-11-01 18:50:23592 // Give the delegate a chance to override.
593 if (!delegate_->ShouldCompleteDownload(download))
594 return;
595
[email protected]adb2f3d12011-01-23 16:24:54596 VLOG(20) << __FUNCTION__ << "()" << " executing: download = "
597 << download->DebugString(false);
598
599 // Remove the id from in_progress
[email protected]c09a8fd2011-11-21 19:54:50600 in_progress_.erase(download->GetId());
[email protected]5f8589fe2011-08-17 20:58:39601 UpdateDownloadProgress(); // Reflect removal from in_progress_.
[email protected]adb2f3d12011-01-23 16:24:54602
[email protected]2588ea9d2011-08-22 20:59:53603 delegate_->UpdateItemInPersistentStore(download);
[email protected]adb2f3d12011-01-23 16:24:54604
[email protected]f5920322011-03-24 20:34:16605 // Finish the download.
[email protected]48837962011-04-19 17:03:29606 download->OnDownloadCompleting(file_manager_);
[email protected]9ccbb372008-10-10 18:50:32607}
608
[email protected]fc03de22011-12-06 23:28:12609void DownloadManagerImpl::DownloadCompleted(DownloadItem* download) {
[email protected]70850c72011-01-11 17:31:27610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]cc3c7c092011-05-09 18:40:21611 DCHECK(download);
[email protected]2588ea9d2011-08-22 20:59:53612 delegate_->UpdateItemInPersistentStore(download);
[email protected]fc03de22011-12-06 23:28:12613 active_downloads_.erase(download->GetId());
614 AssertStateConsistent(download);
[email protected]70850c72011-01-11 17:31:27615}
616
[email protected]5656f8a2011-11-17 16:12:58617void DownloadManagerImpl::OnDownloadRenamedToFinalName(
618 int download_id,
619 const FilePath& full_path,
620 int uniquifier) {
[email protected]da6e3922010-11-24 21:45:50621 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
[email protected]f5920322011-03-24 20:34:16622 << " full_path = \"" << full_path.value() << "\""
623 << " uniquifier = " << uniquifier;
[email protected]ca4b5fa32010-10-09 12:42:18624 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]f5920322011-03-24 20:34:16625
[email protected]2e030682010-07-23 19:45:36626 DownloadItem* item = GetDownloadItem(download_id);
627 if (!item)
628 return;
[email protected]6cade212008-12-03 00:32:22629
[email protected]a62d42902012-01-24 17:24:38630 if (item->GetDangerType() == content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS ||
[email protected]795b76a2011-12-14 16:52:53631 item->PromptUserForSaveLocation()) {
632 DCHECK_EQ(0, uniquifier)
633 << "We should not uniquify user supplied filenames or safe filenames "
634 "that have already been uniquified.";
[email protected]8fa1eeb52011-04-13 14:18:02635 }
636
[email protected]fabf36d22011-10-28 21:50:17637 BrowserThread::PostTask(
638 BrowserThread::FILE, FROM_HERE,
639 base::Bind(&DownloadFileManager::CompleteDownload,
[email protected]c09a8fd2011-11-21 19:54:50640 file_manager_, item->GetGlobalId()));
[email protected]9ccbb372008-10-10 18:50:32641
[email protected]f5920322011-03-24 20:34:16642 if (uniquifier)
[email protected]c09a8fd2011-11-21 19:54:50643 item->SetPathUniquifier(uniquifier);
[email protected]9ccbb372008-10-10 18:50:32644
[email protected]f5920322011-03-24 20:34:16645 item->OnDownloadRenamedToFinalName(full_path);
[email protected]2588ea9d2011-08-22 20:59:53646 delegate_->UpdatePathForItemInPersistentStore(item, full_path);
initial.commit09911bf2008-07-26 23:55:29647}
648
[email protected]5656f8a2011-11-17 16:12:58649void DownloadManagerImpl::CancelDownload(int32 download_id) {
[email protected]93af2272011-09-21 18:29:17650 DownloadItem* download = GetActiveDownload(download_id);
651 // A cancel at the right time could remove the download from the
652 // |active_downloads_| map before we get here.
653 if (!download)
654 return;
655
656 download->Cancel(true);
657}
658
[email protected]fc03de22011-12-06 23:28:12659void DownloadManagerImpl::DownloadCancelled(DownloadItem* download) {
[email protected]d8472d92011-08-26 20:15:20660 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]d8472d92011-08-26 20:15:20661
662 VLOG(20) << __FUNCTION__ << "()"
[email protected]da6e3922010-11-24 21:45:50663 << " download = " << download->DebugString(true);
664
[email protected]93af2272011-09-21 18:29:17665 RemoveFromActiveList(download);
[email protected]47a881b2011-08-29 22:59:21666 // This function is called from the DownloadItem, so DI state
667 // should already have been updated.
[email protected]fc03de22011-12-06 23:28:12668 AssertStateConsistent(download);
initial.commit09911bf2008-07-26 23:55:29669
[email protected]15d90ba2011-11-03 03:41:55670 if (file_manager_)
671 download->OffThreadCancel(file_manager_);
initial.commit09911bf2008-07-26 23:55:29672}
673
[email protected]5656f8a2011-11-17 16:12:58674void DownloadManagerImpl::OnDownloadInterrupted(int32 download_id,
675 int64 size,
[email protected]0afff032012-01-06 20:55:00676 const std::string& hash_state,
[email protected]5656f8a2011-11-17 16:12:58677 InterruptReason reason) {
[email protected]47a881b2011-08-29 22:59:21678 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
679
680 DownloadItem* download = GetActiveDownload(download_id);
681 if (!download)
682 return;
683
[email protected]be76b7e2011-10-13 12:57:57684 VLOG(20) << __FUNCTION__ << "()"
685 << " reason " << InterruptReasonDebugString(reason)
[email protected]c09a8fd2011-11-21 19:54:50686 << " at offset " << download->GetReceivedBytes()
[email protected]47a881b2011-08-29 22:59:21687 << " size = " << size
688 << " download = " << download->DebugString(true);
689
[email protected]93af2272011-09-21 18:29:17690 RemoveFromActiveList(download);
[email protected]443853c62011-12-22 19:22:41691 download->Interrupted(size, hash_state, reason);
[email protected]93af2272011-09-21 18:29:17692 download->OffThreadCancel(file_manager_);
[email protected]47a881b2011-08-29 22:59:21693}
694
[email protected]5656f8a2011-11-17 16:12:58695DownloadItem* DownloadManagerImpl::GetActiveDownload(int32 download_id) {
[email protected]bf68a00b2011-04-07 17:28:26696 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
697 DownloadMap::iterator it = active_downloads_.find(download_id);
[email protected]bf68a00b2011-04-07 17:28:26698 if (it == active_downloads_.end())
[email protected]47a881b2011-08-29 22:59:21699 return NULL;
[email protected]bf68a00b2011-04-07 17:28:26700
701 DownloadItem* download = it->second;
702
[email protected]47a881b2011-08-29 22:59:21703 DCHECK(download);
[email protected]c09a8fd2011-11-21 19:54:50704 DCHECK_EQ(download_id, download->GetId());
[email protected]4cd82f72011-05-23 19:15:01705
[email protected]47a881b2011-08-29 22:59:21706 return download;
707}
[email protected]54610672011-07-18 18:24:43708
[email protected]5656f8a2011-11-17 16:12:58709void DownloadManagerImpl::RemoveFromActiveList(DownloadItem* download) {
[email protected]93af2272011-09-21 18:29:17710 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
711 DCHECK(download);
712
713 // Clean up will happen when the history system create callback runs if we
714 // don't have a valid db_handle yet.
[email protected]c09a8fd2011-11-21 19:54:50715 if (download->GetDbHandle() != DownloadItem::kUninitializedHandle) {
716 in_progress_.erase(download->GetId());
717 active_downloads_.erase(download->GetId());
[email protected]93af2272011-09-21 18:29:17718 UpdateDownloadProgress(); // Reflect removal from in_progress_.
719 delegate_->UpdateItemInPersistentStore(download);
720 }
721}
722
[email protected]fd3a82832012-01-19 20:35:12723bool DownloadManagerImpl::GenerateFileHash() {
724 return delegate_->GenerateFileHash();
725}
726
[email protected]5656f8a2011-11-17 16:12:58727content::DownloadManagerDelegate* DownloadManagerImpl::delegate() const {
728 return delegate_;
729}
730
731void DownloadManagerImpl::SetDownloadManagerDelegate(
[email protected]1bd0ef12011-10-20 05:24:17732 content::DownloadManagerDelegate* delegate) {
[email protected]9bb54ee2011-10-12 17:43:35733 delegate_ = delegate;
734}
735
[email protected]5656f8a2011-11-17 16:12:58736void DownloadManagerImpl::UpdateDownloadProgress() {
[email protected]5f8589fe2011-08-17 20:58:39737 delegate_->DownloadProgressUpdated();
[email protected]6a7fb042010-02-01 16:30:47738}
739
[email protected]5656f8a2011-11-17 16:12:58740int DownloadManagerImpl::RemoveDownloadItems(
[email protected]6d0146c2011-08-04 19:13:04741 const DownloadVector& pending_deletes) {
742 if (pending_deletes.empty())
743 return 0;
744
745 // Delete from internal maps.
746 for (DownloadVector::const_iterator it = pending_deletes.begin();
747 it != pending_deletes.end();
748 ++it) {
749 DownloadItem* download = *it;
750 DCHECK(download);
[email protected]c09a8fd2011-11-21 19:54:50751 history_downloads_.erase(download->GetDbHandle());
752 save_page_downloads_.erase(download->GetId());
[email protected]6d0146c2011-08-04 19:13:04753 downloads_.erase(download);
754 }
755
756 // Tell observers to refresh their views.
757 NotifyModelChanged();
758
759 // Delete the download items themselves.
760 const int num_deleted = static_cast<int>(pending_deletes.size());
761 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
762 return num_deleted;
763}
764
[email protected]fc03de22011-12-06 23:28:12765void DownloadManagerImpl::DownloadRemoved(DownloadItem* download) {
766 if (history_downloads_.find(download->GetDbHandle()) ==
767 history_downloads_.end())
[email protected]93af2272011-09-21 18:29:17768 return;
769
770 // Make history update.
[email protected]93af2272011-09-21 18:29:17771 delegate_->RemoveItemFromPersistentStore(download);
initial.commit09911bf2008-07-26 23:55:29772
773 // Remove from our tables and delete.
[email protected]6d0146c2011-08-04 19:13:04774 int downloads_count = RemoveDownloadItems(DownloadVector(1, download));
[email protected]f04182f32010-12-10 19:12:07775 DCHECK_EQ(1, downloads_count);
initial.commit09911bf2008-07-26 23:55:29776}
777
[email protected]fd3a82832012-01-19 20:35:12778int DownloadManagerImpl::RemoveDownloadsBetween(base::Time remove_begin,
779 base::Time remove_end) {
[email protected]2588ea9d2011-08-22 20:59:53780 delegate_->RemoveItemsFromPersistentStoreBetween(remove_begin, remove_end);
initial.commit09911bf2008-07-26 23:55:29781
[email protected]a312a442010-12-15 23:40:33782 // All downloads visible to the user will be in the history,
783 // so scan that map.
[email protected]6d0146c2011-08-04 19:13:04784 DownloadVector pending_deletes;
785 for (DownloadMap::const_iterator it = history_downloads_.begin();
786 it != history_downloads_.end();
787 ++it) {
initial.commit09911bf2008-07-26 23:55:29788 DownloadItem* download = it->second;
[email protected]c09a8fd2011-11-21 19:54:50789 if (download->GetStartTime() >= remove_begin &&
790 (remove_end.is_null() || download->GetStartTime() < remove_end) &&
[email protected]6d0146c2011-08-04 19:13:04791 (download->IsComplete() || download->IsCancelled())) {
[email protected]fc03de22011-12-06 23:28:12792 AssertStateConsistent(download);
[email protected]78b8fcc92009-03-31 17:36:28793 pending_deletes.push_back(download);
initial.commit09911bf2008-07-26 23:55:29794 }
initial.commit09911bf2008-07-26 23:55:29795 }
[email protected]6d0146c2011-08-04 19:13:04796 return RemoveDownloadItems(pending_deletes);
initial.commit09911bf2008-07-26 23:55:29797}
798
[email protected]fd3a82832012-01-19 20:35:12799int DownloadManagerImpl::RemoveDownloads(base::Time remove_begin) {
[email protected]e93d2822009-01-30 05:59:59800 return RemoveDownloadsBetween(remove_begin, base::Time());
initial.commit09911bf2008-07-26 23:55:29801}
802
[email protected]5656f8a2011-11-17 16:12:58803int DownloadManagerImpl::RemoveAllDownloads() {
[email protected]da4a5582011-10-17 19:08:06804 download_stats::RecordClearAllSize(history_downloads_.size());
[email protected]d41355e6f2009-04-07 21:21:12805 // The null times make the date range unbounded.
806 return RemoveDownloadsBetween(base::Time(), base::Time());
807}
808
initial.commit09911bf2008-07-26 23:55:29809// Initiate a download of a specific URL. We send the request to the
810// ResourceDispatcherHost, and let it send us responses like a regular
811// download.
[email protected]0d4e30c2012-01-28 00:47:53812void DownloadManagerImpl::DownloadUrl(
813 const GURL& url,
814 const GURL& referrer,
815 const std::string& referrer_charset,
816 bool prefer_cache,
817 const DownloadSaveInfo& save_info,
818 WebContents* web_contents) {
[email protected]c79a0c02011-08-22 22:37:37819 ResourceDispatcherHost* resource_dispatcher_host =
[email protected]99907362012-01-11 05:41:40820 ResourceDispatcherHost::Get();
[email protected]443853c62011-12-22 19:22:41821
[email protected]ed24fad2011-05-10 22:44:01822 // We send a pointer to content::ResourceContext, instead of the usual
823 // reference, so that a copy of the object isn't made.
[email protected]fabf36d22011-10-28 21:50:17824 // base::Bind can't handle 7 args, so we use URLParams and RenderParams.
825 BrowserThread::PostTask(
826 BrowserThread::IO, FROM_HERE,
[email protected]0d4e30c2012-01-28 00:47:53827 base::Bind(
828 &BeginDownload,
829 URLParams(url, referrer),
830 prefer_cache,
831 save_info,
832 resource_dispatcher_host,
[email protected]fbc5e5f92012-01-02 06:08:32833 RenderParams(web_contents->GetRenderProcessHost()->GetID(),
834 web_contents->GetRenderViewHost()->routing_id()),
835 &web_contents->GetBrowserContext()->GetResourceContext()));
initial.commit09911bf2008-07-26 23:55:29836}
837
[email protected]5656f8a2011-11-17 16:12:58838void DownloadManagerImpl::AddObserver(Observer* observer) {
initial.commit09911bf2008-07-26 23:55:29839 observers_.AddObserver(observer);
[email protected]75e51b52012-02-04 16:57:54840 observer->ModelChanged(this);
initial.commit09911bf2008-07-26 23:55:29841}
842
[email protected]5656f8a2011-11-17 16:12:58843void DownloadManagerImpl::RemoveObserver(Observer* observer) {
initial.commit09911bf2008-07-26 23:55:29844 observers_.RemoveObserver(observer);
845}
846
[email protected]5656f8a2011-11-17 16:12:58847void DownloadManagerImpl::FileSelected(const FilePath& path, void* params) {
[email protected]4cd82f72011-05-23 19:15:01848 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
849
850 int32* id_ptr = reinterpret_cast<int32*>(params);
851 DCHECK(id_ptr != NULL);
852 int32 download_id = *id_ptr;
853 delete id_ptr;
854
855 DownloadItem* download = GetActiveDownloadItem(download_id);
856 if (!download)
857 return;
858 VLOG(20) << __FUNCTION__ << "()" << " path = \"" << path.value() << "\""
859 << " download = " << download->DebugString(true);
860
[email protected]c09a8fd2011-11-21 19:54:50861 if (download->PromptUserForSaveLocation())
[email protected]7ae7c2cb2009-01-06 23:31:41862 last_download_path_ = path.DirName();
[email protected]287b86b2011-02-26 00:11:35863
[email protected]4cd82f72011-05-23 19:15:01864 // Make sure the initial file name is set only once.
865 ContinueDownloadWithPath(download, path);
initial.commit09911bf2008-07-26 23:55:29866}
867
[email protected]5656f8a2011-11-17 16:12:58868void DownloadManagerImpl::FileSelectionCanceled(void* params) {
initial.commit09911bf2008-07-26 23:55:29869 // The user didn't pick a place to save the file, so need to cancel the
870 // download that's already in progress to the temporary location.
[email protected]4cd82f72011-05-23 19:15:01871 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
872 int32* id_ptr = reinterpret_cast<int32*>(params);
873 DCHECK(id_ptr != NULL);
874 int32 download_id = *id_ptr;
875 delete id_ptr;
876
877 DownloadItem* download = GetActiveDownloadItem(download_id);
878 if (!download)
879 return;
880
881 VLOG(20) << __FUNCTION__ << "()"
882 << " download = " << download->DebugString(true);
883
[email protected]93af2272011-09-21 18:29:17884 // TODO(ahendrickson) -- This currently has no effect, as the download is
885 // not put on the active list until the file selection is complete. Need
886 // to put it on the active list earlier in the process.
887 RemoveFromActiveList(download);
888
889 download->OffThreadCancel(file_manager_);
[email protected]4cd82f72011-05-23 19:15:01890}
891
initial.commit09911bf2008-07-26 23:55:29892// Operations posted to us from the history service ----------------------------
893
894// The history service has retrieved all download entries. 'entries' contains
[email protected]bb1a4212011-08-22 22:38:25895// 'DownloadPersistentStoreInfo's in sorted order (by ascending start_time).
[email protected]5656f8a2011-11-17 16:12:58896void DownloadManagerImpl::OnPersistentStoreQueryComplete(
[email protected]bb1a4212011-08-22 22:38:25897 std::vector<DownloadPersistentStoreInfo>* entries) {
[email protected]d8472d92011-08-26 20:15:20898 // TODO(rdsmith): Remove this and related logic when
[email protected]a896ce32012-01-09 22:04:07899 // http://crbug.com/96627 is fixed.
[email protected]d8472d92011-08-26 20:15:20900 largest_db_handle_in_history_ = 0;
901
initial.commit09911bf2008-07-26 23:55:29902 for (size_t i = 0; i < entries->size(); ++i) {
[email protected]fc03de22011-12-06 23:28:12903 DownloadItem* download = new DownloadItemImpl(
904 this, GetNextId(), entries->at(i));
[email protected]a896ce32012-01-09 22:04:07905 CHECK_96627(!ContainsKey(history_downloads_, download->GetDbHandle()));
[email protected]f04182f32010-12-10 19:12:07906 downloads_.insert(download);
[email protected]c09a8fd2011-11-21 19:54:50907 history_downloads_[download->GetDbHandle()] = download;
[email protected]da6e3922010-11-24 21:45:50908 VLOG(20) << __FUNCTION__ << "()" << i << ">"
909 << " download = " << download->DebugString(true);
[email protected]d8472d92011-08-26 20:15:20910
[email protected]c09a8fd2011-11-21 19:54:50911 if (download->GetDbHandle() > largest_db_handle_in_history_)
912 largest_db_handle_in_history_ = download->GetDbHandle();
initial.commit09911bf2008-07-26 23:55:29913 }
[email protected]b0ab1d42010-02-24 19:29:28914 NotifyModelChanged();
[email protected]9fc114672011-06-15 08:17:48915 CheckForHistoryFilesRemoval();
initial.commit09911bf2008-07-26 23:55:29916}
917
[email protected]5656f8a2011-11-17 16:12:58918void DownloadManagerImpl::AddDownloadItemToHistory(DownloadItem* download,
919 int64 db_handle) {
[email protected]70850c72011-01-11 17:31:27920 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]d2a8fb72010-01-21 05:31:42921
[email protected]a896ce32012-01-09 22:04:07922 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/96627
[email protected]1e9fe7ff2011-06-24 17:37:33923 // is fixed.
[email protected]2588ea9d2011-08-22 20:59:53924 CHECK_NE(DownloadItem::kUninitializedHandle, db_handle);
[email protected]1e9fe7ff2011-06-24 17:37:33925
[email protected]da4a5582011-10-17 19:08:06926 download_stats::RecordHistorySize(history_downloads_.size());
927
[email protected]c09a8fd2011-11-21 19:54:50928 DCHECK(download->GetDbHandle() == DownloadItem::kUninitializedHandle);
929 download->SetDbHandle(db_handle);
[email protected]5bcd73eb2011-03-23 21:14:02930
[email protected]a896ce32012-01-09 22:04:07931 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/96627
[email protected]d8472d92011-08-26 20:15:20932 // is fixed.
[email protected]a896ce32012-01-09 22:04:07933 CHECK_96627(!ContainsKey(history_downloads_, download->GetDbHandle()));
[email protected]c09a8fd2011-11-21 19:54:50934 history_downloads_[download->GetDbHandle()] = download;
[email protected]6d0146c2011-08-04 19:13:04935
936 // Show in the appropriate browser UI.
937 // This includes buttons to save or cancel, for a dangerous download.
938 ShowDownloadInBrowser(download);
939
940 // Inform interested objects about the new download.
941 NotifyModelChanged();
[email protected]f9a45b02011-06-30 23:49:19942}
943
[email protected]2588ea9d2011-08-22 20:59:53944
[email protected]5656f8a2011-11-17 16:12:58945void DownloadManagerImpl::OnItemAddedToPersistentStore(int32 download_id,
946 int64 db_handle) {
[email protected]2588ea9d2011-08-22 20:59:53947 if (save_page_downloads_.count(download_id)) {
948 OnSavePageItemAddedToPersistentStore(download_id, db_handle);
949 } else if (active_downloads_.count(download_id)) {
950 OnDownloadItemAddedToPersistentStore(download_id, db_handle);
951 }
952 // It's valid that we don't find a matching item, i.e. on shutdown.
953}
954
[email protected]f9a45b02011-06-30 23:49:19955// Once the new DownloadItem's creation info has been committed to the history
956// service, we associate the DownloadItem with the db handle, update our
957// 'history_downloads_' map and inform observers.
[email protected]5656f8a2011-11-17 16:12:58958void DownloadManagerImpl::OnDownloadItemAddedToPersistentStore(
959 int32 download_id, int64 db_handle) {
[email protected]f9a45b02011-06-30 23:49:19960 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]19420cc2011-07-18 17:43:45961 DownloadItem* download = GetActiveDownloadItem(download_id);
[email protected]93af2272011-09-21 18:29:17962 if (!download)
[email protected]19420cc2011-07-18 17:43:45963 return;
[email protected]54610672011-07-18 18:24:43964
965 VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle
966 << " download_id = " << download_id
967 << " download = " << download->DebugString(true);
[email protected]f9a45b02011-06-30 23:49:19968
[email protected]a896ce32012-01-09 22:04:07969 // TODO(rdsmith): Remove after http://crbug.com/96627 resolved.
[email protected]d8472d92011-08-26 20:15:20970 int64 largest_handle = largest_db_handle_in_history_;
971 base::debug::Alias(&largest_handle);
[email protected]e5107ce2011-09-19 20:36:13972 int32 matching_item_download_id
973 = (ContainsKey(history_downloads_, db_handle) ?
[email protected]c09a8fd2011-11-21 19:54:50974 history_downloads_[db_handle]->GetId() : 0);
[email protected]e5107ce2011-09-19 20:36:13975 base::debug::Alias(&matching_item_download_id);
976
[email protected]a896ce32012-01-09 22:04:07977 CHECK_96627(!ContainsKey(history_downloads_, db_handle));
[email protected]d8472d92011-08-26 20:15:20978
[email protected]f9a45b02011-06-30 23:49:19979 AddDownloadItemToHistory(download, db_handle);
initial.commit09911bf2008-07-26 23:55:29980
[email protected]93af2272011-09-21 18:29:17981 // If the download is still in progress, try to complete it.
982 //
983 // Otherwise, download has been cancelled or interrupted before we've
984 // received the DB handle. We post one final message to the history
985 // service so that it can be properly in sync with the DownloadItem's
986 // completion status, and also inform any observers so that they get
987 // more than just the start notification.
988 if (download->IsInProgress()) {
989 MaybeCompleteDownload(download);
990 } else {
[email protected]a896ce32012-01-09 22:04:07991 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/96627
[email protected]93af2272011-09-21 18:29:17992 // is fixed.
993 CHECK(download->IsCancelled())
994 << " download = " << download->DebugString(true);
995 in_progress_.erase(download_id);
996 active_downloads_.erase(download_id);
997 delegate_->UpdateItemInPersistentStore(download);
998 download->UpdateObservers();
999 }
initial.commit09911bf2008-07-26 23:55:291000}
1001
[email protected]5656f8a2011-11-17 16:12:581002void DownloadManagerImpl::ShowDownloadInBrowser(DownloadItem* download) {
[email protected]8ddbd66a2010-05-21 16:38:341003 // The 'contents' may no longer exist if the user closed the tab before we
[email protected]99cb7f82011-07-28 17:27:261004 // get this start completion event.
[email protected]a62d42902012-01-24 17:24:381005 WebContents* content = download->GetWebContents();
[email protected]99cb7f82011-07-28 17:27:261006
1007 // If the contents no longer exists, we ask the embedder to suggest another
1008 // tab.
[email protected]da1a27b2011-07-29 23:16:331009 if (!content)
[email protected]ef9572e2012-01-04 22:14:121010 content = delegate_->GetAlternativeWebContentsToNotifyForDownload();
[email protected]5e595482009-05-06 20:16:531011
[email protected]0bfbf882011-12-22 18:19:271012 if (content && content->GetDelegate())
1013 content->GetDelegate()->OnStartDownload(content, download);
[email protected]5e595482009-05-06 20:16:531014}
1015
[email protected]5656f8a2011-11-17 16:12:581016int DownloadManagerImpl::InProgressCount() const {
1017 return static_cast<int>(in_progress_.size());
1018}
1019
[email protected]6cade212008-12-03 00:32:221020// Clears the last download path, used to initialize "save as" dialogs.
[email protected]5656f8a2011-11-17 16:12:581021void DownloadManagerImpl::ClearLastDownloadPath() {
[email protected]7ae7c2cb2009-01-06 23:31:411022 last_download_path_ = FilePath();
[email protected]eea46622009-07-15 20:49:381023}
[email protected]b0ab1d42010-02-24 19:29:281024
[email protected]5656f8a2011-11-17 16:12:581025void DownloadManagerImpl::NotifyModelChanged() {
[email protected]75e51b52012-02-04 16:57:541026 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged(this));
[email protected]b0ab1d42010-02-24 19:29:281027}
1028
[email protected]5656f8a2011-11-17 16:12:581029DownloadItem* DownloadManagerImpl::GetDownloadItem(int download_id) {
[email protected]4cd82f72011-05-23 19:15:011030 // The |history_downloads_| map is indexed by the download's db_handle,
1031 // not its id, so we have to iterate.
[email protected]f04182f32010-12-10 19:12:071032 for (DownloadMap::iterator it = history_downloads_.begin();
1033 it != history_downloads_.end(); ++it) {
[email protected]2e030682010-07-23 19:45:361034 DownloadItem* item = it->second;
[email protected]c09a8fd2011-11-21 19:54:501035 if (item->GetId() == download_id)
[email protected]2e030682010-07-23 19:45:361036 return item;
1037 }
1038 return NULL;
1039}
1040
[email protected]5656f8a2011-11-17 16:12:581041DownloadItem* DownloadManagerImpl::GetActiveDownloadItem(int download_id) {
[email protected]5d3e83642011-12-16 01:14:361042 if (ContainsKey(active_downloads_, download_id))
1043 return active_downloads_[download_id];
1044 return NULL;
[email protected]4cd82f72011-05-23 19:15:011045}
1046
[email protected]57fd1252010-12-23 17:24:091047// Confirm that everything in all maps is also in |downloads_|, and that
1048// everything in |downloads_| is also in some other map.
[email protected]5656f8a2011-11-17 16:12:581049void DownloadManagerImpl::AssertContainersConsistent() const {
[email protected]f04182f32010-12-10 19:12:071050#if !defined(NDEBUG)
[email protected]57fd1252010-12-23 17:24:091051 // Turn everything into sets.
[email protected]6d0146c2011-08-04 19:13:041052 const DownloadMap* input_maps[] = {&active_downloads_,
1053 &history_downloads_,
1054 &save_page_downloads_};
1055 DownloadSet active_set, history_set, save_page_set;
1056 DownloadSet* all_sets[] = {&active_set, &history_set, &save_page_set};
1057 DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(all_sets));
[email protected]57fd1252010-12-23 17:24:091058 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input_maps); i++) {
1059 for (DownloadMap::const_iterator it = input_maps[i]->begin();
[email protected]6d0146c2011-08-04 19:13:041060 it != input_maps[i]->end(); ++it) {
1061 all_sets[i]->insert(&*it->second);
[email protected]f04182f32010-12-10 19:12:071062 }
1063 }
[email protected]57fd1252010-12-23 17:24:091064
1065 // Check if each set is fully present in downloads, and create a union.
[email protected]57fd1252010-12-23 17:24:091066 DownloadSet downloads_union;
1067 for (int i = 0; i < static_cast<int>(ARRAYSIZE_UNSAFE(all_sets)); i++) {
1068 DownloadSet remainder;
1069 std::insert_iterator<DownloadSet> insert_it(remainder, remainder.begin());
1070 std::set_difference(all_sets[i]->begin(), all_sets[i]->end(),
1071 downloads_.begin(), downloads_.end(),
1072 insert_it);
1073 DCHECK(remainder.empty());
1074 std::insert_iterator<DownloadSet>
1075 insert_union(downloads_union, downloads_union.end());
1076 std::set_union(downloads_union.begin(), downloads_union.end(),
1077 all_sets[i]->begin(), all_sets[i]->end(),
1078 insert_union);
1079 }
1080
1081 // Is everything in downloads_ present in one of the other sets?
1082 DownloadSet remainder;
1083 std::insert_iterator<DownloadSet>
1084 insert_remainder(remainder, remainder.begin());
1085 std::set_difference(downloads_.begin(), downloads_.end(),
1086 downloads_union.begin(), downloads_union.end(),
1087 insert_remainder);
1088 DCHECK(remainder.empty());
[email protected]f04182f32010-12-10 19:12:071089#endif
1090}
1091
[email protected]6d0146c2011-08-04 19:13:041092// SavePackage will call SavePageDownloadFinished upon completion/cancellation.
[email protected]2588ea9d2011-08-22 20:59:531093// The history callback will call OnSavePageItemAddedToPersistentStore.
[email protected]6d0146c2011-08-04 19:13:041094// If the download finishes before the history callback,
[email protected]2588ea9d2011-08-22 20:59:531095// OnSavePageItemAddedToPersistentStore calls SavePageDownloadFinished, ensuring
1096// that the history event is update regardless of the order in which these two
1097// events complete.
[email protected]6d0146c2011-08-04 19:13:041098// If something removes the download item from the download manager (Remove,
1099// Shutdown) the result will be that the SavePage system will not be able to
1100// properly update the download item (which no longer exists) or the download
1101// history, but the action will complete properly anyway. This may lead to the
1102// history entry being wrong on a reload of chrome (specifically in the case of
1103// Initiation -> History Callback -> Removal -> Completion), but there's no way
1104// to solve that without canceling on Remove (which would then update the DB).
1105
[email protected]5656f8a2011-11-17 16:12:581106void DownloadManagerImpl::OnSavePageItemAddedToPersistentStore(
1107 int32 download_id, int64 db_handle) {
[email protected]6d0146c2011-08-04 19:13:041108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1109
1110 DownloadMap::const_iterator it = save_page_downloads_.find(download_id);
1111 // This can happen if the download manager is shutting down and all maps
1112 // have been cleared.
1113 if (it == save_page_downloads_.end())
1114 return;
1115
1116 DownloadItem* download = it->second;
1117 if (!download) {
1118 NOTREACHED();
1119 return;
1120 }
1121
[email protected]a896ce32012-01-09 22:04:071122 // TODO(rdsmith): Remove after http://crbug.com/96627 resolved.
[email protected]d8472d92011-08-26 20:15:201123 int64 largest_handle = largest_db_handle_in_history_;
1124 base::debug::Alias(&largest_handle);
[email protected]a896ce32012-01-09 22:04:071125 CHECK_96627(!ContainsKey(history_downloads_, db_handle));
[email protected]d8472d92011-08-26 20:15:201126
[email protected]6d0146c2011-08-04 19:13:041127 AddDownloadItemToHistory(download, db_handle);
1128
1129 // Finalize this download if it finished before the history callback.
1130 if (!download->IsInProgress())
1131 SavePageDownloadFinished(download);
1132}
1133
[email protected]5656f8a2011-11-17 16:12:581134void DownloadManagerImpl::SavePageDownloadFinished(DownloadItem* download) {
[email protected]c09a8fd2011-11-21 19:54:501135 if (download->GetDbHandle() != DownloadItem::kUninitializedHandle) {
[email protected]2588ea9d2011-08-22 20:59:531136 delegate_->UpdateItemInPersistentStore(download);
[email protected]c09a8fd2011-11-21 19:54:501137 DCHECK(ContainsKey(save_page_downloads_, download->GetId()));
1138 save_page_downloads_.erase(download->GetId());
[email protected]6d0146c2011-08-04 19:13:041139
1140 if (download->IsComplete())
[email protected]ad50def52011-10-19 23:17:071141 content::NotificationService::current()->Notify(
[email protected]6d0146c2011-08-04 19:13:041142 content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
[email protected]6c2381d2011-10-19 02:52:531143 content::Source<DownloadManager>(this),
1144 content::Details<DownloadItem>(download));
[email protected]6d0146c2011-08-04 19:13:041145 }
1146}
[email protected]da4a5582011-10-17 19:08:061147
[email protected]fc03de22011-12-06 23:28:121148void DownloadManagerImpl::DownloadOpened(DownloadItem* download) {
[email protected]da4a5582011-10-17 19:08:061149 delegate_->UpdateItemInPersistentStore(download);
1150 int num_unopened = 0;
1151 for (DownloadMap::iterator it = history_downloads_.begin();
1152 it != history_downloads_.end(); ++it) {
[email protected]c09a8fd2011-11-21 19:54:501153 if (it->second->IsComplete() && !it->second->GetOpened())
[email protected]da4a5582011-10-17 19:08:061154 ++num_unopened;
1155 }
1156 download_stats::RecordOpensOutstanding(num_unopened);
1157}
[email protected]5656f8a2011-11-17 16:12:581158
1159void DownloadManagerImpl::SetFileManager(DownloadFileManager* file_manager) {
1160 file_manager_ = file_manager;
1161}