Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1686)

Side by Side Diff: chrome/browser/download_manager.cc

Issue 2826: Move the download code to new directories: (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 12 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/download_manager.h ('k') | chrome/browser/download_manager_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <time.h>
6
7 #include "chrome/browser/download_manager.h"
8
9 #include "base/file_util.h"
10 #include "base/logging.h"
11 #include "base/message_loop.h"
12 #include "base/path_service.h"
13 #include "base/registry.h"
14 #include "base/string_util.h"
15 #include "base/task.h"
16 #include "base/thread.h"
17 #include "base/timer.h"
18 #include "base/win_util.h"
19 #include "chrome/browser/browser_list.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/download_file.h"
22 #include "chrome/browser/download_util.h"
23 #include "chrome/browser/profile.h"
24 #include "chrome/browser/render_process_host.h"
25 #include "chrome/browser/render_view_host.h"
26 #include "chrome/browser/resource_dispatcher_host.h"
27 #include "chrome/browser/tab_util.h"
28 #include "chrome/browser/web_contents.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/l10n_util.h"
31 #include "chrome/common/notification_service.h"
32 #include "chrome/common/pref_names.h"
33 #include "chrome/common/pref_service.h"
34 #include "chrome/common/stl_util-inl.h"
35 #include "chrome/common/win_util.h"
36 #include "googleurl/src/gurl.h"
37 #include "net/base/mime_util.h"
38 #include "net/base/net_util.h"
39 #include "net/url_request/url_request_context.h"
40
41 #include "generated_resources.h"
42
43 // Periodically update our observers.
44 class DownloadItemUpdateTask : public Task {
45 public:
46 explicit DownloadItemUpdateTask(DownloadItem* item) : item_(item) {}
47 void Run() { if (item_) item_->UpdateObservers(); }
48
49 private:
50 DownloadItem* item_;
51 };
52
53 // Update frequency (milliseconds).
54 static const int kUpdateTimeMs = 1000;
55
56 // Our download table ID starts at 1, so we use 0 to represent a download that
57 // has started, but has not yet had its data persisted in the table. We use fake
58 // database handles in incognito mode starting at -1 and progressly getting more
59 // negative.
60 static const int kUninitializedHandle = 0;
61
62 // Attempts to modify |path| to be a non-existing path.
63 // Returns true if |path| points to a non-existing path upon return.
64 static bool UniquifyPath(std::wstring* path) {
65 DCHECK(path);
66 const int kMaxAttempts = 100;
67
68 if (!file_util::PathExists(*path))
69 return true;
70
71 std::wstring new_path;
72 for (int count = 1; count <= kMaxAttempts; ++count) {
73 new_path.assign(*path);
74 file_util::InsertBeforeExtension(&new_path, StringPrintf(L" (%d)", count));
75
76 if (!file_util::PathExists(new_path)) {
77 path->swap(new_path);
78 return true;
79 }
80 }
81
82 return false;
83 }
84
85 static bool DownloadPathIsDangerous(const std::wstring& download_path) {
86 std::wstring desktop_dir;
87 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_dir)) {
88 NOTREACHED();
89 return false;
90 }
91 return (download_path == desktop_dir);
92 }
93
94 // DownloadItem implementation -------------------------------------------------
95
96 // Constructor for reading from the history service.
97 DownloadItem::DownloadItem(const DownloadCreateInfo& info)
98 : id_(-1),
99 full_path_(info.path),
100 url_(info.url),
101 total_bytes_(info.total_bytes),
102 received_bytes_(info.received_bytes),
103 start_tick_(0),
104 state_(static_cast<DownloadState>(info.state)),
105 start_time_(info.start_time),
106 db_handle_(info.db_handle),
107 manager_(NULL),
108 is_paused_(false),
109 open_when_complete_(false),
110 render_process_id_(-1),
111 request_id_(-1) {
112 if (state_ == IN_PROGRESS)
113 state_ = CANCELLED;
114 Init(false /* don't start progress timer */);
115 }
116
117 // Constructor for DownloadItem created via user action in the main thread.
118 DownloadItem::DownloadItem(int32 download_id,
119 const std::wstring& path,
120 const std::wstring& url,
121 const Time start_time,
122 int64 download_size,
123 int render_process_id,
124 int request_id)
125 : id_(download_id),
126 full_path_(path),
127 url_(url),
128 total_bytes_(download_size),
129 received_bytes_(0),
130 start_tick_(GetTickCount()),
131 state_(IN_PROGRESS),
132 start_time_(start_time),
133 db_handle_(kUninitializedHandle),
134 manager_(NULL),
135 is_paused_(false),
136 open_when_complete_(false),
137 render_process_id_(render_process_id),
138 request_id_(request_id) {
139 Init(true /* start progress timer */);
140 }
141
142 void DownloadItem::Init(bool start_timer) {
143 file_name_ = file_util::GetFilenameFromPath(full_path_);
144 if (start_timer)
145 StartProgressTimer();
146 }
147
148 DownloadItem::~DownloadItem() {
149 state_ = REMOVING;
150 UpdateObservers();
151 }
152
153 void DownloadItem::AddObserver(Observer* observer) {
154 observers_.AddObserver(observer);
155 }
156
157 void DownloadItem::RemoveObserver(Observer* observer) {
158 observers_.RemoveObserver(observer);
159 }
160
161 void DownloadItem::UpdateObservers() {
162 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
163 }
164
165 // If we've received more data than we were expecting (bad server info?), revert
166 // to 'unknown size mode'.
167 void DownloadItem::UpdateSize(int64 bytes_so_far) {
168 received_bytes_ = bytes_so_far;
169 if (received_bytes_ > total_bytes_)
170 total_bytes_ = 0;
171 }
172
173 // Updates from the download thread may have been posted while this download
174 // was being cancelled in the UI thread, so we'll accept them unless we're
175 // complete.
176 void DownloadItem::Update(int64 bytes_so_far) {
177 if (state_ == COMPLETE) {
178 NOTREACHED();
179 return;
180 }
181 UpdateSize(bytes_so_far);
182 UpdateObservers();
183 }
184
185 // Triggered by a user action
186 void DownloadItem::Cancel(bool update_history) {
187 if (state_ != IN_PROGRESS) {
188 // Small downloads might be complete before this method has a chance to run.
189 return;
190 }
191 state_ = CANCELLED;
192 UpdateObservers();
193 StopProgressTimer();
194 if (update_history)
195 manager_->DownloadCancelled(id_);
196 }
197
198 void DownloadItem::Finished(int64 size) {
199 state_ = COMPLETE;
200 UpdateSize(size);
201 UpdateObservers();
202 StopProgressTimer();
203 }
204
205 void DownloadItem::Remove() {
206 Cancel(true);
207 state_ = REMOVING;
208 manager_->RemoveDownload(db_handle_);
209 }
210
211 void DownloadItem::StartProgressTimer() {
212 update_timer_.Start(TimeDelta::FromMilliseconds(kUpdateTimeMs), this,
213 &DownloadItem::UpdateObservers);
214 }
215
216 void DownloadItem::StopProgressTimer() {
217 update_timer_.Stop();
218 }
219
220 bool DownloadItem::TimeRemaining(TimeDelta* remaining) const {
221 if (total_bytes_ <= 0)
222 return false; // We never received the content_length for this download.
223
224 int64 speed = CurrentSpeed();
225 if (speed == 0)
226 return false;
227
228 *remaining =
229 TimeDelta::FromSeconds((total_bytes_ - received_bytes_) / speed);
230 return true;
231 }
232
233 int64 DownloadItem::CurrentSpeed() const {
234 uintptr_t diff = GetTickCount() - start_tick_;
235 return diff == 0 ? 0 : received_bytes_ * 1000 / diff;
236 }
237
238 int DownloadItem::PercentComplete() const {
239 int percent = -1;
240 if (total_bytes_ > 0)
241 percent = static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
242 return percent;
243 }
244
245 void DownloadItem::Rename(const std::wstring& full_path) {
246 DCHECK(!full_path.empty());
247 full_path_ = full_path;
248 file_name_ = file_util::GetFilenameFromPath(full_path_);
249 }
250
251 void DownloadItem::TogglePause() {
252 DCHECK(state_ == IN_PROGRESS);
253 manager_->PauseDownload(id_, !is_paused_);
254 is_paused_ = !is_paused_;
255 UpdateObservers();
256 }
257
258 // DownloadManager implementation ----------------------------------------------
259
260 // static
261 void DownloadManager::RegisterUserPrefs(PrefService* prefs) {
262 prefs->RegisterBooleanPref(prefs::kPromptForDownload, false);
263 prefs->RegisterStringPref(prefs::kDownloadExtensionsToOpen, L"");
264 prefs->RegisterBooleanPref(prefs::kDownloadDirUpgraded, false);
265
266 // The default download path is userprofile\download.
267 std::wstring default_download_path;
268 if (!PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_download_path)) {
269 NOTREACHED();
270 }
271 file_util::AppendToPath(&default_download_path,
272 l10n_util::GetString(IDS_DOWNLOAD_DIRECTORY));
273 prefs->RegisterStringPref(prefs::kDownloadDefaultDirectory,
274 default_download_path);
275
276 // If the download path is dangerous we forcefully reset it. But if we do
277 // so we set a flag to make sure we only do it once, to avoid fighting
278 // the user if he really wants it on an unsafe place such as the desktop.
279
280 if (!prefs->GetBoolean(prefs::kDownloadDirUpgraded)) {
281 std::wstring current_download_dir =
282 prefs->GetString(prefs::kDownloadDefaultDirectory);
283 if (DownloadPathIsDangerous(current_download_dir)) {
284 prefs->SetString(prefs::kDownloadDefaultDirectory,
285 default_download_path);
286 }
287 prefs->SetBoolean(prefs::kDownloadDirUpgraded, true);
288 }
289 }
290
291 DownloadManager::DownloadManager()
292 : shutdown_needed_(false),
293 profile_(NULL),
294 file_manager_(NULL),
295 ui_loop_(MessageLoop::current()),
296 file_loop_(NULL) {
297 }
298
299 DownloadManager::~DownloadManager() {
300 if (shutdown_needed_)
301 Shutdown();
302 }
303
304 void DownloadManager::Shutdown() {
305 DCHECK(shutdown_needed_) << "Shutdown called when not needed.";
306
307 // Stop receiving download updates
308 file_manager_->RemoveDownloadManager(this);
309
310 // Stop making history service requests
311 cancelable_consumer_.CancelAllRequests();
312
313 // 'in_progress_' may contain DownloadItems that have not finished the start
314 // complete (from the history service) and thus aren't in downloads_.
315 DownloadMap::iterator it = in_progress_.begin();
316 for (; it != in_progress_.end(); ++it) {
317 DownloadItem* download = it->second;
318 if (download->state() == DownloadItem::IN_PROGRESS) {
319 download->Cancel(false);
320 UpdateHistoryForDownload(download);
321 }
322 if (download->db_handle() == kUninitializedHandle) {
323 // An invalid handle means that 'download' does not yet exist in
324 // 'downloads_', so we have to delete it here.
325 delete download;
326 }
327 }
328
329 in_progress_.clear();
330 STLDeleteValues(&downloads_);
331
332 file_manager_ = NULL;
333
334 // Save our file extensions to auto open.
335 SaveAutoOpens();
336
337 // Make sure the save as dialog doesn't notify us back if we're gone before
338 // it returns.
339 if (select_file_dialog_.get())
340 select_file_dialog_->ListenerDestroyed();
341
342 shutdown_needed_ = false;
343 }
344
345 // Issue a history query for downloads matching 'search_text'. If 'search_text'
346 // is empty, return all downloads that we know about.
347 void DownloadManager::GetDownloads(Observer* observer,
348 const std::wstring& search_text) {
349 DCHECK(observer);
350
351 // Return a empty list if we've not yet received the set of downloads from the
352 // history system (we'll update all observers once we get that list in
353 // OnQueryDownloadEntriesComplete), or if there are no downloads at all.
354 std::vector<DownloadItem*> download_copy;
355 if (downloads_.empty()) {
356 observer->SetDownloads(download_copy);
357 return;
358 }
359
360 // We already know all the downloads and there is no filter, so just return a
361 // copy to the observer.
362 if (search_text.empty()) {
363 download_copy.reserve(downloads_.size());
364 for (DownloadMap::iterator it = downloads_.begin();
365 it != downloads_.end(); ++it) {
366 download_copy.push_back(it->second);
367 }
368
369 // We retain ownership of the DownloadItems.
370 observer->SetDownloads(download_copy);
371 return;
372 }
373
374 // Issue a request to the history service for a list of downloads matching
375 // our search text.
376 HistoryService* hs =
377 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
378 if (hs) {
379 HistoryService::Handle h =
380 hs->SearchDownloads(search_text,
381 &cancelable_consumer_,
382 NewCallback(this,
383 &DownloadManager::OnSearchComplete));
384 cancelable_consumer_.SetClientData(hs, h, observer);
385 }
386 }
387
388 // Query the history service for information about all persisted downloads.
389 bool DownloadManager::Init(Profile* profile) {
390 DCHECK(profile);
391 DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
392 shutdown_needed_ = true;
393
394 profile_ = profile;
395 request_context_ = profile_->GetRequestContext();
396
397 // 'incognito mode' will have access to past downloads, but we won't store
398 // information about new downloads while in that mode.
399 QueryHistoryForDownloads();
400
401 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
402 if (!rdh) {
403 NOTREACHED();
404 return false;
405 }
406
407 file_manager_ = rdh->download_file_manager();
408 if (!file_manager_) {
409 NOTREACHED();
410 return false;
411 }
412
413 file_loop_ = g_browser_process->file_thread()->message_loop();
414 if (!file_loop_) {
415 NOTREACHED();
416 return false;
417 }
418
419 // Get our user preference state.
420 PrefService* prefs = profile_->GetPrefs();
421 DCHECK(prefs);
422 prompt_for_download_.Init(prefs::kPromptForDownload, prefs, NULL);
423
424 download_path_.Init(prefs::kDownloadDefaultDirectory, prefs, NULL);
425
426 // Ensure that the download directory specified in the preferences exists.
427 file_loop_->PostTask(FROM_HERE, NewRunnableMethod(
428 file_manager_, &DownloadFileManager::CreateDirectory, *download_path_));
429
430 // We store any file extension that should be opened automatically at
431 // download completion in this pref.
432 download_util::InitializeExeTypes(&exe_types_);
433
434 std::wstring extensions_to_open =
435 prefs->GetString(prefs::kDownloadExtensionsToOpen);
436 std::vector<std::wstring> extensions;
437 SplitString(extensions_to_open, L':', &extensions);
438 for (size_t i = 0; i < extensions.size(); ++i) {
439 if (!extensions[i].empty() && !IsExecutable(extensions[i]))
440 auto_open_.insert(extensions[i]);
441 }
442
443 return true;
444 }
445
446 void DownloadManager::QueryHistoryForDownloads() {
447 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
448 if (hs) {
449 hs->QueryDownloads(
450 &cancelable_consumer_,
451 NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete));
452 }
453 }
454
455 // We have received a message from DownloadFileManager about a new download. We
456 // create a download item and store it in our download map, and inform the
457 // history system of a new download. Since this method can be called while the
458 // history service thread is still reading the persistent state, we do not
459 // insert the new DownloadItem into 'downloads_' or inform our observers at this
460 // point. OnCreateDatabaseEntryComplete() handles that finalization of the the
461 // download creation as a callback from the history thread.
462 void DownloadManager::StartDownload(DownloadCreateInfo* info) {
463 DCHECK(MessageLoop::current() == ui_loop_);
464 DCHECK(info);
465
466 // Determine the proper path for a download, by choosing either the default
467 // download directory, or prompting the user.
468 std::wstring generated_name;
469 GenerateFilename(info, &generated_name);
470 if (*prompt_for_download_ && !last_download_path_.empty())
471 info->suggested_path = last_download_path_;
472 else
473 info->suggested_path = *download_path_;
474 file_util::AppendToPath(&info->suggested_path, generated_name);
475
476 // We need to move over to the download thread because we don't want to stat
477 // the suggested path on the UI thread.
478 file_loop_->PostTask(FROM_HERE,
479 NewRunnableMethod(this,
480 &DownloadManager::CheckIfSuggestedPathExists,
481 info));
482 }
483
484 void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info) {
485 DCHECK(info);
486
487 // Check writability of the suggested path. If we can't write to it, default
488 // to the user's "My Documents" directory. We'll prompt them in this case.
489 std::wstring path = file_util::GetDirectoryFromPath(info->suggested_path);
490 if (!file_util::PathIsWritable(path)) {
491 info->save_as = true;
492 const std::wstring filename =
493 file_util::GetFilenameFromPath(info->suggested_path);
494 PathService::Get(chrome::DIR_USER_DOCUMENTS, &info->suggested_path);
495 file_util::AppendToPath(&info->suggested_path, filename);
496 }
497
498 info->suggested_path_exists = !UniquifyPath(&info->suggested_path);
499
500 // Now we return to the UI thread.
501 ui_loop_->PostTask(FROM_HERE,
502 NewRunnableMethod(this,
503 &DownloadManager::OnPathExistenceAvailable,
504 info));
505 }
506
507 void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) {
508 DCHECK(MessageLoop::current() == ui_loop_);
509 DCHECK(info);
510
511 if (*prompt_for_download_ || info->save_as || info->suggested_path_exists) {
512 // We must ask the user for the place to put the download.
513 if (!select_file_dialog_.get())
514 select_file_dialog_ = SelectFileDialog::Create(this);
515
516 TabContents* contents = tab_util::GetTabContentsByID(
517 info->render_process_id, info->render_view_id);
518 HWND owning_hwnd =
519 contents ? GetAncestor(contents->GetContainerHWND(), GA_ROOT) : NULL;
520 select_file_dialog_->SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE,
521 std::wstring(), info->suggested_path,
522 owning_hwnd, info);
523 } else {
524 // No prompting for download, just continue with the suggested name.
525 ContinueStartDownload(info, info->suggested_path);
526 }
527 }
528
529 void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info,
530 const std::wstring& target_path) {
531 scoped_ptr<DownloadCreateInfo> infop(info);
532 info->path = target_path;
533
534 DownloadItem* download = NULL;
535 DownloadMap::iterator it = in_progress_.find(info->download_id);
536 if (it == in_progress_.end()) {
537 download = new DownloadItem(info->download_id,
538 info->path,
539 info->url,
540 info->start_time,
541 info->total_bytes,
542 info->render_process_id,
543 info->request_id);
544 download->set_manager(this);
545 in_progress_[info->download_id] = download;
546 } else {
547 NOTREACHED(); // Should not exist!
548 return;
549 }
550
551 // If the download already completed by the time we reached this point, then
552 // notify observers that it did.
553 PendingFinishedMap::iterator pending_it =
554 pending_finished_downloads_.find(info->download_id);
555 if (pending_it != pending_finished_downloads_.end())
556 DownloadFinished(pending_it->first, pending_it->second);
557
558 download->Rename(target_path);
559
560 file_loop_->PostTask(FROM_HERE,
561 NewRunnableMethod(file_manager_,
562 &DownloadFileManager::OnFinalDownloadName,
563 download->id(),
564 target_path));
565
566 if (profile_->IsOffTheRecord()) {
567 // Fake a db handle for incognito mode, since nothing is actually stored in
568 // the database in this mode. We have to make sure that these handles don't
569 // collide with normal db handles, so we use a negative value. Eventually,
570 // they could overlap, but you'd have to do enough downloading that your ISP
571 // would likely stab you in the neck first. YMMV.
572 static int64 fake_db_handle = kUninitializedHandle - 1;
573 OnCreateDownloadEntryComplete(*info, fake_db_handle--);
574 } else {
575 // Update the history system with the new download.
576 // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
577 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
578 if (hs) {
579 hs->CreateDownload(
580 *info, &cancelable_consumer_,
581 NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
582 }
583 }
584 }
585
586 // Convenience function for updating the history service for a download.
587 void DownloadManager::UpdateHistoryForDownload(DownloadItem* download) {
588 DCHECK(download);
589
590 // Don't store info in the database if the download was initiated while in
591 // incognito mode or if it hasn't been initialized in our database table.
592 if (download->db_handle() <= kUninitializedHandle)
593 return;
594
595 // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
596 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
597 if (hs) {
598 hs->UpdateDownload(download->received_bytes(),
599 download->state(),
600 download->db_handle());
601 }
602 }
603
604 void DownloadManager::RemoveDownloadFromHistory(DownloadItem* download) {
605 DCHECK(download);
606 // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
607 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
608 if (download->db_handle() > kUninitializedHandle && hs)
609 hs->RemoveDownload(download->db_handle());
610 }
611
612 void DownloadManager::RemoveDownloadsFromHistoryBetween(const Time remove_begin,
613 const Time remove_end) {
614 // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong.
615 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
616 if (hs)
617 hs->RemoveDownloadsBetween(remove_begin, remove_end);
618 }
619
620 void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
621 DownloadMap::iterator it = in_progress_.find(download_id);
622 if (it != in_progress_.end()) {
623 DownloadItem* download = it->second;
624 download->Update(size);
625 UpdateHistoryForDownload(download);
626 }
627 }
628
629 void DownloadManager::DownloadFinished(int32 download_id, int64 size) {
630 DownloadMap::iterator it = in_progress_.find(download_id);
631 if (it != in_progress_.end()) {
632 // Remove the id from the list of pending ids.
633 PendingFinishedMap::iterator erase_it =
634 pending_finished_downloads_.find(download_id);
635 if (erase_it != pending_finished_downloads_.end())
636 pending_finished_downloads_.erase(erase_it);
637
638 DownloadItem* download = it->second;
639 download->Finished(size);
640
641 // Open the download if the user or user prefs indicate it should be.
642 const std::wstring extension =
643 file_util::GetFileExtensionFromPath(download->full_path());
644 if (download->open_when_complete() || ShouldOpenFileExtension(extension))
645 OpenDownloadInShell(download, NULL);
646
647 // Clean up will happen when the history system create callback runs if we
648 // don't have a valid db_handle yet.
649 if (download->db_handle() != kUninitializedHandle) {
650 in_progress_.erase(it);
651 NotifyAboutDownloadStop();
652 UpdateHistoryForDownload(download);
653 }
654 } else {
655 // The download is done, but the user hasn't selected a final location for
656 // it yet (the Save As dialog box is probably still showing), so just keep
657 // track of the fact that this download id is complete, when the
658 // DownloadItem is constructed later we'll notify its completion then.
659 PendingFinishedMap::iterator erase_it =
660 pending_finished_downloads_.find(download_id);
661 DCHECK(erase_it == pending_finished_downloads_.end());
662 pending_finished_downloads_[download_id] = size;
663 }
664 }
665
666 // static
667 // We have to tell the ResourceDispatcherHost to cancel the download from this
668 // thread, since we can't forward tasks from the file thread to the io thread
669 // reliably (crash on shutdown race condition).
670 void DownloadManager::CancelDownloadRequest(int render_process_id,
671 int request_id) {
672 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
673 base::Thread* io_thread = g_browser_process->io_thread();
674 if (!io_thread || !rdh)
675 return;
676 io_thread->message_loop()->PostTask(FROM_HERE,
677 NewRunnableFunction(&DownloadManager::OnCancelDownloadRequest,
678 rdh,
679 render_process_id,
680 request_id));
681 }
682
683 // static
684 void DownloadManager::OnCancelDownloadRequest(ResourceDispatcherHost* rdh,
685 int render_process_id,
686 int request_id) {
687 rdh->CancelRequest(render_process_id, request_id, false);
688 }
689
690 void DownloadManager::DownloadCancelled(int32 download_id) {
691 DownloadMap::iterator it = in_progress_.find(download_id);
692 if (it == in_progress_.end())
693 return;
694 DownloadItem* download = it->second;
695
696 CancelDownloadRequest(download->render_process_id(), download->request_id());
697
698 // Clean up will happen when the history system create callback runs if we
699 // don't have a valid db_handle yet.
700 if (download->db_handle() != kUninitializedHandle) {
701 in_progress_.erase(it);
702 NotifyAboutDownloadStop();
703 UpdateHistoryForDownload(download);
704 }
705
706 // Tell the file manager to cancel the download.
707 file_manager_->RemoveDownload(download->id(), this); // On the UI thread
708 file_loop_->PostTask(FROM_HERE,
709 NewRunnableMethod(file_manager_,
710 &DownloadFileManager::CancelDownload,
711 download->id()));
712 }
713
714 void DownloadManager::PauseDownload(int32 download_id, bool pause) {
715 DownloadMap::iterator it = in_progress_.find(download_id);
716 if (it != in_progress_.end()) {
717 DownloadItem* download = it->second;
718 if (pause == download->is_paused())
719 return;
720
721 // Inform the ResourceDispatcherHost of the new pause state.
722 base::Thread* io_thread = g_browser_process->io_thread();
723 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
724 if (!io_thread || !rdh)
725 return;
726
727 io_thread->message_loop()->PostTask(FROM_HERE,
728 NewRunnableFunction(&DownloadManager::OnPauseDownloadRequest,
729 rdh,
730 download->render_process_id(),
731 download->request_id(),
732 pause));
733 }
734 }
735
736 // static
737 void DownloadManager::OnPauseDownloadRequest(ResourceDispatcherHost* rdh,
738 int render_process_id,
739 int request_id,
740 bool pause) {
741 rdh->PauseRequest(render_process_id, request_id, pause);
742 }
743
744 void DownloadManager::RemoveDownload(int64 download_handle) {
745 DownloadMap::iterator it = downloads_.find(download_handle);
746 if (it == downloads_.end())
747 return;
748
749 // Make history update.
750 DownloadItem* download = it->second;
751 RemoveDownloadFromHistory(download);
752
753 // Remove from our tables and delete.
754 downloads_.erase(it);
755 delete download;
756
757 // Tell observers to refresh their views.
758 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
759 }
760
761 int DownloadManager::RemoveDownloadsBetween(const Time remove_begin,
762 const Time remove_end) {
763 RemoveDownloadsFromHistoryBetween(remove_begin, remove_end);
764
765 int num_deleted = 0;
766 DownloadMap::iterator it = downloads_.begin();
767 while (it != downloads_.end()) {
768 DownloadItem* download = it->second;
769 DownloadItem::DownloadState state = download->state();
770 if (download->start_time() >= remove_begin &&
771 (remove_end.is_null() || download->start_time() < remove_end) &&
772 (state == DownloadItem::COMPLETE ||
773 state == DownloadItem::CANCELLED)) {
774 // Remove from the map and move to the next in the list.
775 it = downloads_.erase(it);
776 delete download;
777
778 ++num_deleted;
779 continue;
780 }
781
782 ++it;
783 }
784
785 // Tell observers to refresh their views.
786 if (num_deleted > 0)
787 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
788
789 return num_deleted;
790 }
791
792 int DownloadManager::RemoveDownloads(const Time remove_begin) {
793 return RemoveDownloadsBetween(remove_begin, Time());
794 }
795
796 // Initiate a download of a specific URL. We send the request to the
797 // ResourceDispatcherHost, and let it send us responses like a regular
798 // download.
799 void DownloadManager::DownloadUrl(const GURL& url,
800 const GURL& referrer,
801 WebContents* web_contents) {
802 DCHECK(web_contents);
803 file_manager_->DownloadUrl(url,
804 referrer,
805 web_contents->process()->host_id(),
806 web_contents->render_view_host()->routing_id(),
807 request_context_.get());
808 }
809
810 void DownloadManager::NotifyAboutDownloadStart() {
811 NotificationService::current()->
812 Notify(NOTIFY_DOWNLOAD_START, NotificationService::AllSources(),
813 NotificationService::NoDetails());
814 }
815
816 void DownloadManager::NotifyAboutDownloadStop() {
817 NotificationService::current()->
818 Notify(NOTIFY_DOWNLOAD_STOP, NotificationService::AllSources(),
819 NotificationService::NoDetails());
820 }
821
822 void DownloadManager::GenerateExtension(const std::wstring& file_name,
823 const std::string& mime_type,
824 std::wstring* generated_extension) {
825 // We're worried about three things here:
826 //
827 // 1) Security. Many sites let users upload content, such as buddy icons, to
828 // their web sites. We want to mitigate the case where an attacker
829 // supplies a malicious executable with an executable file extension but an
830 // honest site serves the content with a benign content type, such as
831 // image/jpeg.
832 //
833 // 2) Usability. If the site fails to provide a file extension, we want to
834 // guess a reasonable file extension based on the content type.
835 //
836 // 3) Shell integration. Some file extensions automatically integrate with
837 // the shell. We block these extensions to prevent a malicious web site
838 // from integrating with the user's shell.
839
840 static const wchar_t default_extension[] = L"download";
841
842 // See if our file name already contains an extension.
843 std::wstring extension(file_util::GetFileExtensionFromPath(file_name));
844
845 // Rename shell-integrated extensions.
846 if (win_util::IsShellIntegratedExtension(extension))
847 extension.assign(default_extension);
848
849 std::string mime_type_from_extension;
850 net::GetMimeTypeFromFile(file_name, &mime_type_from_extension);
851 if (mime_type == mime_type_from_extension) {
852 // The hinted extension matches the mime type. It looks like a winner.
853 generated_extension->swap(extension);
854 return;
855 }
856
857 if (IsExecutable(extension) && !IsExecutableMimeType(mime_type)) {
858 // We want to be careful about executable extensions. The worry here is
859 // that a trusted web site could be tricked into dropping an executable file
860 // on the user's filesystem.
861 if (!net::GetPreferredExtensionForMimeType(mime_type, &extension)) {
862 // We couldn't find a good extension for this content type. Use a dummy
863 // extension instead.
864 extension.assign(default_extension);
865 }
866 }
867
868 if (extension.empty()) {
869 net::GetPreferredExtensionForMimeType(mime_type, &extension);
870 } else {
871 // Append entension generated from the mime type if:
872 // 1. New extension is not ".txt"
873 // 2. New extension is not the same as the already existing extension.
874 // 3. New extension is not executable. This action mitigates the case when
875 // an execuatable is hidden in a benign file extension;
876 // E.g. my-cat.jpg becomes my-cat.jpg.js if content type is
877 // application/x-javascript.
878 std::wstring append_extension;
879 if (net::GetPreferredExtensionForMimeType(mime_type, &append_extension)) {
880 if (append_extension != L".txt" && append_extension != extension &&
881 !IsExecutable(append_extension))
882 extension += append_extension;
883 }
884 }
885
886 generated_extension->swap(extension);
887 }
888
889 void DownloadManager::GenerateFilename(DownloadCreateInfo* info,
890 std::wstring* generated_name) {
891 std::wstring file_name =
892 net::GetSuggestedFilename(GURL(info->url),
893 info->content_disposition,
894 L"download");
895 DCHECK(!file_name.empty());
896
897 // Make sure we get the right file extension.
898 std::wstring extension;
899 GenerateExtension(file_name, info->mime_type, &extension);
900 file_util::ReplaceExtension(&file_name, extension);
901
902 // Prepend "_" to the file name if it's a reserved name
903 if (win_util::IsReservedName(file_name))
904 file_name = std::wstring(L"_") + file_name;
905
906 generated_name->assign(file_name);
907 }
908
909 void DownloadManager::AddObserver(Observer* observer) {
910 observers_.AddObserver(observer);
911 observer->ModelChanged();
912 }
913
914 void DownloadManager::RemoveObserver(Observer* observer) {
915 observers_.RemoveObserver(observer);
916 }
917
918 // Post Windows Shell operations to the Download thread, to avoid blocking the
919 // user interface.
920 void DownloadManager::ShowDownloadInShell(const DownloadItem* download) {
921 DCHECK(file_manager_);
922 file_loop_->PostTask(FROM_HERE,
923 NewRunnableMethod(file_manager_,
924 &DownloadFileManager::OnShowDownloadInShell,
925 download->full_path()));
926 }
927
928 void DownloadManager::OpenDownloadInShell(const DownloadItem* download,
929 HWND parent_window) {
930 DCHECK(file_manager_);
931 file_loop_->PostTask(FROM_HERE,
932 NewRunnableMethod(file_manager_,
933 &DownloadFileManager::OnOpenDownloadInShell,
934 download->full_path(), download->url(), parent_window));
935 }
936
937 void DownloadManager::OpenFilesOfExtension(const std::wstring& extension,
938 bool open) {
939 if (open && !IsExecutable(extension))
940 auto_open_.insert(extension);
941 else
942 auto_open_.erase(extension);
943 SaveAutoOpens();
944 }
945
946 bool DownloadManager::ShouldOpenFileExtension(const std::wstring& extension) {
947 if (!IsExecutable(extension) &&
948 auto_open_.find(extension) != auto_open_.end())
949 return true;
950 return false;
951 }
952
953 // static
954 bool DownloadManager::IsExecutableMimeType(const std::string& mime_type) {
955 // JavaScript is just as powerful as EXE.
956 if (net::MatchesMimeType("text/javascript", mime_type))
957 return true;
958 if (net::MatchesMimeType("text/javascript;version=*", mime_type))
959 return true;
960
961 // We don't consider other non-application types to be executable.
962 if (!net::MatchesMimeType("application/*", mime_type))
963 return false;
964
965 // These application types are not executable.
966 if (net::MatchesMimeType("application/*+xml", mime_type))
967 return false;
968 if (net::MatchesMimeType("application/xml", mime_type))
969 return false;
970
971 return true;
972 }
973
974 bool DownloadManager::IsExecutable(const std::wstring& extension) {
975 return exe_types_.find(extension) != exe_types_.end();
976 }
977
978 void DownloadManager::ResetAutoOpenFiles() {
979 auto_open_.clear();
980 SaveAutoOpens();
981 }
982
983 bool DownloadManager::HasAutoOpenFileTypesRegistered() const {
984 return !auto_open_.empty();
985 }
986
987 void DownloadManager::SaveAutoOpens() {
988 PrefService* prefs = profile_->GetPrefs();
989 if (prefs) {
990 std::wstring extensions;
991 for (std::set<std::wstring>::iterator it = auto_open_.begin();
992 it != auto_open_.end(); ++it) {
993 extensions += *it + L":";
994 }
995 if (!extensions.empty())
996 extensions.erase(extensions.size() - 1);
997 prefs->SetString(prefs::kDownloadExtensionsToOpen, extensions);
998 }
999 }
1000
1001 void DownloadManager::FileSelected(const std::wstring& path, void* params) {
1002 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
1003 if (*prompt_for_download_)
1004 last_download_path_ = file_util::GetDirectoryFromPath(path);
1005 ContinueStartDownload(info, path);
1006 }
1007
1008 void DownloadManager::FileSelectionCanceled(void* params) {
1009 // The user didn't pick a place to save the file, so need to cancel the
1010 // download that's already in progress to the temporary location.
1011 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params);
1012 file_loop_->PostTask(FROM_HERE,
1013 NewRunnableMethod(file_manager_, &DownloadFileManager::CancelDownload,
1014 info->download_id));
1015 }
1016
1017 // Operations posted to us from the history service ----------------------------
1018
1019 // The history service has retrieved all download entries. 'entries' contains
1020 // 'DownloadCreateInfo's in sorted order (by ascending start_time).
1021 void DownloadManager::OnQueryDownloadEntriesComplete(
1022 std::vector<DownloadCreateInfo>* entries) {
1023 for (size_t i = 0; i < entries->size(); ++i) {
1024 DownloadItem* download = new DownloadItem(entries->at(i));
1025 DCHECK(downloads_.find(download->db_handle()) == downloads_.end());
1026 downloads_[download->db_handle()] = download;
1027 download->set_manager(this);
1028 }
1029 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
1030 }
1031
1032
1033 // Once the new DownloadItem's creation info has been committed to the history
1034 // service, we associate the DownloadItem with the db handle, update our
1035 // 'downloads_' map and inform observers.
1036 void DownloadManager::OnCreateDownloadEntryComplete(DownloadCreateInfo info,
1037 int64 db_handle) {
1038 DownloadMap::iterator it = in_progress_.find(info.download_id);
1039 DCHECK(it != in_progress_.end());
1040
1041 DownloadItem* download = it->second;
1042 DCHECK(download->db_handle() == kUninitializedHandle);
1043 download->set_db_handle(db_handle);
1044
1045 // Insert into our full map.
1046 DCHECK(downloads_.find(download->db_handle()) == downloads_.end());
1047 downloads_[download->db_handle()] = download;
1048
1049 // The 'contents' may no longer exist if the user closed the tab before we get
1050 // this start completion event. If it does, tell the origin WebContents to
1051 // display its download shelf.
1052 TabContents* contents =
1053 tab_util::GetTabContentsByID(info.render_process_id, info.render_view_id);
1054
1055 // If the contents no longer exists or is no longer active, we start the
1056 // download in the last active browser. This is not ideal but better than
1057 // fully hiding the download from the user. Note: non active means that the
1058 // user navigated away from the tab contents. This has nothing to do with
1059 // tab selection.
1060 if (!contents || !contents->is_active()) {
1061 Browser* last_active = BrowserList::GetLastActive();
1062 if (last_active)
1063 contents = last_active->GetSelectedTabContents();
1064 }
1065
1066 if (contents)
1067 contents->OnStartDownload(download);
1068
1069 // Inform interested objects about the new download.
1070 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
1071 NotifyAboutDownloadStart();
1072
1073 // If this download has been completed before we've received the db handle,
1074 // post one final message to the history service so that it can be properly
1075 // in sync with the DownloadItem's completion status, and also inform any
1076 // observers so that they get more than just the start notification.
1077 if (download->state() != DownloadItem::IN_PROGRESS) {
1078 in_progress_.erase(it);
1079 NotifyAboutDownloadStop();
1080 UpdateHistoryForDownload(download);
1081 download->UpdateObservers();
1082 }
1083 }
1084
1085 // Called when the history service has retrieved the list of downloads that
1086 // match the search text.
1087 void DownloadManager::OnSearchComplete(HistoryService::Handle handle,
1088 std::vector<int64>* results) {
1089 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
1090 Observer* requestor = cancelable_consumer_.GetClientData(hs, handle);
1091 if (!requestor)
1092 return;
1093
1094 std::vector<DownloadItem*> searched_downloads;
1095 for (std::vector<int64>::iterator it = results->begin();
1096 it != results->end(); ++it) {
1097 DownloadMap::iterator dit = downloads_.find(*it);
1098 if (dit != downloads_.end())
1099 searched_downloads.push_back(dit->second);
1100 }
1101
1102 requestor->SetDownloads(searched_downloads);
1103 }
1104
OLDNEW
« no previous file with comments | « chrome/browser/download_manager.h ('k') | chrome/browser/download_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698