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

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

Issue 7618048: Move the core download files to content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 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
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "chrome/browser/download/download_manager.h"
6
7 #include "base/callback.h"
8 #include "base/file_util.h"
9 #include "base/i18n/case_conversion.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/task.h"
13 #include "build/build_config.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/download/download_create_info.h"
16 #include "chrome/browser/download/download_file_manager.h"
17 #include "chrome/browser/download/download_history.h"
18 #include "chrome/browser/download/download_item.h"
19 #include "chrome/browser/download/download_manager_delegate.h"
20 #include "chrome/browser/download/download_prefs.h"
21 #include "chrome/browser/download/download_request_handle.h"
22 #include "chrome/browser/download/download_status_updater.h"
23 #include "chrome/browser/download/download_util.h"
24 #include "chrome/browser/history/download_history_info.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "content/browser/browser_thread.h"
27 #include "content/browser/renderer_host/render_process_host.h"
28 #include "content/browser/renderer_host/render_view_host.h"
29 #include "content/browser/renderer_host/resource_dispatcher_host.h"
30 #include "content/browser/tab_contents/tab_contents.h"
31 #include "content/common/content_notification_types.h"
32 #include "content/common/notification_service.h"
33
34 DownloadManager::DownloadManager(DownloadManagerDelegate* delegate,
35 DownloadStatusUpdater* status_updater)
36 : shutdown_needed_(false),
37 profile_(NULL),
38 file_manager_(NULL),
39 status_updater_(status_updater->AsWeakPtr()),
40 next_save_page_id_(0),
41 delegate_(delegate) {
42 if (status_updater_)
43 status_updater_->AddDelegate(this);
44 }
45
46 DownloadManager::~DownloadManager() {
47 DCHECK(!shutdown_needed_);
48 if (status_updater_)
49 status_updater_->RemoveDelegate(this);
50 }
51
52 void DownloadManager::Shutdown() {
53 VLOG(20) << __FUNCTION__ << "()"
54 << " shutdown_needed_ = " << shutdown_needed_;
55 if (!shutdown_needed_)
56 return;
57 shutdown_needed_ = false;
58
59 FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown());
60
61 if (file_manager_) {
62 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
63 NewRunnableMethod(file_manager_,
64 &DownloadFileManager::OnDownloadManagerShutdown,
65 make_scoped_refptr(this)));
66 }
67
68 AssertContainersConsistent();
69
70 // Go through all downloads in downloads_. Dangerous ones we need to
71 // remove on disk, and in progress ones we need to cancel.
72 for (DownloadSet::iterator it = downloads_.begin(); it != downloads_.end();) {
73 DownloadItem* download = *it;
74
75 // Save iterator from potential erases in this set done by called code.
76 // Iterators after an erasure point are still valid for lists and
77 // associative containers such as sets.
78 it++;
79
80 if (download->safety_state() == DownloadItem::DANGEROUS &&
81 download->IsPartialDownload()) {
82 // The user hasn't accepted it, so we need to remove it
83 // from the disk. This may or may not result in it being
84 // removed from the DownloadManager queues and deleted
85 // (specifically, DownloadManager::RemoveDownload only
86 // removes and deletes it if it's known to the history service)
87 // so the only thing we know after calling this function is that
88 // the download was deleted if-and-only-if it was removed
89 // from all queues.
90 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN);
91 } else if (download->IsPartialDownload()) {
92 download->Cancel(false);
93 download_history_->UpdateEntry(download);
94 }
95 }
96
97 // At this point, all dangerous downloads have had their files removed
98 // and all in progress downloads have been cancelled. We can now delete
99 // anything left.
100
101 // Copy downloads_ to separate container so as not to set off checks
102 // in DownloadItem destruction.
103 DownloadSet downloads_to_delete;
104 downloads_to_delete.swap(downloads_);
105
106 in_progress_.clear();
107 active_downloads_.clear();
108 history_downloads_.clear();
109 STLDeleteElements(&downloads_to_delete);
110
111 DCHECK(save_page_downloads_.empty());
112
113 file_manager_ = NULL;
114
115 download_history_.reset();
116 download_prefs_.reset();
117
118 shutdown_needed_ = false;
119 }
120
121 void DownloadManager::GetTemporaryDownloads(
122 const FilePath& dir_path, DownloadVector* result) {
123 DCHECK(result);
124
125 for (DownloadMap::iterator it = history_downloads_.begin();
126 it != history_downloads_.end(); ++it) {
127 if (it->second->is_temporary() &&
128 it->second->full_path().DirName() == dir_path)
129 result->push_back(it->second);
130 }
131 }
132
133 void DownloadManager::GetAllDownloads(
134 const FilePath& dir_path, DownloadVector* result) {
135 DCHECK(result);
136
137 for (DownloadMap::iterator it = history_downloads_.begin();
138 it != history_downloads_.end(); ++it) {
139 if (!it->second->is_temporary() &&
140 (dir_path.empty() || it->second->full_path().DirName() == dir_path))
141 result->push_back(it->second);
142 }
143 }
144
145 void DownloadManager::GetCurrentDownloads(
146 const FilePath& dir_path, DownloadVector* result) {
147 DCHECK(result);
148
149 for (DownloadMap::iterator it = history_downloads_.begin();
150 it != history_downloads_.end(); ++it) {
151 DownloadItem* item =it->second;
152 // Skip temporary items.
153 if (item->is_temporary())
154 continue;
155 // Skip items that have all their data, and are OK to save.
156 if (!item->IsPartialDownload() &&
157 (item->safety_state() != DownloadItem::DANGEROUS))
158 continue;
159 // Skip items that don't match |dir_path|.
160 // If |dir_path| is empty, all remaining items match.
161 if (!dir_path.empty() && (it->second->full_path().DirName() != dir_path))
162 continue;
163
164 result->push_back(item);
165 }
166
167 // If we have a parent profile, let it add its downloads to the results.
168 Profile* original_profile = profile_->GetOriginalProfile();
169 if (original_profile != profile_)
170 original_profile->GetDownloadManager()->GetCurrentDownloads(dir_path,
171 result);
172 }
173
174 void DownloadManager::SearchDownloads(const string16& query,
175 DownloadVector* result) {
176 DCHECK(result);
177
178 string16 query_lower(base::i18n::ToLower(query));
179
180 for (DownloadMap::iterator it = history_downloads_.begin();
181 it != history_downloads_.end(); ++it) {
182 DownloadItem* download_item = it->second;
183
184 if (download_item->is_temporary() || download_item->is_extension_install())
185 continue;
186
187 // Display Incognito downloads only in Incognito window, and vice versa.
188 // The Incognito Downloads page will get the list of non-Incognito downloads
189 // from its parent profile.
190 if (profile_->IsOffTheRecord() != download_item->is_otr())
191 continue;
192
193 if (download_item->MatchesQuery(query_lower))
194 result->push_back(download_item);
195 }
196
197 // If we have a parent profile, let it add its downloads to the results.
198 Profile* original_profile = profile_->GetOriginalProfile();
199 if (original_profile != profile_)
200 original_profile->GetDownloadManager()->SearchDownloads(query, result);
201 }
202
203 // Query the history service for information about all persisted downloads.
204 bool DownloadManager::Init(Profile* profile) {
205 DCHECK(profile);
206 DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
207 shutdown_needed_ = true;
208
209 profile_ = profile;
210 download_history_.reset(new DownloadHistory(profile));
211 download_history_->Load(
212 NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete));
213
214 download_prefs_.reset(new DownloadPrefs(profile_->GetPrefs()));
215
216 // In test mode, there may be no ResourceDispatcherHost. In this case it's
217 // safe to avoid setting |file_manager_| because we only call a small set of
218 // functions, none of which need it.
219 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
220 if (rdh) {
221 file_manager_ = rdh->download_file_manager();
222 DCHECK(file_manager_);
223 }
224
225 other_download_manager_observer_.reset(
226 new OtherDownloadManagerObserver(this));
227
228 return true;
229 }
230
231 // We have received a message from DownloadFileManager about a new download.
232 void DownloadManager::StartDownload(int32 download_id) {
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234
235 if (delegate_->ShouldStartDownload(download_id))
236 RestartDownload(download_id);
237 }
238
239 void DownloadManager::CheckForHistoryFilesRemoval() {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
241 for (DownloadMap::iterator it = history_downloads_.begin();
242 it != history_downloads_.end(); ++it) {
243 CheckForFileRemoval(it->second);
244 }
245 }
246
247 void DownloadManager::CheckForFileRemoval(DownloadItem* download_item) {
248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
249 if (download_item->IsComplete() &&
250 !download_item->file_externally_removed()) {
251 BrowserThread::PostTask(
252 BrowserThread::FILE, FROM_HERE,
253 NewRunnableMethod(this,
254 &DownloadManager::CheckForFileRemovalOnFileThread,
255 download_item->db_handle(),
256 download_item->GetTargetFilePath()));
257 }
258 }
259
260 void DownloadManager::CheckForFileRemovalOnFileThread(
261 int64 db_handle, const FilePath& path) {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
263 if (!file_util::PathExists(path)) {
264 BrowserThread::PostTask(
265 BrowserThread::UI, FROM_HERE,
266 NewRunnableMethod(this,
267 &DownloadManager::OnFileRemovalDetected,
268 db_handle));
269 }
270 }
271
272 void DownloadManager::OnFileRemovalDetected(int64 db_handle) {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
274 DownloadMap::iterator it = history_downloads_.find(db_handle);
275 if (it != history_downloads_.end()) {
276 DownloadItem* download_item = it->second;
277 download_item->OnDownloadedFileRemoved();
278 }
279 }
280
281 void DownloadManager::RestartDownload(
282 int32 download_id) {
283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
284
285 DownloadItem* download = GetActiveDownloadItem(download_id);
286 if (!download)
287 return;
288
289 VLOG(20) << __FUNCTION__ << "()"
290 << " download = " << download->DebugString(true);
291
292 FilePath suggested_path = download->suggested_path();
293
294 if (download->prompt_user_for_save_location()) {
295 // We must ask the user for the place to put the download.
296 DownloadRequestHandle request_handle = download->request_handle();
297 TabContents* contents = request_handle.GetTabContents();
298
299 // |id_ptr| will be deleted in either FileSelected() or
300 // FileSelectionCancelled().
301 int32* id_ptr = new int32;
302 *id_ptr = download_id;
303
304 delegate_->ChooseDownloadPath(
305 contents, suggested_path, reinterpret_cast<void*>(id_ptr));
306
307 FOR_EACH_OBSERVER(Observer, observers_,
308 SelectFileDialogDisplayed(download_id));
309 } else {
310 // No prompting for download, just continue with the suggested name.
311 ContinueDownloadWithPath(download, suggested_path);
312 }
313 }
314
315 void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) {
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
317
318 DownloadItem* download = new DownloadItem(this, *info,
319 profile_->IsOffTheRecord());
320 int32 download_id = info->download_id;
321 DCHECK(!ContainsKey(in_progress_, download_id));
322 DCHECK(!ContainsKey(active_downloads_, download_id));
323 downloads_.insert(download);
324 active_downloads_[download_id] = download;
325 }
326
327 void DownloadManager::ContinueDownloadWithPath(DownloadItem* download,
328 const FilePath& chosen_file) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
330 DCHECK(download);
331
332 int32 download_id = download->id();
333
334 // NOTE(ahendrickson) Eventually |active_downloads_| will replace
335 // |in_progress_|, but we don't want to change the semantics yet.
336 DCHECK(!ContainsKey(in_progress_, download_id));
337 DCHECK(ContainsKey(downloads_, download));
338 DCHECK(ContainsKey(active_downloads_, download_id));
339
340 // Make sure the initial file name is set only once.
341 DCHECK(download->full_path().empty());
342 download->OnPathDetermined(chosen_file);
343
344 VLOG(20) << __FUNCTION__ << "()"
345 << " download = " << download->DebugString(true);
346
347 in_progress_[download_id] = download;
348 UpdateAppIcon(); // Reflect entry into in_progress_.
349
350 // Rename to intermediate name.
351 FilePath download_path;
352 if (download->IsDangerous()) {
353 // The download is not safe. We can now rename the file to its
354 // tentative name using RenameInProgressDownloadFile.
355 // NOTE: The |Rename| below will be a no-op for dangerous files, as we're
356 // renaming it to the same name.
357 download_path = download->full_path();
358 } else {
359 // The download is a safe download. We need to
360 // rename it to its intermediate '.crdownload' path. The final
361 // name after user confirmation will be set from
362 // DownloadItem::OnDownloadCompleting.
363 download_path =
364 download_util::GetCrDownloadPath(download->full_path());
365 }
366
367 BrowserThread::PostTask(
368 BrowserThread::FILE, FROM_HERE,
369 NewRunnableMethod(
370 file_manager_, &DownloadFileManager::RenameInProgressDownloadFile,
371 download->id(), download_path));
372
373 download->Rename(download_path);
374
375 download_history_->AddEntry(download,
376 NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
377 }
378
379 void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
381 DownloadMap::iterator it = active_downloads_.find(download_id);
382 if (it != active_downloads_.end()) {
383 DownloadItem* download = it->second;
384 if (download->IsInProgress()) {
385 download->Update(size);
386 UpdateAppIcon(); // Reflect size updates.
387 download_history_->UpdateEntry(download);
388 }
389 }
390 }
391
392 void DownloadManager::OnResponseCompleted(int32 download_id,
393 int64 size,
394 int os_error,
395 const std::string& hash) {
396 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
397 // ERR_CONNECTION_CLOSED is allowed since a number of servers in the wild
398 // advertise a larger Content-Length than the amount of bytes in the message
399 // body, and then close the connection. Other browsers - IE8, Firefox 4.0.1,
400 // and Safari 5.0.4 - treat the download as complete in this case, so we
401 // follow their lead.
402 if (os_error == 0 || os_error == net::ERR_CONNECTION_CLOSED) {
403 OnAllDataSaved(download_id, size, hash);
404 } else {
405 OnDownloadError(download_id, size, os_error);
406 }
407 }
408
409 void DownloadManager::OnAllDataSaved(int32 download_id,
410 int64 size,
411 const std::string& hash) {
412 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
413 << " size = " << size;
414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415
416 // If it's not in active_downloads_, that means it was cancelled; just
417 // ignore the notification.
418 if (active_downloads_.count(download_id) == 0)
419 return;
420
421 DownloadItem* download = active_downloads_[download_id];
422 download->OnAllDataSaved(size);
423
424 MaybeCompleteDownload(download);
425 }
426
427 void DownloadManager::AssertQueueStateConsistent(DownloadItem* download) {
428 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
429 if (download->state() == DownloadItem::REMOVING) {
430 CHECK(!ContainsKey(downloads_, download));
431 CHECK(!ContainsKey(active_downloads_, download->id()));
432 CHECK(!ContainsKey(in_progress_, download->id()));
433 CHECK(!ContainsKey(history_downloads_, download->db_handle()));
434 return;
435 }
436
437 // Should be in downloads_ if we're not REMOVING.
438 CHECK(ContainsKey(downloads_, download));
439
440 // Check history_downloads_ consistency.
441 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
442 CHECK(ContainsKey(history_downloads_, download->db_handle()));
443 } else {
444 // TODO(rdsmith): Somewhat painful; make sure to disable in
445 // release builds after resolution of http://crbug.com/85408.
446 for (DownloadMap::iterator it = history_downloads_.begin();
447 it != history_downloads_.end(); ++it) {
448 CHECK(it->second != download);
449 }
450 }
451
452 CHECK(ContainsKey(active_downloads_, download->id()) ==
453 (download->state() == DownloadItem::IN_PROGRESS));
454 CHECK(ContainsKey(in_progress_, download->id()) ==
455 (download->state() == DownloadItem::IN_PROGRESS));
456 }
457
458 bool DownloadManager::IsDownloadReadyForCompletion(DownloadItem* download) {
459 // If we don't have all the data, the download is not ready for
460 // completion.
461 if (!download->all_data_saved())
462 return false;
463
464 // If the download is dangerous, but not yet validated, it's not ready for
465 // completion.
466 if (download->safety_state() == DownloadItem::DANGEROUS)
467 return false;
468
469 // If the download isn't active (e.g. has been cancelled) it's not
470 // ready for completion.
471 if (active_downloads_.count(download->id()) == 0)
472 return false;
473
474 // If the download hasn't been inserted into the history system
475 // (which occurs strictly after file name determination, intermediate
476 // file rename, and UI display) then it's not ready for completion.
477 if (download->db_handle() == DownloadHistory::kUninitializedHandle)
478 return false;
479
480 return true;
481 }
482
483 void DownloadManager::MaybeCompleteDownload(DownloadItem* download) {
484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
485 VLOG(20) << __FUNCTION__ << "()" << " download = "
486 << download->DebugString(false);
487
488 if (!IsDownloadReadyForCompletion(download))
489 return;
490
491 // TODO(rdsmith): DCHECK that we only pass through this point
492 // once per download. The natural way to do this is by a state
493 // transition on the DownloadItem.
494
495 // Confirm we're in the proper set of states to be here;
496 // in in_progress_, have all data, have a history handle, (validated or safe).
497 DCHECK_NE(DownloadItem::DANGEROUS, download->safety_state());
498 DCHECK_EQ(1u, in_progress_.count(download->id()));
499 DCHECK(download->all_data_saved());
500 DCHECK(download->db_handle() != DownloadHistory::kUninitializedHandle);
501 DCHECK_EQ(1u, history_downloads_.count(download->db_handle()));
502
503 VLOG(20) << __FUNCTION__ << "()" << " executing: download = "
504 << download->DebugString(false);
505
506 // Remove the id from in_progress
507 in_progress_.erase(download->id());
508 UpdateAppIcon(); // Reflect removal from in_progress_.
509
510 download_history_->UpdateEntry(download);
511
512 // Finish the download.
513 download->OnDownloadCompleting(file_manager_);
514 }
515
516 void DownloadManager::DownloadCompleted(int32 download_id) {
517 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
518 DownloadItem* download = GetDownloadItem(download_id);
519 DCHECK(download);
520 download_history_->UpdateEntry(download);
521 active_downloads_.erase(download_id);
522 }
523
524 void DownloadManager::OnDownloadRenamedToFinalName(int download_id,
525 const FilePath& full_path,
526 int uniquifier) {
527 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
528 << " full_path = \"" << full_path.value() << "\""
529 << " uniquifier = " << uniquifier;
530 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
531
532 DownloadItem* item = GetDownloadItem(download_id);
533 if (!item)
534 return;
535
536 if (item->safety_state() == DownloadItem::SAFE) {
537 DCHECK_EQ(0, uniquifier) << "We should not uniquify SAFE downloads twice";
538 }
539
540 BrowserThread::PostTask(
541 BrowserThread::FILE, FROM_HERE,
542 NewRunnableMethod(
543 file_manager_, &DownloadFileManager::CompleteDownload, download_id));
544
545 if (uniquifier)
546 item->set_path_uniquifier(uniquifier);
547
548 item->OnDownloadRenamedToFinalName(full_path);
549 download_history_->UpdateDownloadPath(item, full_path);
550 }
551
552 void DownloadManager::DownloadCancelled(int32 download_id) {
553 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
554 DownloadMap::iterator it = in_progress_.find(download_id);
555 if (it == in_progress_.end())
556 return;
557 DownloadItem* download = it->second;
558
559 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
560 << " download = " << download->DebugString(true);
561
562 // Clean up will happen when the history system create callback runs if we
563 // don't have a valid db_handle yet.
564 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
565 in_progress_.erase(it);
566 active_downloads_.erase(download_id);
567 UpdateAppIcon(); // Reflect removal from in_progress_.
568 download_history_->UpdateEntry(download);
569 }
570
571 DownloadCancelledInternal(download_id, download->request_handle());
572 }
573
574 void DownloadManager::DownloadCancelledInternal(
575 int download_id, const DownloadRequestHandle& request_handle) {
576 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
577 request_handle.CancelRequest();
578
579 BrowserThread::PostTask(
580 BrowserThread::FILE, FROM_HERE,
581 NewRunnableMethod(
582 file_manager_, &DownloadFileManager::CancelDownload, download_id));
583 }
584
585 void DownloadManager::OnDownloadError(int32 download_id,
586 int64 size,
587 int os_error) {
588 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
589 DownloadMap::iterator it = active_downloads_.find(download_id);
590 // A cancel at the right time could remove the download from the
591 // |active_downloads_| map before we get here.
592 if (it == active_downloads_.end())
593 return;
594
595 DownloadItem* download = it->second;
596
597 VLOG(20) << __FUNCTION__ << "()" << " Error " << os_error
598 << " at offset " << download->received_bytes()
599 << " for download = " << download->DebugString(true);
600
601 download->Interrupted(size, os_error);
602
603 // TODO(ahendrickson) - Remove this when we add resuming of interrupted
604 // downloads, as we will keep the download item around in that case.
605 //
606 // Clean up will happen when the history system create callback runs if we
607 // don't have a valid db_handle yet.
608 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
609 in_progress_.erase(download_id);
610 active_downloads_.erase(download_id);
611 UpdateAppIcon(); // Reflect removal from in_progress_.
612 download_history_->UpdateEntry(download);
613 }
614
615 BrowserThread::PostTask(
616 BrowserThread::FILE, FROM_HERE,
617 NewRunnableMethod(
618 file_manager_, &DownloadFileManager::CancelDownload, download_id));
619 }
620
621 void DownloadManager::UpdateAppIcon() {
622 if (status_updater_)
623 status_updater_->Update();
624 }
625
626 int DownloadManager::RemoveDownloadItems(
627 const DownloadVector& pending_deletes) {
628 if (pending_deletes.empty())
629 return 0;
630
631 // Delete from internal maps.
632 for (DownloadVector::const_iterator it = pending_deletes.begin();
633 it != pending_deletes.end();
634 ++it) {
635 DownloadItem* download = *it;
636 DCHECK(download);
637 history_downloads_.erase(download->db_handle());
638 save_page_downloads_.erase(download->id());
639 downloads_.erase(download);
640 }
641
642 // Tell observers to refresh their views.
643 NotifyModelChanged();
644
645 // Delete the download items themselves.
646 const int num_deleted = static_cast<int>(pending_deletes.size());
647 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
648 return num_deleted;
649 }
650
651 void DownloadManager::RemoveDownload(int64 download_handle) {
652 DownloadMap::iterator it = history_downloads_.find(download_handle);
653 if (it == history_downloads_.end())
654 return;
655
656 // Make history update.
657 DownloadItem* download = it->second;
658 download_history_->RemoveEntry(download);
659
660 // Remove from our tables and delete.
661 int downloads_count = RemoveDownloadItems(DownloadVector(1, download));
662 DCHECK_EQ(1, downloads_count);
663 }
664
665 int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin,
666 const base::Time remove_end) {
667 download_history_->RemoveEntriesBetween(remove_begin, remove_end);
668
669 // All downloads visible to the user will be in the history,
670 // so scan that map.
671 DownloadVector pending_deletes;
672 for (DownloadMap::const_iterator it = history_downloads_.begin();
673 it != history_downloads_.end();
674 ++it) {
675 DownloadItem* download = it->second;
676 if (download->start_time() >= remove_begin &&
677 (remove_end.is_null() || download->start_time() < remove_end) &&
678 (download->IsComplete() || download->IsCancelled())) {
679 AssertQueueStateConsistent(download);
680 pending_deletes.push_back(download);
681 }
682 }
683 return RemoveDownloadItems(pending_deletes);
684 }
685
686 int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
687 return RemoveDownloadsBetween(remove_begin, base::Time());
688 }
689
690 int DownloadManager::RemoveAllDownloads() {
691 if (this != profile_->GetOriginalProfile()->GetDownloadManager()) {
692 // This is an incognito downloader. Clear All should clear main download
693 // manager as well.
694 profile_->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads();
695 }
696 // The null times make the date range unbounded.
697 return RemoveDownloadsBetween(base::Time(), base::Time());
698 }
699
700 // Initiate a download of a specific URL. We send the request to the
701 // ResourceDispatcherHost, and let it send us responses like a regular
702 // download.
703 void DownloadManager::DownloadUrl(const GURL& url,
704 const GURL& referrer,
705 const std::string& referrer_charset,
706 TabContents* tab_contents) {
707 DownloadUrlToFile(url, referrer, referrer_charset, DownloadSaveInfo(),
708 tab_contents);
709 }
710
711 void DownloadManager::DownloadUrlToFile(const GURL& url,
712 const GURL& referrer,
713 const std::string& referrer_charset,
714 const DownloadSaveInfo& save_info,
715 TabContents* tab_contents) {
716 DCHECK(tab_contents);
717 // We send a pointer to content::ResourceContext, instead of the usual
718 // reference, so that a copy of the object isn't made.
719 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
720 NewRunnableFunction(&download_util::DownloadUrl,
721 url,
722 referrer,
723 referrer_charset,
724 save_info,
725 g_browser_process->resource_dispatcher_host(),
726 tab_contents->GetRenderProcessHost()->id(),
727 tab_contents->render_view_host()->routing_id(),
728 &tab_contents->browser_context()->
729 GetResourceContext()));
730 }
731
732 void DownloadManager::AddObserver(Observer* observer) {
733 observers_.AddObserver(observer);
734 observer->ModelChanged();
735 }
736
737 void DownloadManager::RemoveObserver(Observer* observer) {
738 observers_.RemoveObserver(observer);
739 }
740
741 bool DownloadManager::IsDownloadProgressKnown() {
742 for (DownloadMap::iterator i = in_progress_.begin();
743 i != in_progress_.end(); ++i) {
744 if (i->second->total_bytes() <= 0)
745 return false;
746 }
747
748 return true;
749 }
750
751 int64 DownloadManager::GetInProgressDownloadCount() {
752 return in_progress_.size();
753 }
754
755 int64 DownloadManager::GetReceivedDownloadBytes() {
756 DCHECK(IsDownloadProgressKnown());
757 int64 received_bytes = 0;
758 for (DownloadMap::iterator i = in_progress_.begin();
759 i != in_progress_.end(); ++i) {
760 received_bytes += i->second->received_bytes();
761 }
762 return received_bytes;
763 }
764
765 int64 DownloadManager::GetTotalDownloadBytes() {
766 DCHECK(IsDownloadProgressKnown());
767 int64 total_bytes = 0;
768 for (DownloadMap::iterator i = in_progress_.begin();
769 i != in_progress_.end(); ++i) {
770 total_bytes += i->second->total_bytes();
771 }
772 return total_bytes;
773 }
774
775 void DownloadManager::FileSelected(const FilePath& path, void* params) {
776 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
777
778 int32* id_ptr = reinterpret_cast<int32*>(params);
779 DCHECK(id_ptr != NULL);
780 int32 download_id = *id_ptr;
781 delete id_ptr;
782
783 DownloadItem* download = GetActiveDownloadItem(download_id);
784 if (!download)
785 return;
786 VLOG(20) << __FUNCTION__ << "()" << " path = \"" << path.value() << "\""
787 << " download = " << download->DebugString(true);
788
789 if (download->prompt_user_for_save_location())
790 last_download_path_ = path.DirName();
791
792 // Make sure the initial file name is set only once.
793 ContinueDownloadWithPath(download, path);
794 }
795
796 void DownloadManager::FileSelectionCanceled(void* params) {
797 // The user didn't pick a place to save the file, so need to cancel the
798 // download that's already in progress to the temporary location.
799 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
800 int32* id_ptr = reinterpret_cast<int32*>(params);
801 DCHECK(id_ptr != NULL);
802 int32 download_id = *id_ptr;
803 delete id_ptr;
804
805 DownloadItem* download = GetActiveDownloadItem(download_id);
806 if (!download)
807 return;
808
809 VLOG(20) << __FUNCTION__ << "()"
810 << " download = " << download->DebugString(true);
811
812 DownloadCancelledInternal(download_id, download->request_handle());
813 }
814
815 // Operations posted to us from the history service ----------------------------
816
817 // The history service has retrieved all download entries. 'entries' contains
818 // 'DownloadHistoryInfo's in sorted order (by ascending start_time).
819 void DownloadManager::OnQueryDownloadEntriesComplete(
820 std::vector<DownloadHistoryInfo>* entries) {
821 for (size_t i = 0; i < entries->size(); ++i) {
822 DownloadItem* download = new DownloadItem(this, entries->at(i));
823 DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
824 downloads_.insert(download);
825 history_downloads_[download->db_handle()] = download;
826 VLOG(20) << __FUNCTION__ << "()" << i << ">"
827 << " download = " << download->DebugString(true);
828 }
829 NotifyModelChanged();
830 CheckForHistoryFilesRemoval();
831 }
832
833 void DownloadManager::AddDownloadItemToHistory(DownloadItem* download,
834 int64 db_handle) {
835 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
836
837 // It's not immediately obvious, but HistoryBackend::CreateDownload() can
838 // call this function with an invalid |db_handle|. For instance, this can
839 // happen when the history database is offline. We cannot have multiple
840 // DownloadItems with the same invalid db_handle, so we need to assign a
841 // unique |db_handle| here.
842 if (db_handle == DownloadHistory::kUninitializedHandle)
843 db_handle = download_history_->GetNextFakeDbHandle();
844
845 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/84508
846 // is fixed.
847 CHECK_NE(DownloadHistory::kUninitializedHandle, db_handle);
848
849 DCHECK(download->db_handle() == DownloadHistory::kUninitializedHandle);
850 download->set_db_handle(db_handle);
851
852 DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
853 history_downloads_[download->db_handle()] = download;
854
855 // Show in the appropriate browser UI.
856 // This includes buttons to save or cancel, for a dangerous download.
857 ShowDownloadInBrowser(download);
858
859 // Inform interested objects about the new download.
860 NotifyModelChanged();
861 }
862
863 // Once the new DownloadItem's creation info has been committed to the history
864 // service, we associate the DownloadItem with the db handle, update our
865 // 'history_downloads_' map and inform observers.
866 void DownloadManager::OnCreateDownloadEntryComplete(int32 download_id,
867 int64 db_handle) {
868 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
869 DownloadItem* download = GetActiveDownloadItem(download_id);
870 if (!download)
871 return;
872
873 VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle
874 << " download_id = " << download_id
875 << " download = " << download->DebugString(true);
876
877 AddDownloadItemToHistory(download, db_handle);
878
879 // If the download is still in progress, try to complete it.
880 //
881 // Otherwise, download has been cancelled or interrupted before we've
882 // received the DB handle. We post one final message to the history
883 // service so that it can be properly in sync with the DownloadItem's
884 // completion status, and also inform any observers so that they get
885 // more than just the start notification.
886 if (download->IsInProgress()) {
887 MaybeCompleteDownload(download);
888 } else {
889 DCHECK(download->IsCancelled())
890 << " download = " << download->DebugString(true);
891 in_progress_.erase(download_id);
892 active_downloads_.erase(download_id);
893 download_history_->UpdateEntry(download);
894 download->UpdateObservers();
895 }
896 }
897
898 void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) {
899 // The 'contents' may no longer exist if the user closed the tab before we
900 // get this start completion event.
901 DownloadRequestHandle request_handle = download->request_handle();
902 TabContents* content = request_handle.GetTabContents();
903
904 // If the contents no longer exists, we ask the embedder to suggest another
905 // tab.
906 if (!content)
907 content = delegate_->GetAlternativeTabContentsToNotifyForDownload();
908
909 if (content)
910 content->OnStartDownload(download);
911 }
912
913 // Clears the last download path, used to initialize "save as" dialogs.
914 void DownloadManager::ClearLastDownloadPath() {
915 last_download_path_ = FilePath();
916 }
917
918 void DownloadManager::NotifyModelChanged() {
919 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
920 }
921
922 DownloadItem* DownloadManager::GetDownloadItem(int download_id) {
923 // The |history_downloads_| map is indexed by the download's db_handle,
924 // not its id, so we have to iterate.
925 for (DownloadMap::iterator it = history_downloads_.begin();
926 it != history_downloads_.end(); ++it) {
927 DownloadItem* item = it->second;
928 if (item->id() == download_id)
929 return item;
930 }
931 return NULL;
932 }
933
934 DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) {
935 DCHECK(ContainsKey(active_downloads_, download_id));
936 DownloadItem* download = active_downloads_[download_id];
937 DCHECK(download != NULL);
938 return download;
939 }
940
941 // Confirm that everything in all maps is also in |downloads_|, and that
942 // everything in |downloads_| is also in some other map.
943 void DownloadManager::AssertContainersConsistent() const {
944 #if !defined(NDEBUG)
945 // Turn everything into sets.
946 const DownloadMap* input_maps[] = {&active_downloads_,
947 &history_downloads_,
948 &save_page_downloads_};
949 DownloadSet active_set, history_set, save_page_set;
950 DownloadSet* all_sets[] = {&active_set, &history_set, &save_page_set};
951 DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(all_sets));
952 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input_maps); i++) {
953 for (DownloadMap::const_iterator it = input_maps[i]->begin();
954 it != input_maps[i]->end(); ++it) {
955 all_sets[i]->insert(&*it->second);
956 }
957 }
958
959 // Check if each set is fully present in downloads, and create a union.
960 DownloadSet downloads_union;
961 for (int i = 0; i < static_cast<int>(ARRAYSIZE_UNSAFE(all_sets)); i++) {
962 DownloadSet remainder;
963 std::insert_iterator<DownloadSet> insert_it(remainder, remainder.begin());
964 std::set_difference(all_sets[i]->begin(), all_sets[i]->end(),
965 downloads_.begin(), downloads_.end(),
966 insert_it);
967 DCHECK(remainder.empty());
968 std::insert_iterator<DownloadSet>
969 insert_union(downloads_union, downloads_union.end());
970 std::set_union(downloads_union.begin(), downloads_union.end(),
971 all_sets[i]->begin(), all_sets[i]->end(),
972 insert_union);
973 }
974
975 // Is everything in downloads_ present in one of the other sets?
976 DownloadSet remainder;
977 std::insert_iterator<DownloadSet>
978 insert_remainder(remainder, remainder.begin());
979 std::set_difference(downloads_.begin(), downloads_.end(),
980 downloads_union.begin(), downloads_union.end(),
981 insert_remainder);
982 DCHECK(remainder.empty());
983 #endif
984 }
985
986 // DownloadManager::OtherDownloadManagerObserver implementation ----------------
987
988 DownloadManager::OtherDownloadManagerObserver::OtherDownloadManagerObserver(
989 DownloadManager* observing_download_manager)
990 : observing_download_manager_(observing_download_manager),
991 observed_download_manager_(NULL) {
992 if (observing_download_manager->profile_->GetOriginalProfile() ==
993 observing_download_manager->profile_) {
994 return;
995 }
996
997 observed_download_manager_ = observing_download_manager_->
998 profile_->GetOriginalProfile()->GetDownloadManager();
999 observed_download_manager_->AddObserver(this);
1000 }
1001
1002 DownloadManager::OtherDownloadManagerObserver::~OtherDownloadManagerObserver() {
1003 if (observed_download_manager_)
1004 observed_download_manager_->RemoveObserver(this);
1005 }
1006
1007 void DownloadManager::OtherDownloadManagerObserver::ModelChanged() {
1008 observing_download_manager_->NotifyModelChanged();
1009 }
1010
1011 void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() {
1012 observed_download_manager_ = NULL;
1013 }
1014
1015 void DownloadManager::SavePageDownloadStarted(DownloadItem* download) {
1016 DCHECK(!ContainsKey(save_page_downloads_, download->id()));
1017 downloads_.insert(download);
1018 save_page_downloads_[download->id()] = download;
1019
1020 // Add this entry to the history service.
1021 // Additionally, the UI is notified in the callback.
1022 download_history_->AddEntry(download,
1023 NewCallback(this, &DownloadManager::OnSavePageDownloadEntryAdded));
1024 }
1025
1026 // SavePackage will call SavePageDownloadFinished upon completion/cancellation.
1027 // The history callback will call OnSavePageDownloadEntryAdded.
1028 // If the download finishes before the history callback,
1029 // OnSavePageDownloadEntryAdded calls SavePageDownloadFinished, ensuring that
1030 // the history event is update regardless of the order in which these two events
1031 // complete.
1032 // If something removes the download item from the download manager (Remove,
1033 // Shutdown) the result will be that the SavePage system will not be able to
1034 // properly update the download item (which no longer exists) or the download
1035 // history, but the action will complete properly anyway. This may lead to the
1036 // history entry being wrong on a reload of chrome (specifically in the case of
1037 // Initiation -> History Callback -> Removal -> Completion), but there's no way
1038 // to solve that without canceling on Remove (which would then update the DB).
1039
1040 void DownloadManager::OnSavePageDownloadEntryAdded(int32 download_id,
1041 int64 db_handle) {
1042 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1043
1044 DownloadMap::const_iterator it = save_page_downloads_.find(download_id);
1045 // This can happen if the download manager is shutting down and all maps
1046 // have been cleared.
1047 if (it == save_page_downloads_.end())
1048 return;
1049
1050 DownloadItem* download = it->second;
1051 if (!download) {
1052 NOTREACHED();
1053 return;
1054 }
1055
1056 AddDownloadItemToHistory(download, db_handle);
1057
1058 // Finalize this download if it finished before the history callback.
1059 if (!download->IsInProgress())
1060 SavePageDownloadFinished(download);
1061 }
1062
1063 void DownloadManager::SavePageDownloadFinished(DownloadItem* download) {
1064 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
1065 download_history_->UpdateEntry(download);
1066 DCHECK(ContainsKey(save_page_downloads_, download->id()));
1067 save_page_downloads_.erase(download->id());
1068
1069 if (download->IsComplete())
1070 NotificationService::current()->Notify(
1071 content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
1072 Source<DownloadManager>(this),
1073 Details<DownloadItem>(download));
1074 }
1075 }
1076
1077 int32 DownloadManager::GetNextSavePageId() {
1078 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1079 return next_save_page_id_++;
1080 }
1081
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698