blob: 4fa4de3021bd1b48380b0d4f8aba5c53f621157b [file] [log] [blame]
[email protected]d41355e6f2009-04-07 21:21:121// Copyright (c) 2006-2009 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]5a947d5a2009-05-01 23:15:318#include "base/command_line.h"
initial.commit09911bf2008-07-26 23:55:299#include "base/file_util.h"
10#include "base/logging.h"
11#include "base/message_loop.h"
12#include "base/path_service.h"
[email protected]1b5044d2009-02-24 00:04:1413#include "base/rand_util.h"
[email protected]807204142009-05-05 03:31:4414#include "base/stl_util-inl.h"
initial.commit09911bf2008-07-26 23:55:2915#include "base/string_util.h"
[email protected]1b5044d2009-02-24 00:04:1416#include "base/sys_string_conversions.h"
initial.commit09911bf2008-07-26 23:55:2917#include "base/task.h"
18#include "base/thread.h"
19#include "base/timer.h"
initial.commit09911bf2008-07-26 23:55:2920#include "chrome/browser/browser_list.h"
21#include "chrome/browser/browser_process.h"
[email protected]cdaa8652008-09-13 02:48:5922#include "chrome/browser/download/download_file.h"
[email protected]8f783752009-04-01 23:33:4523#include "chrome/browser/extensions/extensions_service.h"
initial.commit09911bf2008-07-26 23:55:2924#include "chrome/browser/profile.h"
[email protected]8c8657d62009-01-16 18:31:2625#include "chrome/browser/renderer_host/render_process_host.h"
[email protected]6524b5f92009-01-22 17:48:2526#include "chrome/browser/renderer_host/render_view_host.h"
[email protected]e3c404b2008-12-23 01:07:3227#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
[email protected]f3ec7742009-01-15 00:59:1628#include "chrome/browser/tab_contents/tab_util.h"
[email protected]57c6a652009-05-04 07:58:3429#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]8c756ac2009-01-30 23:36:4130#include "chrome/common/chrome_constants.h"
initial.commit09911bf2008-07-26 23:55:2931#include "chrome/common/chrome_paths.h"
[email protected]5a947d5a2009-05-01 23:15:3132#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5833#include "chrome/common/extensions/extension.h"
[email protected]076700e62009-04-01 18:41:2334#include "chrome/common/platform_util.h"
initial.commit09911bf2008-07-26 23:55:2935#include "chrome/common/pref_names.h"
36#include "chrome/common/pref_service.h"
[email protected]46072d42008-07-28 14:49:3537#include "googleurl/src/gurl.h"
[email protected]d81706b82009-04-03 20:28:4438#include "grit/chromium_strings.h"
[email protected]34ac8f32009-02-22 23:03:2739#include "grit/generated_resources.h"
initial.commit09911bf2008-07-26 23:55:2940#include "net/base/mime_util.h"
41#include "net/base/net_util.h"
42#include "net/url_request/url_request_context.h"
43
[email protected]b7f05882009-02-22 01:21:5644#if defined(OS_WIN)
[email protected]4a0765a2009-05-08 23:12:2545#include "app/win_util.h"
[email protected]b7f05882009-02-22 01:21:5646// TODO(port): some of these need porting.
47#include "base/registry.h"
48#include "base/win_util.h"
49#include "chrome/browser/download/download_util.h"
[email protected]b7f05882009-02-22 01:21:5650#endif
51
[email protected]0f44d3e2009-03-12 23:36:3052#if defined(OS_LINUX)
53#include <gtk/gtk.h>
54#endif
55
initial.commit09911bf2008-07-26 23:55:2956// Periodically update our observers.
57class DownloadItemUpdateTask : public Task {
58 public:
59 explicit DownloadItemUpdateTask(DownloadItem* item) : item_(item) {}
60 void Run() { if (item_) item_->UpdateObservers(); }
61
62 private:
63 DownloadItem* item_;
64};
65
66// Update frequency (milliseconds).
67static const int kUpdateTimeMs = 1000;
68
69// Our download table ID starts at 1, so we use 0 to represent a download that
70// has started, but has not yet had its data persisted in the table. We use fake
[email protected]6cade212008-12-03 00:32:2271// database handles in incognito mode starting at -1 and progressively getting
72// more negative.
initial.commit09911bf2008-07-26 23:55:2973static const int kUninitializedHandle = 0;
74
[email protected]7a256ea2008-10-17 17:34:1675// Appends the passed the number between parenthesis the path before the
76// extension.
[email protected]7ae7c2cb2009-01-06 23:31:4177static void AppendNumberToPath(FilePath* path, int number) {
78 file_util::InsertBeforeExtension(path,
79 StringPrintf(FILE_PATH_LITERAL(" (%d)"), number));
[email protected]7a256ea2008-10-17 17:34:1680}
81
82// Attempts to find a number that can be appended to that path to make it
83// unique. If |path| does not exist, 0 is returned. If it fails to find such
84// a number, -1 is returned.
[email protected]7ae7c2cb2009-01-06 23:31:4185static int GetUniquePathNumber(const FilePath& path) {
initial.commit09911bf2008-07-26 23:55:2986 const int kMaxAttempts = 100;
87
[email protected]7a256ea2008-10-17 17:34:1688 if (!file_util::PathExists(path))
89 return 0;
initial.commit09911bf2008-07-26 23:55:2990
[email protected]7ae7c2cb2009-01-06 23:31:4191 FilePath new_path;
initial.commit09911bf2008-07-26 23:55:2992 for (int count = 1; count <= kMaxAttempts; ++count) {
[email protected]7ae7c2cb2009-01-06 23:31:4193 new_path = FilePath(path);
[email protected]7a256ea2008-10-17 17:34:1694 AppendNumberToPath(&new_path, count);
initial.commit09911bf2008-07-26 23:55:2995
[email protected]7a256ea2008-10-17 17:34:1696 if (!file_util::PathExists(new_path))
97 return count;
initial.commit09911bf2008-07-26 23:55:2998 }
99
[email protected]7a256ea2008-10-17 17:34:16100 return -1;
initial.commit09911bf2008-07-26 23:55:29101}
102
[email protected]7ae7c2cb2009-01-06 23:31:41103static bool DownloadPathIsDangerous(const FilePath& download_path) {
104 FilePath desktop_dir;
[email protected]f052118e2008-09-05 02:25:32105 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_dir)) {
106 NOTREACHED();
107 return false;
108 }
109 return (download_path == desktop_dir);
110}
111
initial.commit09911bf2008-07-26 23:55:29112// DownloadItem implementation -------------------------------------------------
113
114// Constructor for reading from the history service.
115DownloadItem::DownloadItem(const DownloadCreateInfo& info)
116 : id_(-1),
117 full_path_(info.path),
118 url_(info.url),
119 total_bytes_(info.total_bytes),
120 received_bytes_(info.received_bytes),
[email protected]b7f05882009-02-22 01:21:56121 start_tick_(base::TimeTicks()),
initial.commit09911bf2008-07-26 23:55:29122 state_(static_cast<DownloadState>(info.state)),
123 start_time_(info.start_time),
124 db_handle_(info.db_handle),
initial.commit09911bf2008-07-26 23:55:29125 manager_(NULL),
126 is_paused_(false),
127 open_when_complete_(false),
[email protected]b7f05882009-02-22 01:21:56128 safety_state_(SAFE),
129 original_name_(info.original_name),
initial.commit09911bf2008-07-26 23:55:29130 render_process_id_(-1),
131 request_id_(-1) {
132 if (state_ == IN_PROGRESS)
133 state_ = CANCELLED;
134 Init(false /* don't start progress timer */);
135}
136
137// Constructor for DownloadItem created via user action in the main thread.
138DownloadItem::DownloadItem(int32 download_id,
[email protected]7ae7c2cb2009-01-06 23:31:41139 const FilePath& path,
[email protected]7a256ea2008-10-17 17:34:16140 int path_uniquifier,
[email protected]f6b48532009-02-12 01:56:32141 const GURL& url,
[email protected]7ae7c2cb2009-01-06 23:31:41142 const FilePath& original_name,
[email protected]e93d2822009-01-30 05:59:59143 const base::Time start_time,
initial.commit09911bf2008-07-26 23:55:29144 int64 download_size,
145 int render_process_id,
[email protected]9ccbb372008-10-10 18:50:32146 int request_id,
147 bool is_dangerous)
initial.commit09911bf2008-07-26 23:55:29148 : id_(download_id),
149 full_path_(path),
[email protected]7a256ea2008-10-17 17:34:16150 path_uniquifier_(path_uniquifier),
initial.commit09911bf2008-07-26 23:55:29151 url_(url),
152 total_bytes_(download_size),
153 received_bytes_(0),
[email protected]b7f05882009-02-22 01:21:56154 start_tick_(base::TimeTicks::Now()),
initial.commit09911bf2008-07-26 23:55:29155 state_(IN_PROGRESS),
156 start_time_(start_time),
157 db_handle_(kUninitializedHandle),
initial.commit09911bf2008-07-26 23:55:29158 manager_(NULL),
159 is_paused_(false),
160 open_when_complete_(false),
[email protected]b7f05882009-02-22 01:21:56161 safety_state_(is_dangerous ? DANGEROUS : SAFE),
162 original_name_(original_name),
initial.commit09911bf2008-07-26 23:55:29163 render_process_id_(render_process_id),
164 request_id_(request_id) {
165 Init(true /* start progress timer */);
166}
167
168void DownloadItem::Init(bool start_timer) {
[email protected]7ae7c2cb2009-01-06 23:31:41169 file_name_ = full_path_.BaseName();
initial.commit09911bf2008-07-26 23:55:29170 if (start_timer)
171 StartProgressTimer();
172}
173
174DownloadItem::~DownloadItem() {
initial.commit09911bf2008-07-26 23:55:29175 state_ = REMOVING;
176 UpdateObservers();
177}
178
179void DownloadItem::AddObserver(Observer* observer) {
180 observers_.AddObserver(observer);
181}
182
183void DownloadItem::RemoveObserver(Observer* observer) {
184 observers_.RemoveObserver(observer);
185}
186
187void DownloadItem::UpdateObservers() {
188 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
189}
190
[email protected]45e3c122009-04-07 19:58:03191void DownloadItem::NotifyObserversDownloadOpened() {
192 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this));
193}
194
initial.commit09911bf2008-07-26 23:55:29195// If we've received more data than we were expecting (bad server info?), revert
196// to 'unknown size mode'.
197void DownloadItem::UpdateSize(int64 bytes_so_far) {
198 received_bytes_ = bytes_so_far;
199 if (received_bytes_ > total_bytes_)
200 total_bytes_ = 0;
201}
202
203// Updates from the download thread may have been posted while this download
204// was being cancelled in the UI thread, so we'll accept them unless we're
205// complete.
206void DownloadItem::Update(int64 bytes_so_far) {
207 if (state_ == COMPLETE) {
208 NOTREACHED();
209 return;
210 }
211 UpdateSize(bytes_so_far);
212 UpdateObservers();
213}
214
[email protected]6cade212008-12-03 00:32:22215// Triggered by a user action.
initial.commit09911bf2008-07-26 23:55:29216void DownloadItem::Cancel(bool update_history) {
217 if (state_ != IN_PROGRESS) {
218 // Small downloads might be complete before this method has a chance to run.
219 return;
220 }
221 state_ = CANCELLED;
222 UpdateObservers();
223 StopProgressTimer();
224 if (update_history)
225 manager_->DownloadCancelled(id_);
226}
227
228void DownloadItem::Finished(int64 size) {
229 state_ = COMPLETE;
230 UpdateSize(size);
[email protected]22fbe5a2008-10-29 22:20:40231 UpdateObservers();
initial.commit09911bf2008-07-26 23:55:29232 StopProgressTimer();
233}
234
[email protected]9ccbb372008-10-10 18:50:32235void DownloadItem::Remove(bool delete_on_disk) {
initial.commit09911bf2008-07-26 23:55:29236 Cancel(true);
237 state_ = REMOVING;
[email protected]9ccbb372008-10-10 18:50:32238 if (delete_on_disk)
239 manager_->DeleteDownload(full_path_);
initial.commit09911bf2008-07-26 23:55:29240 manager_->RemoveDownload(db_handle_);
[email protected]6cade212008-12-03 00:32:22241 // We have now been deleted.
initial.commit09911bf2008-07-26 23:55:29242}
243
244void DownloadItem::StartProgressTimer() {
[email protected]e93d2822009-01-30 05:59:59245 update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateTimeMs), this,
[email protected]2d316662008-09-03 18:18:14246 &DownloadItem::UpdateObservers);
initial.commit09911bf2008-07-26 23:55:29247}
248
249void DownloadItem::StopProgressTimer() {
[email protected]2d316662008-09-03 18:18:14250 update_timer_.Stop();
initial.commit09911bf2008-07-26 23:55:29251}
252
[email protected]e93d2822009-01-30 05:59:59253bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const {
initial.commit09911bf2008-07-26 23:55:29254 if (total_bytes_ <= 0)
255 return false; // We never received the content_length for this download.
256
257 int64 speed = CurrentSpeed();
258 if (speed == 0)
259 return false;
260
261 *remaining =
[email protected]e93d2822009-01-30 05:59:59262 base::TimeDelta::FromSeconds((total_bytes_ - received_bytes_) / speed);
initial.commit09911bf2008-07-26 23:55:29263 return true;
264}
265
266int64 DownloadItem::CurrentSpeed() const {
[email protected]b7f05882009-02-22 01:21:56267 base::TimeDelta diff = base::TimeTicks::Now() - start_tick_;
268 int64 diff_ms = diff.InMilliseconds();
269 return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms;
initial.commit09911bf2008-07-26 23:55:29270}
271
272int DownloadItem::PercentComplete() const {
273 int percent = -1;
274 if (total_bytes_ > 0)
275 percent = static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
276 return percent;
277}
278
[email protected]7ae7c2cb2009-01-06 23:31:41279void DownloadItem::Rename(const FilePath& full_path) {
initial.commit09911bf2008-07-26 23:55:29280 DCHECK(!full_path.empty());
281 full_path_ = full_path;
[email protected]7ae7c2cb2009-01-06 23:31:41282 file_name_ = full_path_.BaseName();
initial.commit09911bf2008-07-26 23:55:29283}
284
285void DownloadItem::TogglePause() {
286 DCHECK(state_ == IN_PROGRESS);
287 manager_->PauseDownload(id_, !is_paused_);
288 is_paused_ = !is_paused_;
289 UpdateObservers();
290}
291
[email protected]7ae7c2cb2009-01-06 23:31:41292FilePath DownloadItem::GetFileName() const {
[email protected]9ccbb372008-10-10 18:50:32293 if (safety_state_ == DownloadItem::SAFE)
294 return file_name_;
[email protected]7a256ea2008-10-17 17:34:16295 if (path_uniquifier_ > 0) {
[email protected]7ae7c2cb2009-01-06 23:31:41296 FilePath name(original_name_);
[email protected]7a256ea2008-10-17 17:34:16297 AppendNumberToPath(&name, path_uniquifier_);
298 return name;
299 }
[email protected]9ccbb372008-10-10 18:50:32300 return original_name_;
301}
302
initial.commit09911bf2008-07-26 23:55:29303// DownloadManager implementation ----------------------------------------------
304
305// static
306void DownloadManager::RegisterUserPrefs(PrefService* prefs) {
307 prefs->RegisterBooleanPref(prefs::kPromptForDownload, false);
308 prefs->RegisterStringPref(prefs::kDownloadExtensionsToOpen, L"");
[email protected]f052118e2008-09-05 02:25:32309 prefs->RegisterBooleanPref(prefs::kDownloadDirUpgraded, false);
310
311 // The default download path is userprofile\download.
[email protected]7ae7c2cb2009-01-06 23:31:41312 FilePath default_download_path;
[email protected]cbc43fc2008-10-28 00:44:12313 if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS,
314 &default_download_path)) {
[email protected]f052118e2008-09-05 02:25:32315 NOTREACHED();
316 }
[email protected]b9636002009-03-04 00:05:25317 prefs->RegisterFilePathPref(prefs::kDownloadDefaultDirectory,
318 default_download_path);
[email protected]f052118e2008-09-05 02:25:32319
320 // If the download path is dangerous we forcefully reset it. But if we do
321 // so we set a flag to make sure we only do it once, to avoid fighting
322 // the user if he really wants it on an unsafe place such as the desktop.
323
324 if (!prefs->GetBoolean(prefs::kDownloadDirUpgraded)) {
[email protected]7ae7c2cb2009-01-06 23:31:41325 FilePath current_download_dir = FilePath::FromWStringHack(
326 prefs->GetString(prefs::kDownloadDefaultDirectory));
[email protected]f052118e2008-09-05 02:25:32327 if (DownloadPathIsDangerous(current_download_dir)) {
328 prefs->SetString(prefs::kDownloadDefaultDirectory,
[email protected]7ae7c2cb2009-01-06 23:31:41329 default_download_path.ToWStringHack());
[email protected]f052118e2008-09-05 02:25:32330 }
331 prefs->SetBoolean(prefs::kDownloadDirUpgraded, true);
332 }
initial.commit09911bf2008-07-26 23:55:29333}
334
335DownloadManager::DownloadManager()
336 : shutdown_needed_(false),
337 profile_(NULL),
338 file_manager_(NULL),
339 ui_loop_(MessageLoop::current()),
340 file_loop_(NULL) {
341}
342
343DownloadManager::~DownloadManager() {
344 if (shutdown_needed_)
345 Shutdown();
346}
347
348void DownloadManager::Shutdown() {
349 DCHECK(shutdown_needed_) << "Shutdown called when not needed.";
350
351 // Stop receiving download updates
352 file_manager_->RemoveDownloadManager(this);
353
354 // Stop making history service requests
355 cancelable_consumer_.CancelAllRequests();
356
357 // 'in_progress_' may contain DownloadItems that have not finished the start
358 // complete (from the history service) and thus aren't in downloads_.
359 DownloadMap::iterator it = in_progress_.begin();
[email protected]9ccbb372008-10-10 18:50:32360 std::set<DownloadItem*> to_remove;
initial.commit09911bf2008-07-26 23:55:29361 for (; it != in_progress_.end(); ++it) {
362 DownloadItem* download = it->second;
[email protected]9ccbb372008-10-10 18:50:32363 if (download->safety_state() == DownloadItem::DANGEROUS) {
364 // Forget about any download that the user did not approve.
365 // Note that we cannot call download->Remove() this would invalidate our
366 // iterator.
367 to_remove.insert(download);
368 continue;
initial.commit09911bf2008-07-26 23:55:29369 }
[email protected]9ccbb372008-10-10 18:50:32370 DCHECK_EQ(DownloadItem::IN_PROGRESS, download->state());
371 download->Cancel(false);
372 UpdateHistoryForDownload(download);
initial.commit09911bf2008-07-26 23:55:29373 if (download->db_handle() == kUninitializedHandle) {
374 // An invalid handle means that 'download' does not yet exist in
375 // 'downloads_', so we have to delete it here.
376 delete download;
377 }
378 }
379
[email protected]9ccbb372008-10-10 18:50:32380 // 'dangerous_finished_' contains all complete downloads that have not been
381 // approved. They should be removed.
382 it = dangerous_finished_.begin();
383 for (; it != dangerous_finished_.end(); ++it)
384 to_remove.insert(it->second);
385
386 // Remove the dangerous download that are not approved.
387 for (std::set<DownloadItem*>::const_iterator rm_it = to_remove.begin();
388 rm_it != to_remove.end(); ++rm_it) {
389 DownloadItem* download = *rm_it;
[email protected]e10e17c72008-10-15 17:48:32390 int64 handle = download->db_handle();
[email protected]9ccbb372008-10-10 18:50:32391 download->Remove(true);
[email protected]e10e17c72008-10-15 17:48:32392 // Same as above, delete the download if it is not in 'downloads_' (as the
393 // Remove() call above won't have deleted it).
394 if (handle == kUninitializedHandle)
[email protected]9ccbb372008-10-10 18:50:32395 delete download;
396 }
397 to_remove.clear();
398
initial.commit09911bf2008-07-26 23:55:29399 in_progress_.clear();
[email protected]9ccbb372008-10-10 18:50:32400 dangerous_finished_.clear();
initial.commit09911bf2008-07-26 23:55:29401 STLDeleteValues(&downloads_);
402
403 file_manager_ = NULL;
404
405 // Save our file extensions to auto open.
406 SaveAutoOpens();
407
408 // Make sure the save as dialog doesn't notify us back if we're gone before
409 // it returns.
410 if (select_file_dialog_.get())
411 select_file_dialog_->ListenerDestroyed();
412
413 shutdown_needed_ = false;
414}
415
416// Issue a history query for downloads matching 'search_text'. If 'search_text'
417// is empty, return all downloads that we know about.
418void DownloadManager::GetDownloads(Observer* observer,
419 const std::wstring& search_text) {
420 DCHECK(observer);
421
422 // Return a empty list if we've not yet received the set of downloads from the
423 // history system (we'll update all observers once we get that list in
424 // OnQueryDownloadEntriesComplete), or if there are no downloads at all.
425 std::vector<DownloadItem*> download_copy;
426 if (downloads_.empty()) {
427 observer->SetDownloads(download_copy);
428 return;
429 }
430
431 // We already know all the downloads and there is no filter, so just return a
432 // copy to the observer.
433 if (search_text.empty()) {
434 download_copy.reserve(downloads_.size());
435 for (DownloadMap::iterator it = downloads_.begin();
436 it != downloads_.end(); ++it) {
437 download_copy.push_back(it->second);
438 }
439
440 // We retain ownership of the DownloadItems.
441 observer->SetDownloads(download_copy);
442 return;
443 }
444
445 // Issue a request to the history service for a list of downloads matching
446 // our search text.
447 HistoryService* hs =
448 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
449 if (hs) {
450 HistoryService::Handle h =
451 hs->SearchDownloads(search_text,
452 &cancelable_consumer_,
453 NewCallback(this,
454 &DownloadManager::OnSearchComplete));
455 cancelable_consumer_.SetClientData(hs, h, observer);
456 }
457}
458
459// Query the history service for information about all persisted downloads.
460bool DownloadManager::Init(Profile* profile) {
461 DCHECK(profile);
462 DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
463 shutdown_needed_ = true;
464
465 profile_ = profile;
466 request_context_ = profile_->GetRequestContext();
467
468 // 'incognito mode' will have access to past downloads, but we won't store
469 // information about new downloads while in that mode.
470 QueryHistoryForDownloads();
471
472 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
473 if (!rdh) {
474 NOTREACHED();
475 return false;
476 }
477
478 file_manager_ = rdh->download_file_manager();
479 if (!file_manager_) {
480 NOTREACHED();
481 return false;
482 }
483
484 file_loop_ = g_browser_process->file_thread()->message_loop();
485 if (!file_loop_) {
486 NOTREACHED();
487 return false;
488 }
489
490 // Get our user preference state.
491 PrefService* prefs = profile_->GetPrefs();
492 DCHECK(prefs);
493 prompt_for_download_.Init(prefs::kPromptForDownload, prefs, NULL);
494
initial.commit09911bf2008-07-26 23:55:29495 download_path_.Init(prefs::kDownloadDefaultDirectory, prefs, NULL);
496
[email protected]7ae7c2cb2009-01-06 23:31:41497 // This variable is needed to resolve which CreateDirectory we want to point
498 // to. Without it, the NewRunnableFunction cannot resolve the ambiguity.
499 // TODO(estade): when file_util::CreateDirectory(wstring) is removed,
500 // get rid of |CreateDirectoryPtr|.
501 bool (*CreateDirectoryPtr)(const FilePath&) = &file_util::CreateDirectory;
[email protected]bb69e9b32008-08-14 23:08:14502 // Ensure that the download directory specified in the preferences exists.
[email protected]7ae7c2cb2009-01-06 23:31:41503 file_loop_->PostTask(FROM_HERE, NewRunnableFunction(
504 CreateDirectoryPtr, download_path()));
initial.commit09911bf2008-07-26 23:55:29505
[email protected]2b2f8f72009-02-24 22:42:05506#if defined(OS_WIN)
507 // We use this on windows to determine possibly dangerous downloads.
508 download_util::InitializeExeTypes(&exe_types_);
509#endif
510
511 // We store any file extension that should be opened automatically at
512 // download completion in this pref.
initial.commit09911bf2008-07-26 23:55:29513 std::wstring extensions_to_open =
514 prefs->GetString(prefs::kDownloadExtensionsToOpen);
515 std::vector<std::wstring> extensions;
516 SplitString(extensions_to_open, L':', &extensions);
517 for (size_t i = 0; i < extensions.size(); ++i) {
[email protected]b7f05882009-02-22 01:21:56518 if (!extensions[i].empty() && !IsExecutable(
519 FilePath::FromWStringHack(extensions[i]).value()))
520 auto_open_.insert(FilePath::FromWStringHack(extensions[i]).value());
initial.commit09911bf2008-07-26 23:55:29521 }
522
523 return true;
524}
525
526void DownloadManager::QueryHistoryForDownloads() {
527 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
528 if (hs) {
529 hs->QueryDownloads(
530 &cancelable_consumer_,
531 NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete));
532 }
533}
534
535// We have received a message from DownloadFileManager about a new download. We
536// create a download item and store it in our download map, and inform the
537// history system of a new download. Since this method can be called while the
538// history service thread is still reading the persistent state, we do not
539// insert the new DownloadItem into 'downloads_' or inform our observers at this
540// point. OnCreateDatabaseEntryComplete() handles that finalization of the the
541// download creation as a callback from the history thread.
542void DownloadManager::StartDownload(DownloadCreateInfo* info) {
543 DCHECK(MessageLoop::current() == ui_loop_);
544 DCHECK(info);
545
[email protected]7d3851d82008-12-12 03:26:07546 // Freeze the user's preference for showing a Save As dialog. We're going to
547 // bounce around a bunch of threads and we don't want to worry about race
548 // conditions where the user changes this pref out from under us.
549 if (*prompt_for_download_)
550 info->save_as = true;
551
initial.commit09911bf2008-07-26 23:55:29552 // Determine the proper path for a download, by choosing either the default
553 // download directory, or prompting the user.
[email protected]7ae7c2cb2009-01-06 23:31:41554 FilePath generated_name;
initial.commit09911bf2008-07-26 23:55:29555 GenerateFilename(info, &generated_name);
[email protected]7d3851d82008-12-12 03:26:07556 if (info->save_as && !last_download_path_.empty())
initial.commit09911bf2008-07-26 23:55:29557 info->suggested_path = last_download_path_;
558 else
[email protected]7ae7c2cb2009-01-06 23:31:41559 info->suggested_path = download_path();
560 info->suggested_path = info->suggested_path.Append(generated_name);
initial.commit09911bf2008-07-26 23:55:29561
[email protected]7d3851d82008-12-12 03:26:07562 if (!info->save_as) {
563 // Let's check if this download is dangerous, based on its name.
[email protected]7ae7c2cb2009-01-06 23:31:41564 info->is_dangerous = IsDangerous(info->suggested_path.BaseName());
[email protected]e9ebf3fc2008-10-17 22:06:58565 }
566
initial.commit09911bf2008-07-26 23:55:29567 // We need to move over to the download thread because we don't want to stat
568 // the suggested path on the UI thread.
569 file_loop_->PostTask(FROM_HERE,
570 NewRunnableMethod(this,
571 &DownloadManager::CheckIfSuggestedPathExists,
572 info));
573}
574
575void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info) {
576 DCHECK(info);
577
578 // Check writability of the suggested path. If we can't write to it, default
579 // to the user's "My Documents" directory. We'll prompt them in this case.
[email protected]7ae7c2cb2009-01-06 23:31:41580 FilePath dir = info->suggested_path.DirName();
581 FilePath filename = info->suggested_path.BaseName();
[email protected]9ccbb372008-10-10 18:50:32582 if (!file_util::PathIsWritable(dir)) {
initial.commit09911bf2008-07-26 23:55:29583 info->save_as = true;
initial.commit09911bf2008-07-26 23:55:29584 PathService::Get(chrome::DIR_USER_DOCUMENTS, &info->suggested_path);
[email protected]7ae7c2cb2009-01-06 23:31:41585 info->suggested_path = info->suggested_path.Append(filename);
initial.commit09911bf2008-07-26 23:55:29586 }
587
[email protected]7a256ea2008-10-17 17:34:16588 info->path_uniquifier = GetUniquePathNumber(info->suggested_path);
initial.commit09911bf2008-07-26 23:55:29589
[email protected]6cade212008-12-03 00:32:22590 // If the download is deemed dangerous, we'll use a temporary name for it.
[email protected]e9ebf3fc2008-10-17 22:06:58591 if (info->is_dangerous) {
[email protected]7ae7c2cb2009-01-06 23:31:41592 info->original_name = FilePath(info->suggested_path).BaseName();
[email protected]9ccbb372008-10-10 18:50:32593 // Create a temporary file to hold the file until the user approves its
594 // download.
[email protected]7ae7c2cb2009-01-06 23:31:41595 FilePath::StringType file_name;
596 FilePath path;
[email protected]9ccbb372008-10-10 18:50:32597 while (path.empty()) {
[email protected]7ae7c2cb2009-01-06 23:31:41598 SStringPrintf(&file_name, FILE_PATH_LITERAL("unconfirmed %d.download"),
[email protected]9ccbb372008-10-10 18:50:32599 base::RandInt(0, 100000));
[email protected]7ae7c2cb2009-01-06 23:31:41600 path = dir.Append(file_name);
[email protected]7d3851d82008-12-12 03:26:07601 if (file_util::PathExists(path))
[email protected]7ae7c2cb2009-01-06 23:31:41602 path = FilePath();
[email protected]9ccbb372008-10-10 18:50:32603 }
604 info->suggested_path = path;
[email protected]7a256ea2008-10-17 17:34:16605 } else {
606 // We know the final path, build it if necessary.
607 if (info->path_uniquifier > 0) {
608 AppendNumberToPath(&(info->suggested_path), info->path_uniquifier);
609 // Setting path_uniquifier to 0 to make sure we don't try to unique it
610 // later on.
611 info->path_uniquifier = 0;
[email protected]7d3851d82008-12-12 03:26:07612 } else if (info->path_uniquifier == -1) {
613 // We failed to find a unique path. We have to prompt the user.
614 info->save_as = true;
[email protected]7a256ea2008-10-17 17:34:16615 }
[email protected]9ccbb372008-10-10 18:50:32616 }
617
[email protected]7d3851d82008-12-12 03:26:07618 if (!info->save_as) {
619 // Create an empty file at the suggested path so that we don't allocate the
620 // same "non-existant" path to multiple downloads.
621 // See: http://code.google.com/p/chromium/issues/detail?id=3662
[email protected]7ae7c2cb2009-01-06 23:31:41622 file_util::WriteFile(info->suggested_path.ToWStringHack(), "", 0);
[email protected]7d3851d82008-12-12 03:26:07623 }
624
initial.commit09911bf2008-07-26 23:55:29625 // Now we return to the UI thread.
626 ui_loop_->PostTask(FROM_HERE,
627 NewRunnableMethod(this,
628 &DownloadManager::OnPathExistenceAvailable,
629 info));
630}
631
632void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) {
633 DCHECK(MessageLoop::current() == ui_loop_);
634 DCHECK(info);
635
[email protected]7d3851d82008-12-12 03:26:07636 if (info->save_as) {
initial.commit09911bf2008-07-26 23:55:29637 // We must ask the user for the place to put the download.
638 if (!select_file_dialog_.get())
639 select_file_dialog_ = SelectFileDialog::Create(this);
640
[email protected]57c6a652009-05-04 07:58:34641 TabContents* contents = tab_util::GetTabContentsByID(
initial.commit09911bf2008-07-26 23:55:29642 info->render_process_id, info->render_view_id);
[email protected]b949f1112009-04-12 20:03:08643 SelectFileDialog::FileTypeInfo file_type_info;
644 file_type_info.extensions.resize(1);
645 file_type_info.extensions[0].push_back(info->suggested_path.Extension());
[email protected]15bc8052009-04-17 19:57:24646 if (!file_type_info.extensions[0][0].empty())
647 file_type_info.extensions[0][0].erase(0, 1); // drop the .
[email protected]b949f1112009-04-12 20:03:08648 file_type_info.include_all_files = true;
[email protected]076700e62009-04-01 18:41:23649 gfx::NativeWindow owning_window =
650 contents ? platform_util::GetTopLevel(contents->GetNativeView()) : NULL;
initial.commit09911bf2008-07-26 23:55:29651 select_file_dialog_->SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE,
[email protected]561abe62009-04-06 18:08:34652 string16(),
653 info->suggested_path,
[email protected]b949f1112009-04-12 20:03:08654 &file_type_info, 0, FILE_PATH_LITERAL(""),
[email protected]0f44d3e2009-03-12 23:36:30655 owning_window, info);
initial.commit09911bf2008-07-26 23:55:29656 } else {
657 // No prompting for download, just continue with the suggested name.
658 ContinueStartDownload(info, info->suggested_path);
659 }
660}
661
662void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info,
[email protected]7ae7c2cb2009-01-06 23:31:41663 const FilePath& target_path) {
initial.commit09911bf2008-07-26 23:55:29664 scoped_ptr<DownloadCreateInfo> infop(info);
665 info->path = target_path;
666
667 DownloadItem* download = NULL;
668 DownloadMap::iterator it = in_progress_.find(info->download_id);
669 if (it == in_progress_.end()) {
670 download = new DownloadItem(info->download_id,
671 info->path,
[email protected]7a256ea2008-10-17 17:34:16672 info->path_uniquifier,
initial.commit09911bf2008-07-26 23:55:29673 info->url,
[email protected]9ccbb372008-10-10 18:50:32674 info->original_name,
initial.commit09911bf2008-07-26 23:55:29675 info->start_time,
676 info->total_bytes,
677 info->render_process_id,
[email protected]9ccbb372008-10-10 18:50:32678 info->request_id,
679 info->is_dangerous);
initial.commit09911bf2008-07-26 23:55:29680 download->set_manager(this);
681 in_progress_[info->download_id] = download;
682 } else {
683 NOTREACHED(); // Should not exist!
684 return;
685 }
686
[email protected]6b323782009-03-27 18:43:08687 // Called before DownloadFinished in order to avoid a race condition where we
688 // attempt to open a completed download before it has been renamed.
689 file_loop_->PostTask(FROM_HERE,
690 NewRunnableMethod(file_manager_,
691 &DownloadFileManager::OnFinalDownloadName,
692 download->id(),
[email protected]8f783752009-04-01 23:33:45693 target_path,
694 this));
[email protected]6b323782009-03-27 18:43:08695
initial.commit09911bf2008-07-26 23:55:29696 // If the download already completed by the time we reached this point, then
697 // notify observers that it did.
698 PendingFinishedMap::iterator pending_it =
699 pending_finished_downloads_.find(info->download_id);
700 if (pending_it != pending_finished_downloads_.end())
701 DownloadFinished(pending_it->first, pending_it->second);
702
703 download->Rename(target_path);
704
initial.commit09911bf2008-07-26 23:55:29705 if (profile_->IsOffTheRecord()) {
706 // Fake a db handle for incognito mode, since nothing is actually stored in
707 // the database in this mode. We have to make sure that these handles don't
708 // collide with normal db handles, so we use a negative value. Eventually,
709 // they could overlap, but you'd have to do enough downloading that your ISP
710 // would likely stab you in the neck first. YMMV.
711 static int64 fake_db_handle = kUninitializedHandle - 1;
712 OnCreateDownloadEntryComplete(*info, fake_db_handle--);
713 } else {
714 // Update the history system with the new download.
[email protected]6cade212008-12-03 00:32:22715 // FIXME(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
initial.commit09911bf2008-07-26 23:55:29716 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
717 if (hs) {
718 hs->CreateDownload(
719 *info, &cancelable_consumer_,
720 NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
721 }
722 }
723}
724
725// Convenience function for updating the history service for a download.
726void DownloadManager::UpdateHistoryForDownload(DownloadItem* download) {
727 DCHECK(download);
728
729 // Don't store info in the database if the download was initiated while in
730 // incognito mode or if it hasn't been initialized in our database table.
731 if (download->db_handle() <= kUninitializedHandle)
732 return;
733
[email protected]6cade212008-12-03 00:32:22734 // FIXME(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
initial.commit09911bf2008-07-26 23:55:29735 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
736 if (hs) {
737 hs->UpdateDownload(download->received_bytes(),
738 download->state(),
739 download->db_handle());
740 }
741}
742
743void DownloadManager::RemoveDownloadFromHistory(DownloadItem* download) {
744 DCHECK(download);
[email protected]6cade212008-12-03 00:32:22745 // FIXME(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
initial.commit09911bf2008-07-26 23:55:29746 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
747 if (download->db_handle() > kUninitializedHandle && hs)
748 hs->RemoveDownload(download->db_handle());
749}
750
[email protected]e93d2822009-01-30 05:59:59751void DownloadManager::RemoveDownloadsFromHistoryBetween(
752 const base::Time remove_begin,
753 const base::Time remove_end) {
[email protected]6cade212008-12-03 00:32:22754 // FIXME(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
initial.commit09911bf2008-07-26 23:55:29755 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
756 if (hs)
757 hs->RemoveDownloadsBetween(remove_begin, remove_end);
758}
759
760void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
761 DownloadMap::iterator it = in_progress_.find(download_id);
762 if (it != in_progress_.end()) {
763 DownloadItem* download = it->second;
764 download->Update(size);
765 UpdateHistoryForDownload(download);
766 }
767}
768
769void DownloadManager::DownloadFinished(int32 download_id, int64 size) {
770 DownloadMap::iterator it = in_progress_.find(download_id);
[email protected]9ccbb372008-10-10 18:50:32771 if (it == in_progress_.end()) {
initial.commit09911bf2008-07-26 23:55:29772 // The download is done, but the user hasn't selected a final location for
773 // it yet (the Save As dialog box is probably still showing), so just keep
774 // track of the fact that this download id is complete, when the
775 // DownloadItem is constructed later we'll notify its completion then.
776 PendingFinishedMap::iterator erase_it =
777 pending_finished_downloads_.find(download_id);
778 DCHECK(erase_it == pending_finished_downloads_.end());
779 pending_finished_downloads_[download_id] = size;
[email protected]9ccbb372008-10-10 18:50:32780 return;
initial.commit09911bf2008-07-26 23:55:29781 }
[email protected]9ccbb372008-10-10 18:50:32782
783 // Remove the id from the list of pending ids.
784 PendingFinishedMap::iterator erase_it =
785 pending_finished_downloads_.find(download_id);
786 if (erase_it != pending_finished_downloads_.end())
787 pending_finished_downloads_.erase(erase_it);
788
789 DownloadItem* download = it->second;
790 download->Finished(size);
791
792 // Clean up will happen when the history system create callback runs if we
793 // don't have a valid db_handle yet.
794 if (download->db_handle() != kUninitializedHandle) {
795 in_progress_.erase(it);
[email protected]9ccbb372008-10-10 18:50:32796 UpdateHistoryForDownload(download);
797 }
798
799 // If this a dangerous download not yet validated by the user, don't do
800 // anything. When the user notifies us, it will trigger a call to
801 // ProceedWithFinishedDangerousDownload.
802 if (download->safety_state() == DownloadItem::DANGEROUS) {
803 dangerous_finished_[download_id] = download;
804 return;
805 }
806
807 if (download->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED) {
[email protected]6cade212008-12-03 00:32:22808 // We first need to rename the downloaded file from its temporary name to
[email protected]9ccbb372008-10-10 18:50:32809 // its final name before we can continue.
810 file_loop_->PostTask(FROM_HERE,
811 NewRunnableMethod(
812 this, &DownloadManager::ProceedWithFinishedDangerousDownload,
813 download->db_handle(),
814 download->full_path(), download->original_name()));
815 return;
816 }
817 ContinueDownloadFinished(download);
818}
819
[email protected]8f783752009-04-01 23:33:45820void DownloadManager::DownloadRenamedToFinalName(int download_id,
821 const FilePath& full_path) {
822 FilePath::StringType extension = full_path.Extension();
823 // Drop the leading period.
824 if (extension.size() > 0)
825 extension = extension.substr(1);
826
827 if (extension == chrome::kExtensionFileExtension) {
828 OpenChromeExtension(full_path);
829 }
830}
831
[email protected]9ccbb372008-10-10 18:50:32832void DownloadManager::ContinueDownloadFinished(DownloadItem* download) {
833 // If this was a dangerous download, it has now been approved and must be
834 // removed from dangerous_finished_ so it does not get deleted on shutdown.
835 DownloadMap::iterator it = dangerous_finished_.find(download->id());
836 if (it != dangerous_finished_.end())
837 dangerous_finished_.erase(it);
838
839 // Notify our observers that we are complete (the call to Finished() set the
840 // state to complete but did not notify).
841 download->UpdateObservers();
842
843 // Open the download if the user or user prefs indicate it should be.
[email protected]2001fe82009-02-23 23:53:14844 FilePath::StringType extension = download->full_path().Extension();
845 // Drop the leading period.
846 if (extension.size() > 0)
847 extension = extension.substr(1);
[email protected]8f783752009-04-01 23:33:45848
849 // Handle chrome extensions explicitly and skip the shell execute.
850 if (extension == chrome::kExtensionFileExtension) {
851 // Skip the shell execute. This will be handled in
852 // DownloadRenamedToFinalName
853 return;
854 }
855
[email protected]9ccbb372008-10-10 18:50:32856 if (download->open_when_complete() || ShouldOpenFileExtension(extension))
857 OpenDownloadInShell(download, NULL);
858}
859
860// Called on the file thread. Renames the downloaded file to its original name.
861void DownloadManager::ProceedWithFinishedDangerousDownload(
862 int64 download_handle,
[email protected]7ae7c2cb2009-01-06 23:31:41863 const FilePath& path,
864 const FilePath& original_name) {
[email protected]9ccbb372008-10-10 18:50:32865 bool success = false;
[email protected]7ae7c2cb2009-01-06 23:31:41866 FilePath new_path;
[email protected]7a256ea2008-10-17 17:34:16867 int uniquifier = 0;
[email protected]9ccbb372008-10-10 18:50:32868 if (file_util::PathExists(path)) {
[email protected]889ed35c2009-01-21 00:07:24869 new_path = path.DirName().Append(original_name);
[email protected]7a256ea2008-10-17 17:34:16870 // Make our name unique at this point, as if a dangerous file is downloading
871 // and a 2nd download is started for a file with the same name, they would
872 // have the same path. This is because we uniquify the name on download
873 // start, and at that time the first file does not exists yet, so the second
874 // file gets the same name.
875 uniquifier = GetUniquePathNumber(new_path);
876 if (uniquifier > 0)
877 AppendNumberToPath(&new_path, uniquifier);
[email protected]9ccbb372008-10-10 18:50:32878 success = file_util::Move(path, new_path);
879 } else {
880 NOTREACHED();
881 }
[email protected]6cade212008-12-03 00:32:22882
[email protected]9ccbb372008-10-10 18:50:32883 ui_loop_->PostTask(FROM_HERE,
884 NewRunnableMethod(this, &DownloadManager::DangerousDownloadRenamed,
[email protected]7a256ea2008-10-17 17:34:16885 download_handle, success, new_path, uniquifier));
[email protected]9ccbb372008-10-10 18:50:32886}
887
888// Call from the file thread when the finished dangerous download was renamed.
889void DownloadManager::DangerousDownloadRenamed(int64 download_handle,
890 bool success,
[email protected]7ae7c2cb2009-01-06 23:31:41891 const FilePath& new_path,
[email protected]7a256ea2008-10-17 17:34:16892 int new_path_uniquifier) {
[email protected]9ccbb372008-10-10 18:50:32893 DownloadMap::iterator it = downloads_.find(download_handle);
894 if (it == downloads_.end()) {
895 NOTREACHED();
896 return;
897 }
898
899 DownloadItem* download = it->second;
900 // If we failed to rename the file, we'll just keep the name as is.
[email protected]7a256ea2008-10-17 17:34:16901 if (success) {
902 // We need to update the path uniquifier so that the UI shows the right
903 // name when calling GetFileName().
904 download->set_path_uniquifier(new_path_uniquifier);
[email protected]9ccbb372008-10-10 18:50:32905 RenameDownload(download, new_path);
[email protected]7a256ea2008-10-17 17:34:16906 }
[email protected]9ccbb372008-10-10 18:50:32907
908 // Continue the download finished sequence.
909 ContinueDownloadFinished(download);
initial.commit09911bf2008-07-26 23:55:29910}
911
912// static
913// We have to tell the ResourceDispatcherHost to cancel the download from this
[email protected]6cade212008-12-03 00:32:22914// thread, since we can't forward tasks from the file thread to the IO thread
initial.commit09911bf2008-07-26 23:55:29915// reliably (crash on shutdown race condition).
916void DownloadManager::CancelDownloadRequest(int render_process_id,
917 int request_id) {
918 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
[email protected]ab820df2008-08-26 05:55:10919 base::Thread* io_thread = g_browser_process->io_thread();
initial.commit09911bf2008-07-26 23:55:29920 if (!io_thread || !rdh)
921 return;
922 io_thread->message_loop()->PostTask(FROM_HERE,
923 NewRunnableFunction(&DownloadManager::OnCancelDownloadRequest,
924 rdh,
925 render_process_id,
926 request_id));
927}
928
929// static
930void DownloadManager::OnCancelDownloadRequest(ResourceDispatcherHost* rdh,
931 int render_process_id,
932 int request_id) {
933 rdh->CancelRequest(render_process_id, request_id, false);
934}
935
936void DownloadManager::DownloadCancelled(int32 download_id) {
937 DownloadMap::iterator it = in_progress_.find(download_id);
938 if (it == in_progress_.end())
939 return;
940 DownloadItem* download = it->second;
941
942 CancelDownloadRequest(download->render_process_id(), download->request_id());
943
944 // Clean up will happen when the history system create callback runs if we
945 // don't have a valid db_handle yet.
946 if (download->db_handle() != kUninitializedHandle) {
947 in_progress_.erase(it);
initial.commit09911bf2008-07-26 23:55:29948 UpdateHistoryForDownload(download);
949 }
950
951 // Tell the file manager to cancel the download.
952 file_manager_->RemoveDownload(download->id(), this); // On the UI thread
953 file_loop_->PostTask(FROM_HERE,
954 NewRunnableMethod(file_manager_,
955 &DownloadFileManager::CancelDownload,
956 download->id()));
957}
958
959void DownloadManager::PauseDownload(int32 download_id, bool pause) {
960 DownloadMap::iterator it = in_progress_.find(download_id);
961 if (it != in_progress_.end()) {
962 DownloadItem* download = it->second;
963 if (pause == download->is_paused())
964 return;
965
966 // Inform the ResourceDispatcherHost of the new pause state.
[email protected]ab820df2008-08-26 05:55:10967 base::Thread* io_thread = g_browser_process->io_thread();
initial.commit09911bf2008-07-26 23:55:29968 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
969 if (!io_thread || !rdh)
970 return;
971
972 io_thread->message_loop()->PostTask(FROM_HERE,
973 NewRunnableFunction(&DownloadManager::OnPauseDownloadRequest,
974 rdh,
975 download->render_process_id(),
976 download->request_id(),
977 pause));
978 }
979}
980
981// static
982void DownloadManager::OnPauseDownloadRequest(ResourceDispatcherHost* rdh,
983 int render_process_id,
984 int request_id,
985 bool pause) {
986 rdh->PauseRequest(render_process_id, request_id, pause);
987}
988
[email protected]7ae7c2cb2009-01-06 23:31:41989bool DownloadManager::IsDangerous(const FilePath& file_name) {
[email protected]9ccbb372008-10-10 18:50:32990 // TODO(jcampan): Improve me.
[email protected]2001fe82009-02-23 23:53:14991 FilePath::StringType extension = file_name.Extension();
992 // Drop the leading period.
993 if (extension.size() > 0)
994 extension = extension.substr(1);
995 return IsExecutable(extension);
[email protected]9ccbb372008-10-10 18:50:32996}
997
998void DownloadManager::RenameDownload(DownloadItem* download,
[email protected]7ae7c2cb2009-01-06 23:31:41999 const FilePath& new_path) {
[email protected]9ccbb372008-10-10 18:50:321000 download->Rename(new_path);
1001
1002 // Update the history.
1003
1004 // No update necessary if the download was initiated while in incognito mode.
1005 if (download->db_handle() <= kUninitializedHandle)
1006 return;
1007
[email protected]6cade212008-12-03 00:32:221008 // FIXME(paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
[email protected]9ccbb372008-10-10 18:50:321009 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
1010 if (hs)
[email protected]7ae7c2cb2009-01-06 23:31:411011 hs->UpdateDownloadPath(new_path.ToWStringHack(), download->db_handle());
[email protected]9ccbb372008-10-10 18:50:321012}
1013
initial.commit09911bf2008-07-26 23:55:291014void DownloadManager::RemoveDownload(int64 download_handle) {
1015 DownloadMap::iterator it = downloads_.find(download_handle);
1016 if (it == downloads_.end())
1017 return;
1018
1019 // Make history update.
1020 DownloadItem* download = it->second;
1021 RemoveDownloadFromHistory(download);
1022
1023 // Remove from our tables and delete.
1024 downloads_.erase(it);
[email protected]9ccbb372008-10-10 18:50:321025 it = dangerous_finished_.find(download->id());
1026 if (it != dangerous_finished_.end())
1027 dangerous_finished_.erase(it);
initial.commit09911bf2008-07-26 23:55:291028
1029 // Tell observers to refresh their views.
1030 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
[email protected]6f712872008-11-07 00:35:361031
1032 delete download;
initial.commit09911bf2008-07-26 23:55:291033}
1034
[email protected]e93d2822009-01-30 05:59:591035int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin,
1036 const base::Time remove_end) {
initial.commit09911bf2008-07-26 23:55:291037 RemoveDownloadsFromHistoryBetween(remove_begin, remove_end);
1038
initial.commit09911bf2008-07-26 23:55:291039 DownloadMap::iterator it = downloads_.begin();
[email protected]78b8fcc92009-03-31 17:36:281040 std::vector<DownloadItem*> pending_deletes;
initial.commit09911bf2008-07-26 23:55:291041 while (it != downloads_.end()) {
1042 DownloadItem* download = it->second;
1043 DownloadItem::DownloadState state = download->state();
1044 if (download->start_time() >= remove_begin &&
1045 (remove_end.is_null() || download->start_time() < remove_end) &&
1046 (state == DownloadItem::COMPLETE ||
1047 state == DownloadItem::CANCELLED)) {
1048 // Remove from the map and move to the next in the list.
[email protected]b7f05882009-02-22 01:21:561049 downloads_.erase(it++);
[email protected]a6604d92008-10-30 00:58:581050
1051 // Also remove it from any completed dangerous downloads.
1052 DownloadMap::iterator dit = dangerous_finished_.find(download->id());
1053 if (dit != dangerous_finished_.end())
1054 dangerous_finished_.erase(dit);
1055
[email protected]78b8fcc92009-03-31 17:36:281056 pending_deletes.push_back(download);
initial.commit09911bf2008-07-26 23:55:291057
initial.commit09911bf2008-07-26 23:55:291058 continue;
1059 }
1060
1061 ++it;
1062 }
1063
1064 // Tell observers to refresh their views.
[email protected]78b8fcc92009-03-31 17:36:281065 int num_deleted = static_cast<int>(pending_deletes.size());
initial.commit09911bf2008-07-26 23:55:291066 if (num_deleted > 0)
1067 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
1068
[email protected]78b8fcc92009-03-31 17:36:281069 // Delete the download items after updating the observers.
1070 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
1071 pending_deletes.clear();
1072
initial.commit09911bf2008-07-26 23:55:291073 return num_deleted;
1074}
1075
[email protected]e93d2822009-01-30 05:59:591076int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
1077 return RemoveDownloadsBetween(remove_begin, base::Time());
initial.commit09911bf2008-07-26 23:55:291078}
1079
[email protected]d41355e6f2009-04-07 21:21:121080int DownloadManager::RemoveAllDownloads() {
1081 // The null times make the date range unbounded.
1082 return RemoveDownloadsBetween(base::Time(), base::Time());
1083}
1084
initial.commit09911bf2008-07-26 23:55:291085// Initiate a download of a specific URL. We send the request to the
1086// ResourceDispatcherHost, and let it send us responses like a regular
1087// download.
1088void DownloadManager::DownloadUrl(const GURL& url,
1089 const GURL& referrer,
[email protected]c9825a42009-05-01 22:51:501090 const std::string& referrer_charset,
[email protected]57c6a652009-05-04 07:58:341091 TabContents* tab_contents) {
1092 DCHECK(tab_contents);
[email protected]c9825a42009-05-01 22:51:501093 request_context_->set_referrer_charset(referrer_charset);
initial.commit09911bf2008-07-26 23:55:291094 file_manager_->DownloadUrl(url,
1095 referrer,
[email protected]57c6a652009-05-04 07:58:341096 tab_contents->process()->pid(),
1097 tab_contents->render_view_host()->routing_id(),
initial.commit09911bf2008-07-26 23:55:291098 request_context_.get());
1099}
1100
[email protected]7ae7c2cb2009-01-06 23:31:411101void DownloadManager::GenerateExtension(
1102 const FilePath& file_name,
1103 const std::string& mime_type,
1104 FilePath::StringType* generated_extension) {
initial.commit09911bf2008-07-26 23:55:291105 // We're worried about three things here:
1106 //
1107 // 1) Security. Many sites let users upload content, such as buddy icons, to
1108 // their web sites. We want to mitigate the case where an attacker
1109 // supplies a malicious executable with an executable file extension but an
1110 // honest site serves the content with a benign content type, such as
1111 // image/jpeg.
1112 //
1113 // 2) Usability. If the site fails to provide a file extension, we want to
1114 // guess a reasonable file extension based on the content type.
1115 //
1116 // 3) Shell integration. Some file extensions automatically integrate with
1117 // the shell. We block these extensions to prevent a malicious web site
1118 // from integrating with the user's shell.
1119
[email protected]7ae7c2cb2009-01-06 23:31:411120 static const FilePath::CharType default_extension[] =
1121 FILE_PATH_LITERAL("download");
initial.commit09911bf2008-07-26 23:55:291122
1123 // See if our file name already contains an extension.
[email protected]7ae7c2cb2009-01-06 23:31:411124 FilePath::StringType extension(
1125 file_util::GetFileExtensionFromPath(file_name));
initial.commit09911bf2008-07-26 23:55:291126
[email protected]b7f05882009-02-22 01:21:561127#if defined(OS_WIN)
initial.commit09911bf2008-07-26 23:55:291128 // Rename shell-integrated extensions.
1129 if (win_util::IsShellIntegratedExtension(extension))
1130 extension.assign(default_extension);
[email protected]b7f05882009-02-22 01:21:561131#endif
initial.commit09911bf2008-07-26 23:55:291132
1133 std::string mime_type_from_extension;
[email protected]bae0ea12009-02-14 01:20:411134 net::GetMimeTypeFromFile(file_name,
[email protected]7ae7c2cb2009-01-06 23:31:411135 &mime_type_from_extension);
initial.commit09911bf2008-07-26 23:55:291136 if (mime_type == mime_type_from_extension) {
1137 // The hinted extension matches the mime type. It looks like a winner.
1138 generated_extension->swap(extension);
1139 return;
1140 }
1141
1142 if (IsExecutable(extension) && !IsExecutableMimeType(mime_type)) {
1143 // We want to be careful about executable extensions. The worry here is
1144 // that a trusted web site could be tricked into dropping an executable file
1145 // on the user's filesystem.
[email protected]a9bb6f692008-07-30 16:40:101146 if (!net::GetPreferredExtensionForMimeType(mime_type, &extension)) {
initial.commit09911bf2008-07-26 23:55:291147 // We couldn't find a good extension for this content type. Use a dummy
1148 // extension instead.
1149 extension.assign(default_extension);
1150 }
1151 }
1152
1153 if (extension.empty()) {
[email protected]a9bb6f692008-07-30 16:40:101154 net::GetPreferredExtensionForMimeType(mime_type, &extension);
initial.commit09911bf2008-07-26 23:55:291155 } else {
[email protected]6cade212008-12-03 00:32:221156 // Append extension generated from the mime type if:
initial.commit09911bf2008-07-26 23:55:291157 // 1. New extension is not ".txt"
1158 // 2. New extension is not the same as the already existing extension.
1159 // 3. New extension is not executable. This action mitigates the case when
[email protected]7ae7c2cb2009-01-06 23:31:411160 // an executable is hidden in a benign file extension;
initial.commit09911bf2008-07-26 23:55:291161 // E.g. my-cat.jpg becomes my-cat.jpg.js if content type is
1162 // application/x-javascript.
[email protected]e106457b2009-03-25 22:43:371163 // 4. New extension is not ".tar" for .gz files. For misconfigured web
1164 // servers, i.e. bug 5772.
[email protected]7ae7c2cb2009-01-06 23:31:411165 FilePath::StringType append_extension;
[email protected]a9bb6f692008-07-30 16:40:101166 if (net::GetPreferredExtensionForMimeType(mime_type, &append_extension)) {
[email protected]3f156552009-02-09 19:44:171167 if (append_extension != FILE_PATH_LITERAL("txt") &&
[email protected]7ae7c2cb2009-01-06 23:31:411168 append_extension != extension &&
[email protected]e106457b2009-03-25 22:43:371169 !IsExecutable(append_extension) &&
1170 (append_extension != FILE_PATH_LITERAL("tar") ||
1171 extension != FILE_PATH_LITERAL("gz"))) {
[email protected]3f156552009-02-09 19:44:171172 extension += FILE_PATH_LITERAL(".");
initial.commit09911bf2008-07-26 23:55:291173 extension += append_extension;
[email protected]3f156552009-02-09 19:44:171174 }
initial.commit09911bf2008-07-26 23:55:291175 }
1176 }
1177
1178 generated_extension->swap(extension);
1179}
1180
1181void DownloadManager::GenerateFilename(DownloadCreateInfo* info,
[email protected]7ae7c2cb2009-01-06 23:31:411182 FilePath* generated_name) {
1183 *generated_name = FilePath::FromWStringHack(
[email protected]8ac1a752008-07-31 19:40:371184 net::GetSuggestedFilename(GURL(info->url),
1185 info->content_disposition,
[email protected]c9825a42009-05-01 22:51:501186 info->referrer_charset,
[email protected]7ae7c2cb2009-01-06 23:31:411187 L"download"));
1188 DCHECK(!generated_name->empty());
initial.commit09911bf2008-07-26 23:55:291189
[email protected]7ae7c2cb2009-01-06 23:31:411190 GenerateSafeFilename(info->mime_type, generated_name);
initial.commit09911bf2008-07-26 23:55:291191}
1192
1193void DownloadManager::AddObserver(Observer* observer) {
1194 observers_.AddObserver(observer);
1195 observer->ModelChanged();
1196}
1197
1198void DownloadManager::RemoveObserver(Observer* observer) {
1199 observers_.RemoveObserver(observer);
1200}
1201
1202// Post Windows Shell operations to the Download thread, to avoid blocking the
1203// user interface.
1204void DownloadManager::ShowDownloadInShell(const DownloadItem* download) {
1205 DCHECK(file_manager_);
1206 file_loop_->PostTask(FROM_HERE,
1207 NewRunnableMethod(file_manager_,
1208 &DownloadFileManager::OnShowDownloadInShell,
[email protected]7ae7c2cb2009-01-06 23:31:411209 FilePath(download->full_path())));
initial.commit09911bf2008-07-26 23:55:291210}
1211
[email protected]8f783752009-04-01 23:33:451212void DownloadManager::OpenDownload(const DownloadItem* download,
1213 gfx::NativeView parent_window) {
1214 FilePath::StringType extension = download->full_path().Extension();
1215 // Drop the leading period.
1216 if (extension.size() > 0)
1217 extension = extension.substr(1);
1218
1219 // Open Chrome extensions with ExtenstionsService. For everthing else do shell
1220 // execute.
1221 if (extension == chrome::kExtensionFileExtension) {
1222 OpenChromeExtension(download->full_path());
1223 } else {
1224 OpenDownloadInShell(download, parent_window);
1225 }
1226}
1227
1228void DownloadManager::OpenChromeExtension(const FilePath& full_path) {
[email protected]5a947d5a2009-05-01 23:15:311229 if (CommandLine::ForCurrentProcess()->HasSwitch(
1230 switches::kEnableExtensions)) {
1231 // Temporary: Ask the user if it's okay to install the extension. This
1232 // should be replaced with the actual extension installation UI when it is
1233 // avaiable.
[email protected]d81706b82009-04-03 20:28:441234#if defined(OS_WIN)
[email protected]5a947d5a2009-05-01 23:15:311235 if (win_util::MessageBox(GetActiveWindow(),
1236 L"Are you sure you want to install this extension?\n\n"
1237 L"This is a temporary message and it will be removed when extensions "
1238 L"UI is finalized.",
1239 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(), MB_OKCANCEL) == IDOK) {
1240 ExtensionsService* extensions_service = profile_->GetExtensionsService();
1241 extensions_service->InstallExtension(full_path);
1242 }
1243#else
1244 // TODO(port): Needs CreateChromeWindow.
[email protected]d81706b82009-04-03 20:28:441245 ExtensionsService* extensions_service = profile_->GetExtensionsService();
1246 extensions_service->InstallExtension(full_path);
[email protected]d81706b82009-04-03 20:28:441247#endif
[email protected]5a947d5a2009-05-01 23:15:311248 } else {
1249#if defined(OS_WIN)
1250 win_util::MessageBox(GetActiveWindow(),
1251 L"Extensions are not enabled. Add --enable-extensions to the "
1252 L"command-line to enable extensions.\n\n"
1253 L"This is a temporary message and it will be removed when extensions "
1254 L"UI is finalized.",
1255 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(), MB_OK);
1256#else
1257 // TODO(port): Needs CreateChromeWindow
1258#endif
1259 }
[email protected]8f783752009-04-01 23:33:451260}
1261
initial.commit09911bf2008-07-26 23:55:291262void DownloadManager::OpenDownloadInShell(const DownloadItem* download,
[email protected]e93d2822009-01-30 05:59:591263 gfx::NativeView parent_window) {
initial.commit09911bf2008-07-26 23:55:291264 DCHECK(file_manager_);
1265 file_loop_->PostTask(FROM_HERE,
1266 NewRunnableMethod(file_manager_,
1267 &DownloadFileManager::OnOpenDownloadInShell,
1268 download->full_path(), download->url(), parent_window));
1269}
1270
[email protected]7ae7c2cb2009-01-06 23:31:411271void DownloadManager::OpenFilesOfExtension(
1272 const FilePath::StringType& extension, bool open) {
initial.commit09911bf2008-07-26 23:55:291273 if (open && !IsExecutable(extension))
1274 auto_open_.insert(extension);
1275 else
1276 auto_open_.erase(extension);
1277 SaveAutoOpens();
1278}
1279
[email protected]7ae7c2cb2009-01-06 23:31:411280bool DownloadManager::ShouldOpenFileExtension(
1281 const FilePath::StringType& extension) {
[email protected]8c756ac2009-01-30 23:36:411282 // Special-case Chrome extensions as always-open.
initial.commit09911bf2008-07-26 23:55:291283 if (!IsExecutable(extension) &&
[email protected]8c756ac2009-01-30 23:36:411284 (auto_open_.find(extension) != auto_open_.end() ||
1285 extension == chrome::kExtensionFileExtension))
1286 return true;
initial.commit09911bf2008-07-26 23:55:291287 return false;
1288}
1289
[email protected]7b73d992008-12-15 20:56:461290static const char* kExecutableWhiteList[] = {
initial.commit09911bf2008-07-26 23:55:291291 // JavaScript is just as powerful as EXE.
[email protected]7b73d992008-12-15 20:56:461292 "text/javascript",
1293 "text/javascript;version=*",
[email protected]54d8d452009-04-08 17:29:241294 // Registry files can cause critical changes to the MS OS behavior.
1295 // Addition of this mimetype also addresses bug 7337.
1296 "text/x-registry",
[email protected]60ff8f912008-12-05 07:58:391297 // Some sites use binary/octet-stream to mean application/octet-stream.
1298 // See http://code.google.com/p/chromium/issues/detail?id=1573
[email protected]7b73d992008-12-15 20:56:461299 "binary/octet-stream"
1300};
initial.commit09911bf2008-07-26 23:55:291301
[email protected]7b73d992008-12-15 20:56:461302static const char* kExecutableBlackList[] = {
initial.commit09911bf2008-07-26 23:55:291303 // These application types are not executable.
[email protected]7b73d992008-12-15 20:56:461304 "application/*+xml",
1305 "application/xml"
1306};
initial.commit09911bf2008-07-26 23:55:291307
[email protected]7b73d992008-12-15 20:56:461308// static
1309bool DownloadManager::IsExecutableMimeType(const std::string& mime_type) {
[email protected]bae0ea12009-02-14 01:20:411310 for (size_t i = 0; i < arraysize(kExecutableWhiteList); ++i) {
[email protected]7b73d992008-12-15 20:56:461311 if (net::MatchesMimeType(kExecutableWhiteList[i], mime_type))
1312 return true;
1313 }
[email protected]bae0ea12009-02-14 01:20:411314 for (size_t i = 0; i < arraysize(kExecutableBlackList); ++i) {
[email protected]7b73d992008-12-15 20:56:461315 if (net::MatchesMimeType(kExecutableBlackList[i], mime_type))
1316 return false;
1317 }
1318 // We consider only other application types to be executable.
1319 return net::MatchesMimeType("application/*", mime_type);
initial.commit09911bf2008-07-26 23:55:291320}
1321
[email protected]7ae7c2cb2009-01-06 23:31:411322bool DownloadManager::IsExecutable(const FilePath::StringType& extension) {
[email protected]64da0b932009-02-24 02:30:041323#if defined(OS_WIN)
1324 if (!IsStringASCII(extension))
1325 return false;
1326 std::string ascii_extension = WideToASCII(extension);
1327 StringToLowerASCII(&ascii_extension);
1328
1329 return exe_types_.find(ascii_extension) != exe_types_.end();
1330#elif defined(OS_POSIX)
1331 // TODO(port): we misght not want to call this function on other platforms.
1332 // Figure it out.
1333 NOTIMPLEMENTED();
1334 return false;
1335#endif
initial.commit09911bf2008-07-26 23:55:291336}
1337
1338void DownloadManager::ResetAutoOpenFiles() {
1339 auto_open_.clear();
1340 SaveAutoOpens();
1341}
1342
1343bool DownloadManager::HasAutoOpenFileTypesRegistered() const {
1344 return !auto_open_.empty();
1345}
1346
1347void DownloadManager::SaveAutoOpens() {
1348 PrefService* prefs = profile_->GetPrefs();
1349 if (prefs) {
[email protected]7ae7c2cb2009-01-06 23:31:411350 FilePath::StringType extensions;
1351 for (std::set<FilePath::StringType>::iterator it = auto_open_.begin();
initial.commit09911bf2008-07-26 23:55:291352 it != auto_open_.end(); ++it) {
[email protected]7ae7c2cb2009-01-06 23:31:411353 extensions += *it + FILE_PATH_LITERAL(":");
initial.commit09911bf2008-07-26 23:55:291354 }
1355 if (!extensions.empty())
1356 extensions.erase(extensions.size() - 1);
[email protected]b7f05882009-02-22 01:21:561357
1358 std::wstring extensions_w;
1359#if defined(OS_WIN)
1360 extensions_w = extensions;
1361#elif defined(OS_POSIX)
[email protected]1b5044d2009-02-24 00:04:141362 extensions_w = base::SysNativeMBToWide(extensions);
[email protected]b7f05882009-02-22 01:21:561363#endif
1364
1365 prefs->SetString(prefs::kDownloadExtensionsToOpen, extensions_w);
initial.commit09911bf2008-07-26 23:55:291366 }
1367}
1368
[email protected]561abe62009-04-06 18:08:341369void DownloadManager::FileSelected(const FilePath& path,
[email protected]23b357b2009-03-30 20:02:361370 int index, void* params) {
initial.commit09911bf2008-07-26 23:55:291371 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
[email protected]7d3851d82008-12-12 03:26:071372 if (info->save_as)
[email protected]7ae7c2cb2009-01-06 23:31:411373 last_download_path_ = path.DirName();
initial.commit09911bf2008-07-26 23:55:291374 ContinueStartDownload(info, path);
1375}
1376
1377void DownloadManager::FileSelectionCanceled(void* params) {
1378 // The user didn't pick a place to save the file, so need to cancel the
1379 // download that's already in progress to the temporary location.
1380 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
1381 file_loop_->PostTask(FROM_HERE,
1382 NewRunnableMethod(file_manager_, &DownloadFileManager::CancelDownload,
1383 info->download_id));
1384}
1385
[email protected]7ae7c2cb2009-01-06 23:31:411386void DownloadManager::DeleteDownload(const FilePath& path) {
1387 file_loop_->PostTask(FROM_HERE, NewRunnableFunction(
1388 &DownloadFileManager::DeleteFile, FilePath(path)));
[email protected]9ccbb372008-10-10 18:50:321389}
1390
1391
1392void DownloadManager::DangerousDownloadValidated(DownloadItem* download) {
1393 DCHECK_EQ(DownloadItem::DANGEROUS, download->safety_state());
1394 download->set_safety_state(DownloadItem::DANGEROUS_BUT_VALIDATED);
1395 download->UpdateObservers();
1396
1397 // If the download is not complete, nothing to do. The required
1398 // post-processing will be performed when it does complete.
1399 if (download->state() != DownloadItem::COMPLETE)
1400 return;
1401
1402 file_loop_->PostTask(FROM_HERE,
1403 NewRunnableMethod(this,
1404 &DownloadManager::ProceedWithFinishedDangerousDownload,
1405 download->db_handle(), download->full_path(),
1406 download->original_name()));
1407}
1408
[email protected]763f946a2009-01-06 19:04:391409void DownloadManager::GenerateSafeFilename(const std::string& mime_type,
[email protected]7ae7c2cb2009-01-06 23:31:411410 FilePath* file_name) {
1411 // Make sure we get the right file extension
1412 FilePath::StringType extension;
[email protected]763f946a2009-01-06 19:04:391413 GenerateExtension(*file_name, mime_type, &extension);
1414 file_util::ReplaceExtension(file_name, extension);
1415
[email protected]2b2f8f72009-02-24 22:42:051416#if defined(OS_WIN)
[email protected]763f946a2009-01-06 19:04:391417 // Prepend "_" to the file name if it's a reserved name
[email protected]7ae7c2cb2009-01-06 23:31:411418 FilePath::StringType leaf_name = file_name->BaseName().value();
[email protected]763f946a2009-01-06 19:04:391419 DCHECK(!leaf_name.empty());
1420 if (win_util::IsReservedName(leaf_name)) {
[email protected]7ae7c2cb2009-01-06 23:31:411421 leaf_name = FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name;
1422 *file_name = file_name->DirName();
1423 if (file_name->value() == FilePath::kCurrentDirectory) {
1424 *file_name = FilePath(leaf_name);
[email protected]763f946a2009-01-06 19:04:391425 } else {
[email protected]7ae7c2cb2009-01-06 23:31:411426 *file_name = file_name->Append(leaf_name);
[email protected]763f946a2009-01-06 19:04:391427 }
1428 }
[email protected]b7f05882009-02-22 01:21:561429#elif defined(OS_POSIX)
1430 NOTIMPLEMENTED();
1431#endif
[email protected]763f946a2009-01-06 19:04:391432}
1433
initial.commit09911bf2008-07-26 23:55:291434// Operations posted to us from the history service ----------------------------
1435
1436// The history service has retrieved all download entries. 'entries' contains
1437// 'DownloadCreateInfo's in sorted order (by ascending start_time).
1438void DownloadManager::OnQueryDownloadEntriesComplete(
1439 std::vector<DownloadCreateInfo>* entries) {
1440 for (size_t i = 0; i < entries->size(); ++i) {
1441 DownloadItem* download = new DownloadItem(entries->at(i));
1442 DCHECK(downloads_.find(download->db_handle()) == downloads_.end());
1443 downloads_[download->db_handle()] = download;
1444 download->set_manager(this);
1445 }
1446 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
1447}
1448
initial.commit09911bf2008-07-26 23:55:291449// Once the new DownloadItem's creation info has been committed to the history
1450// service, we associate the DownloadItem with the db handle, update our
1451// 'downloads_' map and inform observers.
1452void DownloadManager::OnCreateDownloadEntryComplete(DownloadCreateInfo info,
1453 int64 db_handle) {
1454 DownloadMap::iterator it = in_progress_.find(info.download_id);
1455 DCHECK(it != in_progress_.end());
1456
1457 DownloadItem* download = it->second;
1458 DCHECK(download->db_handle() == kUninitializedHandle);
1459 download->set_db_handle(db_handle);
1460
1461 // Insert into our full map.
1462 DCHECK(downloads_.find(download->db_handle()) == downloads_.end());
1463 downloads_[download->db_handle()] = download;
1464
[email protected]5e595482009-05-06 20:16:531465 // Show in the appropropriate browser UI.
1466 ShowDownloadInBrowser(info, download);
initial.commit09911bf2008-07-26 23:55:291467
1468 // Inform interested objects about the new download.
1469 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
initial.commit09911bf2008-07-26 23:55:291470
1471 // If this download has been completed before we've received the db handle,
1472 // post one final message to the history service so that it can be properly
1473 // in sync with the DownloadItem's completion status, and also inform any
1474 // observers so that they get more than just the start notification.
1475 if (download->state() != DownloadItem::IN_PROGRESS) {
1476 in_progress_.erase(it);
initial.commit09911bf2008-07-26 23:55:291477 UpdateHistoryForDownload(download);
1478 download->UpdateObservers();
1479 }
1480}
1481
1482// Called when the history service has retrieved the list of downloads that
1483// match the search text.
1484void DownloadManager::OnSearchComplete(HistoryService::Handle handle,
1485 std::vector<int64>* results) {
1486 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
1487 Observer* requestor = cancelable_consumer_.GetClientData(hs, handle);
1488 if (!requestor)
1489 return;
1490
1491 std::vector<DownloadItem*> searched_downloads;
1492 for (std::vector<int64>::iterator it = results->begin();
1493 it != results->end(); ++it) {
1494 DownloadMap::iterator dit = downloads_.find(*it);
1495 if (dit != downloads_.end())
1496 searched_downloads.push_back(dit->second);
1497 }
1498
1499 requestor->SetDownloads(searched_downloads);
1500}
[email protected]905a08d2008-11-19 07:24:121501
[email protected]5e595482009-05-06 20:16:531502void DownloadManager::ShowDownloadInBrowser(const DownloadCreateInfo& info,
1503 DownloadItem* download) {
1504 // Extension downloading skips the shelf. This is a temporary fix until
1505 // we can modularize the download system and develop specific extensiona
1506 // install UI.
1507 FilePath::StringType extension = info.path.Extension();
1508 // Ignore the leading period.
1509 if (extension.find(chrome::kExtensionFileExtension) == 1)
1510 return;
1511
1512 // The 'contents' may no longer exist if the user closed the tab before we get
1513 // this start completion event. If it does, tell the origin TabContents to
1514 // display its download shelf.
1515 TabContents* contents =
1516 tab_util::GetTabContentsByID(info.render_process_id, info.render_view_id);
1517
1518 // If the contents no longer exists, we start the download in the last active
1519 // browser. This is not ideal but better than fully hiding the download from
1520 // the user.
1521 if (!contents) {
1522 Browser* last_active = BrowserList::GetLastActive();
1523 if (last_active)
1524 contents = last_active->GetSelectedTabContents();
1525 }
1526
1527 if (contents)
1528 contents->OnStartDownload(download);
1529}
1530
[email protected]6cade212008-12-03 00:32:221531// Clears the last download path, used to initialize "save as" dialogs.
[email protected]905a08d2008-11-19 07:24:121532void DownloadManager::ClearLastDownloadPath() {
[email protected]7ae7c2cb2009-01-06 23:31:411533 last_download_path_ = FilePath();
[email protected]905a08d2008-11-19 07:24:121534}