blob: d1ec883e6ae0d9713c03b543850e11d35b5cf089 [file] [log] [blame]
[email protected]21ca982c2010-01-26 22:49:551// Copyright (c) 2010 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]cdaa8652008-09-13 02:48:595#include "chrome/browser/download/download_manager.h"
initial.commit09911bf2008-07-26 23:55:296
[email protected]a92b8642009-05-05 23:38:567#include "app/l10n_util.h"
[email protected]21ca982c2010-01-26 22:49:558#include "app/resource_bundle.h"
[email protected]2041cf342010-02-19 03:15:599#include "base/callback.h"
initial.commit09911bf2008-07-26 23:55:2910#include "base/file_util.h"
11#include "base/logging.h"
initial.commit09911bf2008-07-26 23:55:2912#include "base/path_service.h"
[email protected]1b5044d2009-02-24 00:04:1413#include "base/rand_util.h"
[email protected]1b5044d2009-02-24 00:04:1414#include "base/sys_string_conversions.h"
initial.commit09911bf2008-07-26 23:55:2915#include "base/task.h"
[email protected]ce7f62e32010-08-10 23:43:5916#include "base/utf_string_conversions.h"
[email protected]d2a8fb72010-01-21 05:31:4217#include "build/build_config.h"
[email protected]6c69796d2010-07-16 21:41:1618#include "chrome/browser/browser.h"
initial.commit09911bf2008-07-26 23:55:2919#include "chrome/browser/browser_list.h"
20#include "chrome/browser/browser_process.h"
[email protected]d83d03aa2009-11-02 21:44:3721#include "chrome/browser/chrome_thread.h"
[email protected]6c69796d2010-07-16 21:41:1622#include "chrome/browser/download/download_file_manager.h"
[email protected]82f37b02010-07-29 22:04:5723#include "chrome/browser/download/download_history.h"
[email protected]6c69796d2010-07-16 21:41:1624#include "chrome/browser/download/download_item.h"
[email protected]e5dc4222010-08-30 22:16:3225#include "chrome/browser/download/download_prefs.h"
[email protected]e9ef0a62009-08-11 22:50:1326#include "chrome/browser/download/download_util.h"
[email protected]8f783752009-04-01 23:33:4527#include "chrome/browser/extensions/extensions_service.h"
[email protected]6c69796d2010-07-16 21:41:1628#include "chrome/browser/history/download_types.h"
[email protected]be180c802009-10-23 06:33:3129#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]14a000d2010-04-29 21:44:2430#include "chrome/browser/platform_util.h"
initial.commit09911bf2008-07-26 23:55:2931#include "chrome/browser/profile.h"
[email protected]8c8657d62009-01-16 18:31:2632#include "chrome/browser/renderer_host/render_process_host.h"
[email protected]6524b5f92009-01-22 17:48:2533#include "chrome/browser/renderer_host/render_view_host.h"
[email protected]21ca982c2010-01-26 22:49:5534#include "chrome/browser/tab_contents/infobar_delegate.h"
[email protected]57c6a652009-05-04 07:58:3435#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]d2a8fb72010-01-21 05:31:4236#include "chrome/browser/tab_contents/tab_util.h"
initial.commit09911bf2008-07-26 23:55:2937#include "chrome/common/chrome_paths.h"
[email protected]91e1bd82009-09-03 22:04:4038#include "chrome/common/notification_service.h"
39#include "chrome/common/notification_type.h"
initial.commit09911bf2008-07-26 23:55:2940#include "chrome/common/pref_names.h"
[email protected]46072d42008-07-28 14:49:3541#include "googleurl/src/gurl.h"
[email protected]34ac8f32009-02-22 23:03:2742#include "grit/generated_resources.h"
[email protected]21ca982c2010-01-26 22:49:5543#include "grit/theme_resources.h"
initial.commit09911bf2008-07-26 23:55:2944#include "net/base/mime_util.h"
45#include "net/base/net_util.h"
initial.commit09911bf2008-07-26 23:55:2946
[email protected]b7f05882009-02-22 01:21:5647#if defined(OS_WIN)
[email protected]4a0765a2009-05-08 23:12:2548#include "app/win_util.h"
[email protected]a0a9577b2009-05-27 23:52:3249#endif
50
[email protected]21ca982c2010-01-26 22:49:5551namespace {
52
[email protected]b0ab1d42010-02-24 19:29:2853// Used to sort download items based on descending start time.
54bool CompareStartTime(DownloadItem* first, DownloadItem* second) {
55 return first->start_time() > second->start_time();
56}
57
[email protected]21ca982c2010-01-26 22:49:5558} // namespace
59
initial.commit09911bf2008-07-26 23:55:2960DownloadManager::DownloadManager()
61 : shutdown_needed_(false),
62 profile_(NULL),
[email protected]82f37b02010-07-29 22:04:5763 file_manager_(NULL) {
initial.commit09911bf2008-07-26 23:55:2964}
65
66DownloadManager::~DownloadManager() {
[email protected]b0ab1d42010-02-24 19:29:2867 FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown());
68
initial.commit09911bf2008-07-26 23:55:2969 if (shutdown_needed_)
70 Shutdown();
71}
72
73void DownloadManager::Shutdown() {
74 DCHECK(shutdown_needed_) << "Shutdown called when not needed.";
75
76 // Stop receiving download updates
[email protected]2941c2392010-07-15 22:54:3077 if (file_manager_)
78 file_manager_->RemoveDownloadManager(this);
initial.commit09911bf2008-07-26 23:55:2979
initial.commit09911bf2008-07-26 23:55:2980 // 'in_progress_' may contain DownloadItems that have not finished the start
81 // complete (from the history service) and thus aren't in downloads_.
82 DownloadMap::iterator it = in_progress_.begin();
[email protected]9ccbb372008-10-10 18:50:3283 std::set<DownloadItem*> to_remove;
initial.commit09911bf2008-07-26 23:55:2984 for (; it != in_progress_.end(); ++it) {
85 DownloadItem* download = it->second;
[email protected]9ccbb372008-10-10 18:50:3286 if (download->safety_state() == DownloadItem::DANGEROUS) {
87 // Forget about any download that the user did not approve.
88 // Note that we cannot call download->Remove() this would invalidate our
89 // iterator.
90 to_remove.insert(download);
91 continue;
initial.commit09911bf2008-07-26 23:55:2992 }
[email protected]9ccbb372008-10-10 18:50:3293 DCHECK_EQ(DownloadItem::IN_PROGRESS, download->state());
94 download->Cancel(false);
[email protected]82f37b02010-07-29 22:04:5795 download_history_->UpdateEntry(download);
96 if (download->db_handle() == DownloadHistory::kUninitializedHandle) {
initial.commit09911bf2008-07-26 23:55:2997 // An invalid handle means that 'download' does not yet exist in
98 // 'downloads_', so we have to delete it here.
99 delete download;
100 }
101 }
102
[email protected]9ccbb372008-10-10 18:50:32103 // 'dangerous_finished_' contains all complete downloads that have not been
104 // approved. They should be removed.
105 it = dangerous_finished_.begin();
106 for (; it != dangerous_finished_.end(); ++it)
107 to_remove.insert(it->second);
108
109 // Remove the dangerous download that are not approved.
110 for (std::set<DownloadItem*>::const_iterator rm_it = to_remove.begin();
111 rm_it != to_remove.end(); ++rm_it) {
112 DownloadItem* download = *rm_it;
[email protected]e10e17c72008-10-15 17:48:32113 int64 handle = download->db_handle();
[email protected]9ccbb372008-10-10 18:50:32114 download->Remove(true);
[email protected]e10e17c72008-10-15 17:48:32115 // Same as above, delete the download if it is not in 'downloads_' (as the
116 // Remove() call above won't have deleted it).
[email protected]82f37b02010-07-29 22:04:57117 if (handle == DownloadHistory::kUninitializedHandle)
[email protected]9ccbb372008-10-10 18:50:32118 delete download;
119 }
120 to_remove.clear();
121
initial.commit09911bf2008-07-26 23:55:29122 in_progress_.clear();
[email protected]9ccbb372008-10-10 18:50:32123 dangerous_finished_.clear();
initial.commit09911bf2008-07-26 23:55:29124 STLDeleteValues(&downloads_);
125
126 file_manager_ = NULL;
127
initial.commit09911bf2008-07-26 23:55:29128 // Make sure the save as dialog doesn't notify us back if we're gone before
129 // it returns.
130 if (select_file_dialog_.get())
131 select_file_dialog_->ListenerDestroyed();
132
[email protected]82f37b02010-07-29 22:04:57133 download_history_.reset();
134
initial.commit09911bf2008-07-26 23:55:29135 shutdown_needed_ = false;
136}
137
[email protected]82f37b02010-07-29 22:04:57138void DownloadManager::GetTemporaryDownloads(
139 const FilePath& dir_path, std::vector<DownloadItem*>* result) {
140 DCHECK(result);
[email protected]6aa4a1c02010-01-15 18:49:58141
142 for (DownloadMap::iterator it = downloads_.begin();
143 it != downloads_.end(); ++it) {
144 if (it->second->is_temporary() &&
145 it->second->full_path().DirName() == dir_path)
[email protected]82f37b02010-07-29 22:04:57146 result->push_back(it->second);
[email protected]6aa4a1c02010-01-15 18:49:58147 }
[email protected]6aa4a1c02010-01-15 18:49:58148}
149
[email protected]82f37b02010-07-29 22:04:57150void DownloadManager::GetAllDownloads(
151 const FilePath& dir_path, std::vector<DownloadItem*>* result) {
152 DCHECK(result);
[email protected]8ddbd66a2010-05-21 16:38:34153
154 for (DownloadMap::iterator it = downloads_.begin();
155 it != downloads_.end(); ++it) {
156 if (!it->second->is_temporary() &&
157 (dir_path.empty() || it->second->full_path().DirName() == dir_path))
[email protected]82f37b02010-07-29 22:04:57158 result->push_back(it->second);
[email protected]8ddbd66a2010-05-21 16:38:34159 }
[email protected]8ddbd66a2010-05-21 16:38:34160}
161
[email protected]82f37b02010-07-29 22:04:57162void DownloadManager::GetCurrentDownloads(
163 const FilePath& dir_path, std::vector<DownloadItem*>* result) {
164 DCHECK(result);
[email protected]c4a530b2010-03-08 17:33:03165
166 for (DownloadMap::iterator it = downloads_.begin();
167 it != downloads_.end(); ++it) {
168 if (!it->second->is_temporary() &&
169 (it->second->state() == DownloadItem::IN_PROGRESS ||
170 it->second->safety_state() == DownloadItem::DANGEROUS) &&
171 (dir_path.empty() || it->second->full_path().DirName() == dir_path))
[email protected]82f37b02010-07-29 22:04:57172 result->push_back(it->second);
[email protected]c4a530b2010-03-08 17:33:03173 }
[email protected]c4a530b2010-03-08 17:33:03174}
175
[email protected]d3b12902010-08-16 23:39:42176void DownloadManager::SearchDownloads(const string16& query,
177 std::vector<DownloadItem*>* result) {
178 DCHECK(result);
179
180 string16 query_lower(l10n_util::ToLower(query));
181
182 for (DownloadMap::iterator it = downloads_.begin();
183 it != downloads_.end(); ++it) {
184 DownloadItem* download_item = it->second;
185
186 if (download_item->is_temporary() || download_item->is_extension_install())
187 continue;
188
189 // Display Incognito downloads only in Incognito window, and vice versa.
190 // The Incognito Downloads page will get the list of non-Incognito downloads
191 // from its parent profile.
192 if (profile_->IsOffTheRecord() != download_item->is_otr())
193 continue;
194
195 if (download_item->MatchesQuery(query_lower))
196 result->push_back(download_item);
197 }
198
199 // If we have a parent profile, let it add its downloads to the results.
200 Profile* original_profile = profile_->GetOriginalProfile();
201 if (original_profile != profile_)
202 original_profile->GetDownloadManager()->SearchDownloads(query, result);
203}
204
initial.commit09911bf2008-07-26 23:55:29205// Query the history service for information about all persisted downloads.
206bool DownloadManager::Init(Profile* profile) {
207 DCHECK(profile);
208 DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
209 shutdown_needed_ = true;
210
211 profile_ = profile;
[email protected]be180c802009-10-23 06:33:31212 request_context_getter_ = profile_->GetRequestContext();
[email protected]d3b12902010-08-16 23:39:42213 download_history_.reset(new DownloadHistory(profile));
[email protected]82f37b02010-07-29 22:04:57214 download_history_->Load(
215 NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete));
[email protected]024f2f02010-04-30 22:51:46216
[email protected]e5dc4222010-08-30 22:16:32217 download_prefs_.reset(new DownloadPrefs(profile_->GetPrefs()));
218
[email protected]2941c2392010-07-15 22:54:30219 // In test mode, there may be no ResourceDispatcherHost. In this case it's
220 // safe to avoid setting |file_manager_| because we only call a small set of
221 // functions, none of which need it.
initial.commit09911bf2008-07-26 23:55:29222 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
[email protected]2941c2392010-07-15 22:54:30223 if (rdh) {
224 file_manager_ = rdh->download_file_manager();
225 DCHECK(file_manager_);
initial.commit09911bf2008-07-26 23:55:29226 }
227
[email protected]b0ab1d42010-02-24 19:29:28228 other_download_manager_observer_.reset(
229 new OtherDownloadManagerObserver(this));
230
initial.commit09911bf2008-07-26 23:55:29231 return true;
232}
233
initial.commit09911bf2008-07-26 23:55:29234// We have received a message from DownloadFileManager about a new download. We
235// create a download item and store it in our download map, and inform the
236// history system of a new download. Since this method can be called while the
237// history service thread is still reading the persistent state, we do not
238// insert the new DownloadItem into 'downloads_' or inform our observers at this
239// point. OnCreateDatabaseEntryComplete() handles that finalization of the the
240// download creation as a callback from the history thread.
241void DownloadManager::StartDownload(DownloadCreateInfo* info) {
[email protected]d83d03aa2009-11-02 21:44:37242 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
initial.commit09911bf2008-07-26 23:55:29243 DCHECK(info);
244
[email protected]a60c8ae2009-12-25 06:50:57245 // Check whether this download is for an extension install or not.
[email protected]e6875c12010-07-18 11:14:13246 // Allow extensions to be explicitly saved.
247 if (!info->prompt_user_for_save_location) {
[email protected]a60c8ae2009-12-25 06:50:57248 if (UserScript::HasUserScriptFileExtension(info->url) ||
249 info->mime_type == Extension::kMimeType)
250 info->is_extension_install = true;
251 }
252
[email protected]8af9d032010-02-10 00:00:32253 if (info->save_info.file_path.empty()) {
[email protected]2941c2392010-07-15 22:54:30254 FilePath generated_name;
[email protected]7ba53e12010-08-05 17:14:00255 download_util::GenerateFileNameFromInfo(info, &generated_name);
[email protected]2941c2392010-07-15 22:54:30256
257 // Freeze the user's preference for showing a Save As dialog. We're going
258 // to bounce around a bunch of threads and we don't want to worry about race
259 // conditions where the user changes this pref out from under us.
[email protected]e5dc4222010-08-30 22:16:32260 if (download_prefs_->prompt_for_download()) {
[email protected]2941c2392010-07-15 22:54:30261 // But ignore the user's preference for the following scenarios:
262 // 1) Extension installation. Note that we only care here about the case
263 // where an extension is installed, not when one is downloaded with
264 // "save as...".
265 // 2) Filetypes marked "always open." If the user just wants this file
266 // opened, don't bother asking where to keep it.
267 if (!info->is_extension_install &&
268 !ShouldOpenFileBasedOnExtension(generated_name))
[email protected]e6875c12010-07-18 11:14:13269 info->prompt_user_for_save_location = true;
[email protected]2941c2392010-07-15 22:54:30270 }
271
[email protected]8af9d032010-02-10 00:00:32272 // Determine the proper path for a download, by either one of the following:
273 // 1) using the default download directory.
274 // 2) prompting the user.
[email protected]80dc3612010-07-27 19:35:08275 if (info->prompt_user_for_save_location && !last_download_path_.empty()){
[email protected]8af9d032010-02-10 00:00:32276 info->suggested_path = last_download_path_;
[email protected]80dc3612010-07-27 19:35:08277 } else {
[email protected]e5dc4222010-08-30 22:16:32278 info->suggested_path = download_prefs_->download_path();
[email protected]80dc3612010-07-27 19:35:08279 }
[email protected]8af9d032010-02-10 00:00:32280 info->suggested_path = info->suggested_path.Append(generated_name);
281 } else {
282 info->suggested_path = info->save_info.file_path;
283 }
initial.commit09911bf2008-07-26 23:55:29284
[email protected]e6875c12010-07-18 11:14:13285 if (!info->prompt_user_for_save_location &&
286 info->save_info.file_path.empty()) {
[email protected]4289d9b2009-07-25 21:17:34287 // Downloads can be marked as dangerous for two reasons:
288 // a) They have a dangerous-looking filename
289 // b) They are an extension that is not from the gallery
[email protected]e5dc4222010-08-30 22:16:32290 if (download_util::IsExecutableFile(info->suggested_path.BaseName()))
[email protected]4289d9b2009-07-25 21:17:34291 info->is_dangerous = true;
[email protected]a60c8ae2009-12-25 06:50:57292 else if (info->is_extension_install &&
[email protected]b7c2f252009-12-08 00:47:23293 !ExtensionsService::IsDownloadFromGallery(info->url,
294 info->referrer_url)) {
[email protected]4289d9b2009-07-25 21:17:34295 info->is_dangerous = true;
296 }
[email protected]e9ebf3fc2008-10-17 22:06:58297 }
298
initial.commit09911bf2008-07-26 23:55:29299 // We need to move over to the download thread because we don't want to stat
300 // the suggested path on the UI thread.
[email protected]d83d03aa2009-11-02 21:44:37301 ChromeThread::PostTask(
302 ChromeThread::FILE, FROM_HERE,
303 NewRunnableMethod(
304 this, &DownloadManager::CheckIfSuggestedPathExists, info));
initial.commit09911bf2008-07-26 23:55:29305}
306
307void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info) {
[email protected]aa033af2010-07-27 18:16:39308 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
initial.commit09911bf2008-07-26 23:55:29309 DCHECK(info);
310
311 // Check writability of the suggested path. If we can't write to it, default
312 // to the user's "My Documents" directory. We'll prompt them in this case.
[email protected]7ae7c2cb2009-01-06 23:31:41313 FilePath dir = info->suggested_path.DirName();
314 FilePath filename = info->suggested_path.BaseName();
[email protected]9ccbb372008-10-10 18:50:32315 if (!file_util::PathIsWritable(dir)) {
[email protected]e6875c12010-07-18 11:14:13316 info->prompt_user_for_save_location = true;
initial.commit09911bf2008-07-26 23:55:29317 PathService::Get(chrome::DIR_USER_DOCUMENTS, &info->suggested_path);
[email protected]7ae7c2cb2009-01-06 23:31:41318 info->suggested_path = info->suggested_path.Append(filename);
initial.commit09911bf2008-07-26 23:55:29319 }
320
[email protected]6cade212008-12-03 00:32:22321 // If the download is deemed dangerous, we'll use a temporary name for it.
[email protected]e9ebf3fc2008-10-17 22:06:58322 if (info->is_dangerous) {
[email protected]7ae7c2cb2009-01-06 23:31:41323 info->original_name = FilePath(info->suggested_path).BaseName();
[email protected]9ccbb372008-10-10 18:50:32324 // Create a temporary file to hold the file until the user approves its
325 // download.
[email protected]7ae7c2cb2009-01-06 23:31:41326 FilePath::StringType file_name;
327 FilePath path;
[email protected]9ccbb372008-10-10 18:50:32328 while (path.empty()) {
[email protected]594cd7d2010-07-21 03:23:56329 SStringPrintf(&file_name, FILE_PATH_LITERAL("unconfirmed %d.crdownload"),
[email protected]9ccbb372008-10-10 18:50:32330 base::RandInt(0, 100000));
[email protected]7ae7c2cb2009-01-06 23:31:41331 path = dir.Append(file_name);
[email protected]7d3851d82008-12-12 03:26:07332 if (file_util::PathExists(path))
[email protected]7ae7c2cb2009-01-06 23:31:41333 path = FilePath();
[email protected]9ccbb372008-10-10 18:50:32334 }
335 info->suggested_path = path;
[email protected]7a256ea2008-10-17 17:34:16336 } else {
[email protected]594cd7d2010-07-21 03:23:56337 // Do not add the path uniquifier if we are saving to a specific path as in
338 // the drag-out case.
339 if (info->save_info.file_path.empty()) {
340 info->path_uniquifier = download_util::GetUniquePathNumberWithCrDownload(
341 info->suggested_path);
342 }
[email protected]7a256ea2008-10-17 17:34:16343 // We know the final path, build it if necessary.
344 if (info->path_uniquifier > 0) {
[email protected]5a2388a2010-03-26 16:13:39345 download_util::AppendNumberToPath(&(info->suggested_path),
346 info->path_uniquifier);
[email protected]7a256ea2008-10-17 17:34:16347 // Setting path_uniquifier to 0 to make sure we don't try to unique it
348 // later on.
349 info->path_uniquifier = 0;
[email protected]7d3851d82008-12-12 03:26:07350 } else if (info->path_uniquifier == -1) {
351 // We failed to find a unique path. We have to prompt the user.
[email protected]e6875c12010-07-18 11:14:13352 info->prompt_user_for_save_location = true;
[email protected]7a256ea2008-10-17 17:34:16353 }
[email protected]9ccbb372008-10-10 18:50:32354 }
355
[email protected]594cd7d2010-07-21 03:23:56356 // Create an empty file at the suggested path so that we don't allocate the
357 // same "non-existant" path to multiple downloads.
358 // See: http://code.google.com/p/chromium/issues/detail?id=3662
[email protected]e6875c12010-07-18 11:14:13359 if (!info->prompt_user_for_save_location &&
360 info->save_info.file_path.empty()) {
[email protected]594cd7d2010-07-21 03:23:56361 if (info->is_dangerous)
362 file_util::WriteFile(info->suggested_path, "", 0);
363 else
364 file_util::WriteFile(download_util::GetCrDownloadPath(
365 info->suggested_path), "", 0);
[email protected]7d3851d82008-12-12 03:26:07366 }
367
[email protected]d83d03aa2009-11-02 21:44:37368 ChromeThread::PostTask(
369 ChromeThread::UI, FROM_HERE,
initial.commit09911bf2008-07-26 23:55:29370 NewRunnableMethod(this,
371 &DownloadManager::OnPathExistenceAvailable,
372 info));
373}
374
375void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) {
[email protected]d83d03aa2009-11-02 21:44:37376 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
initial.commit09911bf2008-07-26 23:55:29377 DCHECK(info);
378
[email protected]e6875c12010-07-18 11:14:13379 if (info->prompt_user_for_save_location) {
initial.commit09911bf2008-07-26 23:55:29380 // We must ask the user for the place to put the download.
381 if (!select_file_dialog_.get())
382 select_file_dialog_ = SelectFileDialog::Create(this);
383
[email protected]76543b92009-08-31 17:27:45384 TabContents* contents = tab_util::GetTabContentsByID(info->child_id,
385 info->render_view_id);
[email protected]b949f1112009-04-12 20:03:08386 SelectFileDialog::FileTypeInfo file_type_info;
387 file_type_info.extensions.resize(1);
388 file_type_info.extensions[0].push_back(info->suggested_path.Extension());
[email protected]15bc8052009-04-17 19:57:24389 if (!file_type_info.extensions[0][0].empty())
390 file_type_info.extensions[0][0].erase(0, 1); // drop the .
[email protected]b949f1112009-04-12 20:03:08391 file_type_info.include_all_files = true;
[email protected]076700e62009-04-01 18:41:23392 gfx::NativeWindow owning_window =
393 contents ? platform_util::GetTopLevel(contents->GetNativeView()) : NULL;
initial.commit09911bf2008-07-26 23:55:29394 select_file_dialog_->SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE,
[email protected]561abe62009-04-06 18:08:34395 string16(),
396 info->suggested_path,
[email protected]b949f1112009-04-12 20:03:08397 &file_type_info, 0, FILE_PATH_LITERAL(""),
[email protected]0f44d3e2009-03-12 23:36:30398 owning_window, info);
initial.commit09911bf2008-07-26 23:55:29399 } else {
400 // No prompting for download, just continue with the suggested name.
401 ContinueStartDownload(info, info->suggested_path);
402 }
403}
404
405void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info,
[email protected]7ae7c2cb2009-01-06 23:31:41406 const FilePath& target_path) {
[email protected]aa033af2010-07-27 18:16:39407 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
408
initial.commit09911bf2008-07-26 23:55:29409 scoped_ptr<DownloadCreateInfo> infop(info);
410 info->path = target_path;
411
[email protected]aa033af2010-07-27 18:16:39412 DownloadItem* download = new DownloadItem(this, *info,
413 profile_->IsOffTheRecord());
414 DCHECK(!ContainsKey(in_progress_, info->download_id));
415 in_progress_[info->download_id] = download;
initial.commit09911bf2008-07-26 23:55:29416
[email protected]aa033af2010-07-27 18:16:39417 bool download_finished = ContainsKey(pending_finished_downloads_,
418 info->download_id);
[email protected]594cd7d2010-07-21 03:23:56419
420 if (download_finished || info->is_dangerous) {
421 // The download has already finished or the download is not safe.
422 // We can now rename the file to its final name (or its tentative name
423 // in dangerous download cases).
424 ChromeThread::PostTask(
425 ChromeThread::FILE, FROM_HERE,
426 NewRunnableMethod(
427 file_manager_, &DownloadFileManager::OnFinalDownloadName,
428 download->id(), target_path, !info->is_dangerous, this));
429 } else {
430 // The download hasn't finished and it is a safe download. We need to
431 // rename it to its intermediate '.crdownload' path.
432 FilePath download_path = download_util::GetCrDownloadPath(target_path);
433 ChromeThread::PostTask(
434 ChromeThread::FILE, FROM_HERE,
435 NewRunnableMethod(
436 file_manager_, &DownloadFileManager::OnIntermediateDownloadName,
437 download->id(), download_path, this));
438 download->set_need_final_rename(true);
439 }
440
441 if (download_finished) {
442 // If the download already completed by the time we reached this point, then
443 // notify observers that it did.
[email protected]aa033af2010-07-27 18:16:39444 DownloadFinished(info->download_id,
445 pending_finished_downloads_[info->download_id]);
[email protected]594cd7d2010-07-21 03:23:56446 }
initial.commit09911bf2008-07-26 23:55:29447
448 download->Rename(target_path);
449
[email protected]82f37b02010-07-29 22:04:57450 download_history_->AddEntry(*info, download,
451 NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
[email protected]6a7fb042010-02-01 16:30:47452
453 UpdateAppIcon();
initial.commit09911bf2008-07-26 23:55:29454}
455
initial.commit09911bf2008-07-26 23:55:29456void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
457 DownloadMap::iterator it = in_progress_.find(download_id);
458 if (it != in_progress_.end()) {
459 DownloadItem* download = it->second;
460 download->Update(size);
[email protected]82f37b02010-07-29 22:04:57461 download_history_->UpdateEntry(download);
initial.commit09911bf2008-07-26 23:55:29462 }
[email protected]6a7fb042010-02-01 16:30:47463 UpdateAppIcon();
initial.commit09911bf2008-07-26 23:55:29464}
465
466void DownloadManager::DownloadFinished(int32 download_id, int64 size) {
467 DownloadMap::iterator it = in_progress_.find(download_id);
[email protected]9ccbb372008-10-10 18:50:32468 if (it == in_progress_.end()) {
initial.commit09911bf2008-07-26 23:55:29469 // The download is done, but the user hasn't selected a final location for
470 // it yet (the Save As dialog box is probably still showing), so just keep
471 // track of the fact that this download id is complete, when the
472 // DownloadItem is constructed later we'll notify its completion then.
473 PendingFinishedMap::iterator erase_it =
474 pending_finished_downloads_.find(download_id);
475 DCHECK(erase_it == pending_finished_downloads_.end());
476 pending_finished_downloads_[download_id] = size;
[email protected]9ccbb372008-10-10 18:50:32477 return;
initial.commit09911bf2008-07-26 23:55:29478 }
[email protected]9ccbb372008-10-10 18:50:32479
480 // Remove the id from the list of pending ids.
481 PendingFinishedMap::iterator erase_it =
482 pending_finished_downloads_.find(download_id);
483 if (erase_it != pending_finished_downloads_.end())
484 pending_finished_downloads_.erase(erase_it);
485
486 DownloadItem* download = it->second;
487 download->Finished(size);
488
489 // Clean up will happen when the history system create callback runs if we
490 // don't have a valid db_handle yet.
[email protected]82f37b02010-07-29 22:04:57491 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
[email protected]9ccbb372008-10-10 18:50:32492 in_progress_.erase(it);
[email protected]82f37b02010-07-29 22:04:57493 download_history_->UpdateEntry(download);
[email protected]9ccbb372008-10-10 18:50:32494 }
495
[email protected]6a7fb042010-02-01 16:30:47496 UpdateAppIcon();
497
[email protected]9ccbb372008-10-10 18:50:32498 // If this a dangerous download not yet validated by the user, don't do
499 // anything. When the user notifies us, it will trigger a call to
500 // ProceedWithFinishedDangerousDownload.
501 if (download->safety_state() == DownloadItem::DANGEROUS) {
502 dangerous_finished_[download_id] = download;
503 return;
504 }
505
506 if (download->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED) {
[email protected]6cade212008-12-03 00:32:22507 // We first need to rename the downloaded file from its temporary name to
[email protected]9ccbb372008-10-10 18:50:32508 // its final name before we can continue.
[email protected]d83d03aa2009-11-02 21:44:37509 ChromeThread::PostTask(
510 ChromeThread::FILE, FROM_HERE,
[email protected]9ccbb372008-10-10 18:50:32511 NewRunnableMethod(
512 this, &DownloadManager::ProceedWithFinishedDangerousDownload,
513 download->db_handle(),
514 download->full_path(), download->original_name()));
515 return;
516 }
[email protected]594cd7d2010-07-21 03:23:56517
518 if (download->need_final_rename()) {
519 ChromeThread::PostTask(
520 ChromeThread::FILE, FROM_HERE,
521 NewRunnableMethod(
522 file_manager_, &DownloadFileManager::OnFinalDownloadName,
523 download->id(), download->full_path(), false, this));
524 return;
525 }
526
[email protected]9ccbb372008-10-10 18:50:32527 ContinueDownloadFinished(download);
528}
529
[email protected]8f783752009-04-01 23:33:45530void DownloadManager::DownloadRenamedToFinalName(int download_id,
531 const FilePath& full_path) {
[email protected]aa033af2010-07-27 18:16:39532 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
533
[email protected]2e030682010-07-23 19:45:36534 DownloadItem* item = GetDownloadItem(download_id);
535 if (!item)
536 return;
537 item->OnNameFinalized();
[email protected]594cd7d2010-07-21 03:23:56538
[email protected]2e030682010-07-23 19:45:36539 // This was called from DownloadFinished; continue to call
540 // ContinueDownloadFinished.
541 if (item->need_final_rename()) {
542 item->set_need_final_rename(false);
543 ContinueDownloadFinished(item);
[email protected]6aa4a1c02010-01-15 18:49:58544 }
[email protected]8f783752009-04-01 23:33:45545}
546
[email protected]9ccbb372008-10-10 18:50:32547void DownloadManager::ContinueDownloadFinished(DownloadItem* download) {
[email protected]aa033af2010-07-27 18:16:39548 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
549
[email protected]9ccbb372008-10-10 18:50:32550 // If this was a dangerous download, it has now been approved and must be
551 // removed from dangerous_finished_ so it does not get deleted on shutdown.
[email protected]aa033af2010-07-27 18:16:39552 dangerous_finished_.erase(download->id());
[email protected]9ccbb372008-10-10 18:50:32553
[email protected]0aad67b2009-07-15 20:34:28554 // Handle chrome extensions explicitly and skip the shell execute.
[email protected]a60c8ae2009-12-25 06:50:57555 if (download->is_extension_install()) {
[email protected]7ba53e12010-08-05 17:14:00556 download_util::OpenChromeExtension(profile_, this, *download);
[email protected]0aad67b2009-07-15 20:34:28557 download->set_auto_opened(true);
558 } else if (download->open_when_complete() ||
[email protected]6aa4a1c02010-01-15 18:49:58559 ShouldOpenFileBasedOnExtension(download->full_path()) ||
560 download->is_temporary()) {
561 // If the download is temporary, like in drag-and-drop, do not open it but
562 // we still need to set it auto-opened so that it can be removed from the
563 // download shelf.
564 if (!download->is_temporary())
565 OpenDownloadInShell(download, NULL);
[email protected]0aad67b2009-07-15 20:34:28566 download->set_auto_opened(true);
567 }
[email protected]9ccbb372008-10-10 18:50:32568
[email protected]0aad67b2009-07-15 20:34:28569 // Notify our observers that we are complete (the call to Finished() set the
570 // state to complete but did not notify).
571 download->UpdateObservers();
[email protected]6aa4a1c02010-01-15 18:49:58572
573 // The download file is meant to be completed if both the filename is
574 // finalized and the file data is downloaded. The ordering of these two
575 // actions is indeterministic. Thus, if the filename is not finalized yet,
576 // delay the notification.
577 if (download->name_finalized())
578 download->NotifyObserversDownloadFileCompleted();
[email protected]0aad67b2009-07-15 20:34:28579}
[email protected]eccb9d12009-10-28 05:40:09580
[email protected]9ccbb372008-10-10 18:50:32581// Called on the file thread. Renames the downloaded file to its original name.
582void DownloadManager::ProceedWithFinishedDangerousDownload(
583 int64 download_handle,
[email protected]7ae7c2cb2009-01-06 23:31:41584 const FilePath& path,
585 const FilePath& original_name) {
[email protected]9ccbb372008-10-10 18:50:32586 bool success = false;
[email protected]7ae7c2cb2009-01-06 23:31:41587 FilePath new_path;
[email protected]7a256ea2008-10-17 17:34:16588 int uniquifier = 0;
[email protected]9ccbb372008-10-10 18:50:32589 if (file_util::PathExists(path)) {
[email protected]889ed35c2009-01-21 00:07:24590 new_path = path.DirName().Append(original_name);
[email protected]7a256ea2008-10-17 17:34:16591 // Make our name unique at this point, as if a dangerous file is downloading
592 // and a 2nd download is started for a file with the same name, they would
593 // have the same path. This is because we uniquify the name on download
594 // start, and at that time the first file does not exists yet, so the second
595 // file gets the same name.
[email protected]5a2388a2010-03-26 16:13:39596 uniquifier = download_util::GetUniquePathNumber(new_path);
[email protected]7a256ea2008-10-17 17:34:16597 if (uniquifier > 0)
[email protected]5a2388a2010-03-26 16:13:39598 download_util::AppendNumberToPath(&new_path, uniquifier);
[email protected]9ccbb372008-10-10 18:50:32599 success = file_util::Move(path, new_path);
600 } else {
601 NOTREACHED();
602 }
[email protected]6cade212008-12-03 00:32:22603
[email protected]d83d03aa2009-11-02 21:44:37604 ChromeThread::PostTask(
605 ChromeThread::UI, FROM_HERE,
[email protected]9ccbb372008-10-10 18:50:32606 NewRunnableMethod(this, &DownloadManager::DangerousDownloadRenamed,
[email protected]7a256ea2008-10-17 17:34:16607 download_handle, success, new_path, uniquifier));
[email protected]9ccbb372008-10-10 18:50:32608}
609
610// Call from the file thread when the finished dangerous download was renamed.
611void DownloadManager::DangerousDownloadRenamed(int64 download_handle,
612 bool success,
[email protected]7ae7c2cb2009-01-06 23:31:41613 const FilePath& new_path,
[email protected]7a256ea2008-10-17 17:34:16614 int new_path_uniquifier) {
[email protected]9ccbb372008-10-10 18:50:32615 DownloadMap::iterator it = downloads_.find(download_handle);
616 if (it == downloads_.end()) {
617 NOTREACHED();
618 return;
619 }
620
621 DownloadItem* download = it->second;
622 // If we failed to rename the file, we'll just keep the name as is.
[email protected]7a256ea2008-10-17 17:34:16623 if (success) {
624 // We need to update the path uniquifier so that the UI shows the right
625 // name when calling GetFileName().
626 download->set_path_uniquifier(new_path_uniquifier);
[email protected]9ccbb372008-10-10 18:50:32627 RenameDownload(download, new_path);
[email protected]7a256ea2008-10-17 17:34:16628 }
[email protected]9ccbb372008-10-10 18:50:32629
630 // Continue the download finished sequence.
631 ContinueDownloadFinished(download);
initial.commit09911bf2008-07-26 23:55:29632}
633
initial.commit09911bf2008-07-26 23:55:29634void DownloadManager::DownloadCancelled(int32 download_id) {
635 DownloadMap::iterator it = in_progress_.find(download_id);
636 if (it == in_progress_.end())
637 return;
638 DownloadItem* download = it->second;
639
initial.commit09911bf2008-07-26 23:55:29640 // Clean up will happen when the history system create callback runs if we
641 // don't have a valid db_handle yet.
[email protected]82f37b02010-07-29 22:04:57642 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
initial.commit09911bf2008-07-26 23:55:29643 in_progress_.erase(it);
[email protected]82f37b02010-07-29 22:04:57644 download_history_->UpdateEntry(download);
initial.commit09911bf2008-07-26 23:55:29645 }
646
[email protected]d7d1c5c2009-08-05 23:52:50647 DownloadCancelledInternal(download_id,
648 download->render_process_id(),
649 download->request_id());
[email protected]6a7fb042010-02-01 16:30:47650 UpdateAppIcon();
[email protected]d7d1c5c2009-08-05 23:52:50651}
652
653void DownloadManager::DownloadCancelledInternal(int download_id,
654 int render_process_id,
655 int request_id) {
[email protected]d85cf072009-10-27 03:59:31656 // Cancel the network request. RDH is guaranteed to outlive the IO thread.
657 ChromeThread::PostTask(
658 ChromeThread::IO, FROM_HERE,
[email protected]ae8945192010-07-20 16:56:26659 NewRunnableFunction(&download_util::CancelDownloadRequest,
[email protected]d85cf072009-10-27 03:59:31660 g_browser_process->resource_dispatcher_host(),
661 render_process_id,
662 request_id));
[email protected]d7d1c5c2009-08-05 23:52:50663
initial.commit09911bf2008-07-26 23:55:29664 // Tell the file manager to cancel the download.
[email protected]d7d1c5c2009-08-05 23:52:50665 file_manager_->RemoveDownload(download_id, this); // On the UI thread
[email protected]d83d03aa2009-11-02 21:44:37666 ChromeThread::PostTask(
667 ChromeThread::FILE, FROM_HERE,
668 NewRunnableMethod(
669 file_manager_, &DownloadFileManager::CancelDownload, download_id));
initial.commit09911bf2008-07-26 23:55:29670}
671
[email protected]7ba53e12010-08-05 17:14:00672// DownloadManager is owned by RDH, no need for reference counting.
673DISABLE_RUNNABLE_METHOD_REFCOUNT(ResourceDispatcherHost);
674
initial.commit09911bf2008-07-26 23:55:29675void DownloadManager::PauseDownload(int32 download_id, bool pause) {
676 DownloadMap::iterator it = in_progress_.find(download_id);
[email protected]d85cf072009-10-27 03:59:31677 if (it == in_progress_.end())
678 return;
initial.commit09911bf2008-07-26 23:55:29679
[email protected]d85cf072009-10-27 03:59:31680 DownloadItem* download = it->second;
681 if (pause == download->is_paused())
682 return;
initial.commit09911bf2008-07-26 23:55:29683
[email protected]d85cf072009-10-27 03:59:31684 // Inform the ResourceDispatcherHost of the new pause state.
685 ChromeThread::PostTask(
686 ChromeThread::IO, FROM_HERE,
[email protected]7ba53e12010-08-05 17:14:00687 NewRunnableMethod(g_browser_process->resource_dispatcher_host(),
688 &ResourceDispatcherHost::PauseRequest,
689 download->render_process_id(),
690 download->request_id(),
691 pause));
[email protected]9ccbb372008-10-10 18:50:32692}
693
[email protected]6a7fb042010-02-01 16:30:47694void DownloadManager::UpdateAppIcon() {
695 int64 total_bytes = 0;
696 int64 received_bytes = 0;
697 int download_count = 0;
698 bool progress_known = true;
699
700 for (DownloadMap::iterator i = in_progress_.begin();
701 i != in_progress_.end();
702 ++i) {
703 ++download_count;
704 const DownloadItem* item = i->second;
705 if (item->total_bytes() > 0) {
706 total_bytes += item->total_bytes();
707 received_bytes += item->received_bytes();
708 } else {
709 // This download didn't specify a Content-Length, so the combined progress
710 // bar neeeds to be indeterminate.
711 progress_known = false;
712 }
713 }
714
715 float progress = 0;
716 if (progress_known && download_count)
[email protected]7ba53e12010-08-05 17:14:00717 progress = static_cast<float>(received_bytes) / total_bytes;
[email protected]6a7fb042010-02-01 16:30:47718
719 download_util::UpdateAppIconDownloadProgress(download_count,
720 progress_known,
721 progress);
722}
723
[email protected]9ccbb372008-10-10 18:50:32724void DownloadManager::RenameDownload(DownloadItem* download,
[email protected]7ae7c2cb2009-01-06 23:31:41725 const FilePath& new_path) {
[email protected]9ccbb372008-10-10 18:50:32726 download->Rename(new_path);
[email protected]82f37b02010-07-29 22:04:57727 download_history_->UpdateDownloadPath(download, new_path);
[email protected]9ccbb372008-10-10 18:50:32728}
729
initial.commit09911bf2008-07-26 23:55:29730void DownloadManager::RemoveDownload(int64 download_handle) {
731 DownloadMap::iterator it = downloads_.find(download_handle);
732 if (it == downloads_.end())
733 return;
734
735 // Make history update.
736 DownloadItem* download = it->second;
[email protected]82f37b02010-07-29 22:04:57737 download_history_->RemoveEntry(download);
initial.commit09911bf2008-07-26 23:55:29738
739 // Remove from our tables and delete.
740 downloads_.erase(it);
[email protected]9ccbb372008-10-10 18:50:32741 it = dangerous_finished_.find(download->id());
742 if (it != dangerous_finished_.end())
743 dangerous_finished_.erase(it);
initial.commit09911bf2008-07-26 23:55:29744
745 // Tell observers to refresh their views.
[email protected]b0ab1d42010-02-24 19:29:28746 NotifyModelChanged();
[email protected]6f712872008-11-07 00:35:36747
748 delete download;
initial.commit09911bf2008-07-26 23:55:29749}
750
[email protected]e93d2822009-01-30 05:59:59751int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin,
752 const base::Time remove_end) {
[email protected]82f37b02010-07-29 22:04:57753 download_history_->RemoveEntriesBetween(remove_begin, remove_end);
initial.commit09911bf2008-07-26 23:55:29754
initial.commit09911bf2008-07-26 23:55:29755 DownloadMap::iterator it = downloads_.begin();
[email protected]78b8fcc92009-03-31 17:36:28756 std::vector<DownloadItem*> pending_deletes;
initial.commit09911bf2008-07-26 23:55:29757 while (it != downloads_.end()) {
758 DownloadItem* download = it->second;
759 DownloadItem::DownloadState state = download->state();
760 if (download->start_time() >= remove_begin &&
761 (remove_end.is_null() || download->start_time() < remove_end) &&
762 (state == DownloadItem::COMPLETE ||
763 state == DownloadItem::CANCELLED)) {
764 // Remove from the map and move to the next in the list.
[email protected]b7f05882009-02-22 01:21:56765 downloads_.erase(it++);
[email protected]a6604d92008-10-30 00:58:58766
767 // Also remove it from any completed dangerous downloads.
768 DownloadMap::iterator dit = dangerous_finished_.find(download->id());
769 if (dit != dangerous_finished_.end())
770 dangerous_finished_.erase(dit);
771
[email protected]78b8fcc92009-03-31 17:36:28772 pending_deletes.push_back(download);
[email protected]b0ab1d42010-02-24 19:29:28773 // Observer interface.
initial.commit09911bf2008-07-26 23:55:29774
initial.commit09911bf2008-07-26 23:55:29775 continue;
776 }
777
778 ++it;
779 }
780
781 // Tell observers to refresh their views.
[email protected]78b8fcc92009-03-31 17:36:28782 int num_deleted = static_cast<int>(pending_deletes.size());
initial.commit09911bf2008-07-26 23:55:29783 if (num_deleted > 0)
[email protected]b0ab1d42010-02-24 19:29:28784 NotifyModelChanged();
initial.commit09911bf2008-07-26 23:55:29785
[email protected]78b8fcc92009-03-31 17:36:28786 // Delete the download items after updating the observers.
787 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
788 pending_deletes.clear();
789
initial.commit09911bf2008-07-26 23:55:29790 return num_deleted;
791}
792
[email protected]e93d2822009-01-30 05:59:59793int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
794 return RemoveDownloadsBetween(remove_begin, base::Time());
initial.commit09911bf2008-07-26 23:55:29795}
796
[email protected]d41355e6f2009-04-07 21:21:12797int DownloadManager::RemoveAllDownloads() {
[email protected]024f2f02010-04-30 22:51:46798 if (this != profile_->GetOriginalProfile()->GetDownloadManager()) {
799 // This is an incognito downloader. Clear All should clear main download
800 // manager as well.
801 profile_->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads();
802 }
[email protected]d41355e6f2009-04-07 21:21:12803 // The null times make the date range unbounded.
804 return RemoveDownloadsBetween(base::Time(), base::Time());
805}
806
initial.commit09911bf2008-07-26 23:55:29807// Initiate a download of a specific URL. We send the request to the
808// ResourceDispatcherHost, and let it send us responses like a regular
809// download.
810void DownloadManager::DownloadUrl(const GURL& url,
811 const GURL& referrer,
[email protected]c9825a42009-05-01 22:51:50812 const std::string& referrer_charset,
[email protected]57c6a652009-05-04 07:58:34813 TabContents* tab_contents) {
[email protected]ae8945192010-07-20 16:56:26814 DownloadUrlToFile(url, referrer, referrer_charset, DownloadSaveInfo(),
815 tab_contents);
[email protected]6aa4a1c02010-01-15 18:49:58816}
817
818void DownloadManager::DownloadUrlToFile(const GURL& url,
819 const GURL& referrer,
820 const std::string& referrer_charset,
[email protected]8af9d032010-02-10 00:00:32821 const DownloadSaveInfo& save_info,
[email protected]6aa4a1c02010-01-15 18:49:58822 TabContents* tab_contents) {
[email protected]57c6a652009-05-04 07:58:34823 DCHECK(tab_contents);
[email protected]ae8945192010-07-20 16:56:26824 ChromeThread::PostTask(ChromeThread::IO, FROM_HERE,
825 NewRunnableFunction(&download_util::DownloadUrl,
826 url,
827 referrer,
828 referrer_charset,
829 save_info,
830 g_browser_process->resource_dispatcher_host(),
831 tab_contents->GetRenderProcessHost()->id(),
832 tab_contents->render_view_host()->routing_id(),
833 request_context_getter_));
initial.commit09911bf2008-07-26 23:55:29834}
835
initial.commit09911bf2008-07-26 23:55:29836void DownloadManager::AddObserver(Observer* observer) {
837 observers_.AddObserver(observer);
838 observer->ModelChanged();
839}
840
841void DownloadManager::RemoveObserver(Observer* observer) {
842 observers_.RemoveObserver(observer);
843}
844
845// Post Windows Shell operations to the Download thread, to avoid blocking the
846// user interface.
847void DownloadManager::ShowDownloadInShell(const DownloadItem* download) {
848 DCHECK(file_manager_);
[email protected]d83d03aa2009-11-02 21:44:37849 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
[email protected]8b6ff012009-08-18 22:29:58850#if defined(OS_MACOSX)
851 // Mac needs to run this operation on the UI thread.
852 platform_util::ShowItemInFolder(download->full_path());
853#else
[email protected]d83d03aa2009-11-02 21:44:37854 ChromeThread::PostTask(
855 ChromeThread::FILE, FROM_HERE,
856 NewRunnableMethod(
857 file_manager_, &DownloadFileManager::OnShowDownloadInShell,
858 FilePath(download->full_path())));
[email protected]8b6ff012009-08-18 22:29:58859#endif
initial.commit09911bf2008-07-26 23:55:29860}
861
[email protected]ee8f30e62010-08-26 17:17:51862void DownloadManager::OpenDownload(DownloadItem* download,
[email protected]8f783752009-04-01 23:33:45863 gfx::NativeView parent_window) {
[email protected]0e34d7892009-06-05 19:17:40864 // Open Chrome extensions with ExtensionsService. For everything else do shell
[email protected]8f783752009-04-01 23:33:45865 // execute.
[email protected]a60c8ae2009-12-25 06:50:57866 if (download->is_extension_install()) {
[email protected]ee8f30e62010-08-26 17:17:51867 download->Opened();
[email protected]7ba53e12010-08-05 17:14:00868 download_util::OpenChromeExtension(profile_, this, *download);
[email protected]8f783752009-04-01 23:33:45869 } else {
870 OpenDownloadInShell(download, parent_window);
871 }
872}
873
[email protected]ee8f30e62010-08-26 17:17:51874void DownloadManager::OpenDownloadInShell(DownloadItem* download,
[email protected]e93d2822009-01-30 05:59:59875 gfx::NativeView parent_window) {
initial.commit09911bf2008-07-26 23:55:29876 DCHECK(file_manager_);
[email protected]d83d03aa2009-11-02 21:44:37877 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
[email protected]ee8f30e62010-08-26 17:17:51878 download->Opened();
[email protected]8b6ff012009-08-18 22:29:58879#if defined(OS_MACOSX)
880 // Mac OS X requires opening downloads on the UI thread.
881 platform_util::OpenItem(download->full_path());
882#else
[email protected]d83d03aa2009-11-02 21:44:37883 ChromeThread::PostTask(
884 ChromeThread::FILE, FROM_HERE,
885 NewRunnableMethod(
886 file_manager_, &DownloadFileManager::OnOpenDownloadInShell,
887 download->full_path(), download->url(), parent_window));
[email protected]8b6ff012009-08-18 22:29:58888#endif
initial.commit09911bf2008-07-26 23:55:29889}
890
[email protected]eccb9d12009-10-28 05:40:09891bool DownloadManager::ShouldOpenFileBasedOnExtension(
892 const FilePath& path) const {
[email protected]eccb9d12009-10-28 05:40:09893 FilePath::StringType extension = path.Extension();
894 if (extension.empty())
895 return false;
[email protected]7ba53e12010-08-05 17:14:00896 if (download_util::IsExecutableExtension(extension))
[email protected]eccb9d12009-10-28 05:40:09897 return false;
[email protected]92e11c82010-01-13 06:39:56898 if (Extension::IsExtension(path))
899 return false;
[email protected]eccb9d12009-10-28 05:40:09900 DCHECK(extension[0] == FilePath::kExtensionSeparator);
901 extension.erase(0, 1);
[email protected]e5dc4222010-08-30 22:16:32902 return download_prefs_->IsAutoOpenEnabledForExtension(extension);
initial.commit09911bf2008-07-26 23:55:29903}
904
[email protected]561abe62009-04-06 18:08:34905void DownloadManager::FileSelected(const FilePath& path,
[email protected]23b357b2009-03-30 20:02:36906 int index, void* params) {
initial.commit09911bf2008-07-26 23:55:29907 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
[email protected]e6875c12010-07-18 11:14:13908 if (info->prompt_user_for_save_location)
[email protected]7ae7c2cb2009-01-06 23:31:41909 last_download_path_ = path.DirName();
initial.commit09911bf2008-07-26 23:55:29910 ContinueStartDownload(info, path);
911}
912
913void DownloadManager::FileSelectionCanceled(void* params) {
914 // The user didn't pick a place to save the file, so need to cancel the
915 // download that's already in progress to the temporary location.
916 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
[email protected]d7d1c5c2009-08-05 23:52:50917 DownloadCancelledInternal(info->download_id,
[email protected]76543b92009-08-31 17:27:45918 info->child_id,
[email protected]d7d1c5c2009-08-05 23:52:50919 info->request_id);
initial.commit09911bf2008-07-26 23:55:29920}
921
[email protected]9ccbb372008-10-10 18:50:32922void DownloadManager::DangerousDownloadValidated(DownloadItem* download) {
923 DCHECK_EQ(DownloadItem::DANGEROUS, download->safety_state());
924 download->set_safety_state(DownloadItem::DANGEROUS_BUT_VALIDATED);
925 download->UpdateObservers();
926
927 // If the download is not complete, nothing to do. The required
928 // post-processing will be performed when it does complete.
929 if (download->state() != DownloadItem::COMPLETE)
930 return;
931
[email protected]d83d03aa2009-11-02 21:44:37932 ChromeThread::PostTask(
933 ChromeThread::FILE, FROM_HERE,
934 NewRunnableMethod(
935 this, &DownloadManager::ProceedWithFinishedDangerousDownload,
936 download->db_handle(), download->full_path(),
937 download->original_name()));
[email protected]9ccbb372008-10-10 18:50:32938}
939
initial.commit09911bf2008-07-26 23:55:29940// Operations posted to us from the history service ----------------------------
941
942// The history service has retrieved all download entries. 'entries' contains
943// 'DownloadCreateInfo's in sorted order (by ascending start_time).
944void DownloadManager::OnQueryDownloadEntriesComplete(
945 std::vector<DownloadCreateInfo>* entries) {
946 for (size_t i = 0; i < entries->size(); ++i) {
[email protected]aa033af2010-07-27 18:16:39947 DownloadItem* download = new DownloadItem(this, entries->at(i));
948 DCHECK(!ContainsKey(downloads_, download->db_handle()));
initial.commit09911bf2008-07-26 23:55:29949 downloads_[download->db_handle()] = download;
initial.commit09911bf2008-07-26 23:55:29950 }
[email protected]b0ab1d42010-02-24 19:29:28951 NotifyModelChanged();
initial.commit09911bf2008-07-26 23:55:29952}
953
initial.commit09911bf2008-07-26 23:55:29954// Once the new DownloadItem's creation info has been committed to the history
955// service, we associate the DownloadItem with the db handle, update our
956// 'downloads_' map and inform observers.
957void DownloadManager::OnCreateDownloadEntryComplete(DownloadCreateInfo info,
958 int64 db_handle) {
959 DownloadMap::iterator it = in_progress_.find(info.download_id);
960 DCHECK(it != in_progress_.end());
961
962 DownloadItem* download = it->second;
[email protected]d2a8fb72010-01-21 05:31:42963
964 // It's not immediately obvious, but HistoryBackend::CreateDownload() can
965 // call this function with an invalid |db_handle|. For instance, this can
966 // happen when the history database is offline. We cannot have multiple
967 // DownloadItems with the same invalid db_handle, so we need to assign a
968 // unique |db_handle| here.
[email protected]82f37b02010-07-29 22:04:57969 if (db_handle == DownloadHistory::kUninitializedHandle)
970 db_handle = download_history_->GetNextFakeDbHandle();
[email protected]d2a8fb72010-01-21 05:31:42971
[email protected]82f37b02010-07-29 22:04:57972 DCHECK(download->db_handle() == DownloadHistory::kUninitializedHandle);
initial.commit09911bf2008-07-26 23:55:29973 download->set_db_handle(db_handle);
974
975 // Insert into our full map.
976 DCHECK(downloads_.find(download->db_handle()) == downloads_.end());
977 downloads_[download->db_handle()] = download;
978
[email protected]5e595482009-05-06 20:16:53979 // Show in the appropropriate browser UI.
980 ShowDownloadInBrowser(info, download);
initial.commit09911bf2008-07-26 23:55:29981
982 // Inform interested objects about the new download.
[email protected]b0ab1d42010-02-24 19:29:28983 NotifyModelChanged();
initial.commit09911bf2008-07-26 23:55:29984
985 // If this download has been completed before we've received the db handle,
986 // post one final message to the history service so that it can be properly
987 // in sync with the DownloadItem's completion status, and also inform any
988 // observers so that they get more than just the start notification.
989 if (download->state() != DownloadItem::IN_PROGRESS) {
990 in_progress_.erase(it);
[email protected]82f37b02010-07-29 22:04:57991 download_history_->UpdateEntry(download);
initial.commit09911bf2008-07-26 23:55:29992 download->UpdateObservers();
993 }
[email protected]6a7fb042010-02-01 16:30:47994
995 UpdateAppIcon();
initial.commit09911bf2008-07-26 23:55:29996}
997
[email protected]5e595482009-05-06 20:16:53998void DownloadManager::ShowDownloadInBrowser(const DownloadCreateInfo& info,
999 DownloadItem* download) {
[email protected]8ddbd66a2010-05-21 16:38:341000 // The 'contents' may no longer exist if the user closed the tab before we
1001 // get this start completion event. If it does, tell the origin TabContents
1002 // to display its download shelf.
[email protected]76543b92009-08-31 17:27:451003 TabContents* contents = tab_util::GetTabContentsByID(info.child_id,
1004 info.render_view_id);
[email protected]5e595482009-05-06 20:16:531005
1006 // If the contents no longer exists, we start the download in the last active
1007 // browser. This is not ideal but better than fully hiding the download from
1008 // the user.
1009 if (!contents) {
1010 Browser* last_active = BrowserList::GetLastActive();
1011 if (last_active)
1012 contents = last_active->GetSelectedTabContents();
1013 }
1014
1015 if (contents)
1016 contents->OnStartDownload(download);
1017}
1018
[email protected]6cade212008-12-03 00:32:221019// Clears the last download path, used to initialize "save as" dialogs.
[email protected]905a08d2008-11-19 07:24:121020void DownloadManager::ClearLastDownloadPath() {
[email protected]7ae7c2cb2009-01-06 23:31:411021 last_download_path_ = FilePath();
[email protected]eea46622009-07-15 20:49:381022}
[email protected]b0ab1d42010-02-24 19:29:281023
1024void DownloadManager::NotifyModelChanged() {
1025 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
1026}
1027
[email protected]2e030682010-07-23 19:45:361028DownloadItem* DownloadManager::GetDownloadItem(int id) {
1029 for (DownloadMap::iterator it = downloads_.begin();
1030 it != downloads_.end(); ++it) {
1031 DownloadItem* item = it->second;
1032 if (item->id() == id)
1033 return item;
1034 }
1035 return NULL;
1036}
1037
[email protected]b0ab1d42010-02-24 19:29:281038// DownloadManager::OtherDownloadManagerObserver implementation ----------------
1039
1040DownloadManager::OtherDownloadManagerObserver::OtherDownloadManagerObserver(
1041 DownloadManager* observing_download_manager)
1042 : observing_download_manager_(observing_download_manager),
1043 observed_download_manager_(NULL) {
1044 if (observing_download_manager->profile_->GetOriginalProfile() ==
1045 observing_download_manager->profile_) {
1046 return;
1047 }
1048
1049 observed_download_manager_ = observing_download_manager_->
1050 profile_->GetOriginalProfile()->GetDownloadManager();
1051 observed_download_manager_->AddObserver(this);
1052}
1053
1054DownloadManager::OtherDownloadManagerObserver::~OtherDownloadManagerObserver() {
1055 if (observed_download_manager_)
1056 observed_download_manager_->RemoveObserver(this);
1057}
1058
1059void DownloadManager::OtherDownloadManagerObserver::ModelChanged() {
1060 observing_download_manager_->NotifyModelChanged();
1061}
1062
[email protected]b0ab1d42010-02-24 19:29:281063void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() {
1064 observed_download_manager_ = NULL;
1065}