Move the core download files to content.
Review URL: http://codereview.chromium.org/7618048
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96829 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/content/browser/download/download_create_info.cc b/content/browser/download/download_create_info.cc
new file mode 100644
index 0000000..4b556ab
--- /dev/null
+++ b/content/browser/download/download_create_info.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_create_info.h"
+
+#include <string>
+
+#include "base/format_macros.h"
+#include "base/stringprintf.h"
+
+DownloadCreateInfo::DownloadCreateInfo(const FilePath& path,
+ const GURL& url,
+ const base::Time& start_time,
+ int64 received_bytes,
+ int64 total_bytes,
+ int32 state,
+ int32 download_id,
+ bool has_user_gesture)
+ : path(path),
+ url_chain(1, url),
+ path_uniquifier(0),
+ start_time(start_time),
+ received_bytes(received_bytes),
+ total_bytes(total_bytes),
+ state(state),
+ download_id(download_id),
+ has_user_gesture(has_user_gesture),
+ db_handle(0),
+ prompt_user_for_save_location(false),
+ is_extension_install(false) {
+}
+
+DownloadCreateInfo::DownloadCreateInfo()
+ : path_uniquifier(0),
+ received_bytes(0),
+ total_bytes(0),
+ state(-1),
+ download_id(-1),
+ has_user_gesture(false),
+ db_handle(0),
+ prompt_user_for_save_location(false),
+ is_extension_install(false) {
+}
+
+DownloadCreateInfo::~DownloadCreateInfo() {
+}
+
+std::string DownloadCreateInfo::DebugString() const {
+ return base::StringPrintf("{"
+ " download_id = %d"
+ " url = \"%s\""
+ " path = \"%" PRFilePath "\""
+ " received_bytes = %" PRId64
+ " total_bytes = %" PRId64
+ " request_handle = %s"
+ " prompt_user_for_save_location = %c"
+ " }",
+ download_id,
+ url().spec().c_str(),
+ path.value().c_str(),
+ received_bytes,
+ total_bytes,
+ request_handle.DebugString().c_str(),
+ prompt_user_for_save_location ? 'T' : 'F');
+}
+
+const GURL& DownloadCreateInfo::url() const {
+ return url_chain.empty() ? GURL::EmptyGURL() : url_chain.back();
+}
diff --git a/content/browser/download/download_create_info.h b/content/browser/download/download_create_info.h
new file mode 100644
index 0000000..d2e5cd6
--- /dev/null
+++ b/content/browser/download/download_create_info.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_CREATE_INFO_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_CREATE_INFO_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/time.h"
+#include "content/browser/download/download_file.h"
+#include "content/browser/download/download_request_handle.h"
+#include "googleurl/src/gurl.h"
+
+// Used for informing the download manager of a new download, since we don't
+// want to pass |DownloadItem|s between threads.
+struct DownloadCreateInfo {
+ DownloadCreateInfo(const FilePath& path,
+ const GURL& url,
+ const base::Time& start_time,
+ int64 received_bytes,
+ int64 total_bytes,
+ int32 state,
+ int32 download_id,
+ bool has_user_gesture);
+ DownloadCreateInfo();
+ ~DownloadCreateInfo();
+
+ std::string DebugString() const;
+
+ // The URL from which we are downloading. This is the final URL after any
+ // redirection by the server for |url_chain|.
+ const GURL& url() const;
+
+ // DownloadItem fields
+ // The path where we want to save the download file.
+ FilePath path;
+
+ // The chain of redirects that leading up to and including the final URL.
+ std::vector<GURL> url_chain;
+
+ // The URL that referred us.
+ GURL referrer_url;
+
+ // A number that should be added to the suggested path to make it unique.
+ // 0 means no number should be appended. Not actually stored in the db.
+ int path_uniquifier;
+
+ // The time when the download started.
+ base::Time start_time;
+
+ // The number of bytes that have been received.
+ int64 received_bytes;
+
+ // The total download size.
+ int64 total_bytes;
+
+ // The current state of the download.
+ int32 state;
+
+ // The (per-session) ID of the download.
+ int32 download_id;
+
+ // True if the download was initiated by user action.
+ bool has_user_gesture;
+
+ // The handle to the download request information. Used for operations
+ // outside the download system.
+ DownloadRequestHandle request_handle;
+
+ // The handle of the download in the history database.
+ int64 db_handle;
+
+ // The content-disposition string from the response header.
+ std::string content_disposition;
+
+ // The mime type string from the response header (may be overridden).
+ std::string mime_type;
+
+ // The value of the content type header sent with the downloaded item. It
+ // may be different from |mime_type|, which may be set based on heuristics
+ // which may look at the file extension and first few bytes of the file.
+ std::string original_mime_type;
+
+ // True if we should display the 'save as...' UI and prompt the user
+ // for the download location.
+ // False if the UI should be supressed and the download performed to the
+ // default location.
+ bool prompt_user_for_save_location;
+
+ // The original name for a dangerous download.
+ FilePath original_name;
+
+ // Whether this download is for extension install or not.
+ bool is_extension_install;
+
+ // The charset of the referring page where the download request comes from.
+ // It's used to construct a suggested filename.
+ std::string referrer_charset;
+
+ // The download file save info.
+ DownloadSaveInfo save_info;
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_CREATE_INFO_H_
diff --git a/content/browser/download/download_file.cc b/content/browser/download/download_file.cc
new file mode 100644
index 0000000..95b6078
--- /dev/null
+++ b/content/browser/download/download_file.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_file.h"
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/stringprintf.h"
+#include "chrome/browser/download/download_util.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/download/download_create_info.h"
+#include "content/browser/download/download_manager.h"
+
+DownloadFile::DownloadFile(const DownloadCreateInfo* info,
+ DownloadManager* download_manager)
+ : BaseFile(info->save_info.file_path,
+ info->url(),
+ info->referrer_url,
+ info->received_bytes,
+ info->save_info.file_stream),
+ id_(info->download_id),
+ request_handle_(info->request_handle),
+ download_manager_(download_manager) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+}
+
+DownloadFile::~DownloadFile() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+}
+
+void DownloadFile::CancelDownloadRequest() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ request_handle_.CancelRequest();
+}
+
+DownloadManager* DownloadFile::GetDownloadManager() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ return download_manager_.get();
+}
+
+std::string DownloadFile::DebugString() const {
+ return base::StringPrintf("{"
+ " id_ = " "%d"
+ " request_handle = %s"
+ " Base File = %s"
+ " }",
+ id_,
+ request_handle_.DebugString().c_str(),
+ BaseFile::DebugString().c_str());
+}
diff --git a/content/browser/download/download_file.h b/content/browser/download/download_file.h
new file mode 100644
index 0000000..5edffc2
--- /dev/null
+++ b/content/browser/download/download_file.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "content/browser/download/base_file.h"
+#include "content/browser/download/download_request_handle.h"
+#include "content/browser/download/download_types.h"
+
+struct DownloadCreateInfo;
+class DownloadManager;
+class ResourceDispatcherHost;
+
+// These objects live exclusively on the download thread and handle the writing
+// operations for one download. These objects live only for the duration that
+// the download is 'in progress': once the download has been completed or
+// cancelled, the DownloadFile is destroyed.
+class DownloadFile : public BaseFile {
+ public:
+ DownloadFile(const DownloadCreateInfo* info,
+ DownloadManager* download_manager);
+ virtual ~DownloadFile();
+
+ // Cancels the download request associated with this file.
+ void CancelDownloadRequest();
+
+ int id() const { return id_; }
+ DownloadManager* GetDownloadManager();
+
+ virtual std::string DebugString() const;
+
+ private:
+ // The unique identifier for this download, assigned at creation by
+ // the DownloadFileManager for its internal record keeping.
+ int id_;
+
+ // The handle to the request information. Used for operations outside the
+ // download system, specifically canceling a download.
+ DownloadRequestHandle request_handle_;
+
+ // DownloadManager this download belongs to.
+ scoped_refptr<DownloadManager> download_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadFile);
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_H_
diff --git a/content/browser/download/download_file_manager.cc b/content/browser/download/download_file_manager.cc
new file mode 100644
index 0000000..fb80d2f
--- /dev/null
+++ b/content/browser/download/download_file_manager.cc
@@ -0,0 +1,403 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_file_manager.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/task.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/common/pref_names.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/download/download_create_info.h"
+#include "content/browser/download/download_manager.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/io_buffer.h"
+
+namespace {
+
+// Throttle updates to the UI thread so that a fast moving download doesn't
+// cause it to become unresponsive (in milliseconds).
+const int kUpdatePeriodMs = 500;
+
+} // namespace
+
+DownloadFileManager::DownloadFileManager(ResourceDispatcherHost* rdh)
+ : next_id_(0),
+ resource_dispatcher_host_(rdh) {
+}
+
+DownloadFileManager::~DownloadFileManager() {
+ DCHECK(downloads_.empty());
+}
+
+void DownloadFileManager::Shutdown() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &DownloadFileManager::OnShutdown));
+}
+
+void DownloadFileManager::OnShutdown() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ StopUpdateTimer();
+ STLDeleteValues(&downloads_);
+}
+
+void DownloadFileManager::CreateDownloadFile(DownloadCreateInfo* info,
+ DownloadManager* download_manager,
+ bool get_hash) {
+ DCHECK(info);
+ VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString();
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ // Life of |info| ends here. No more references to it after this method.
+ scoped_ptr<DownloadCreateInfo> infop(info);
+
+ scoped_ptr<DownloadFile>
+ download_file(new DownloadFile(info, download_manager));
+ if (!download_file->Initialize(get_hash)) {
+ info->request_handle.CancelRequest();
+ return;
+ }
+
+ int32 id = info->download_id;
+ DCHECK(GetDownloadFile(id) == NULL);
+ downloads_[id] = download_file.release();
+
+ // The file is now ready, we can un-pause the request and start saving data.
+ info->request_handle.ResumeRequest();
+
+ StartUpdateTimer();
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(download_manager,
+ &DownloadManager::StartDownload, id));
+}
+
+DownloadFile* DownloadFileManager::GetDownloadFile(int id) {
+ DownloadFileMap::iterator it = downloads_.find(id);
+ return it == downloads_.end() ? NULL : it->second;
+}
+
+void DownloadFileManager::StartUpdateTimer() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (!update_timer_.IsRunning()) {
+ update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdatePeriodMs),
+ this, &DownloadFileManager::UpdateInProgressDownloads);
+ }
+}
+
+void DownloadFileManager::StopUpdateTimer() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ update_timer_.Stop();
+}
+
+void DownloadFileManager::UpdateInProgressDownloads() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ for (DownloadFileMap::iterator i = downloads_.begin();
+ i != downloads_.end(); ++i) {
+ int id = i->first;
+ DownloadFile* download_file = i->second;
+ DownloadManager* manager = download_file->GetDownloadManager();
+ if (manager) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(manager, &DownloadManager::UpdateDownload,
+ id, download_file->bytes_so_far()));
+ }
+ }
+}
+
+// Called on the IO thread once the ResourceDispatcherHost has decided that a
+// request is a download.
+int DownloadFileManager::GetNextId() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ return next_id_++;
+}
+
+void DownloadFileManager::StartDownload(DownloadCreateInfo* info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(info);
+
+ DownloadManager* manager = info->request_handle.GetDownloadManager();
+ if (!manager) {
+ info->request_handle.CancelRequest();
+ delete info;
+ return;
+ }
+
+ // TODO(phajdan.jr): fix the duplication of path info below.
+ info->path = info->save_info.file_path;
+
+ manager->CreateDownloadItem(info);
+
+#if defined(ENABLE_SAFE_BROWSING)
+ bool hash_needed = manager->profile()->GetPrefs()->GetBoolean(
+ prefs::kSafeBrowsingEnabled) &&
+ g_browser_process->safe_browsing_service()->DownloadBinHashNeeded();
+#else
+ bool hash_needed = false;
+#endif
+
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &DownloadFileManager::CreateDownloadFile,
+ info, make_scoped_refptr(manager), hash_needed));
+}
+
+// We don't forward an update to the UI thread here, since we want to throttle
+// the UI update rate via a periodic timer. If the user has cancelled the
+// download (in the UI thread), we may receive a few more updates before the IO
+// thread gets the cancel message: we just delete the data since the
+// DownloadFile has been deleted.
+void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ std::vector<DownloadBuffer::Contents> contents;
+ {
+ base::AutoLock auto_lock(buffer->lock);
+ contents.swap(buffer->contents);
+ }
+
+ DownloadFile* download_file = GetDownloadFile(id);
+ for (size_t i = 0; i < contents.size(); ++i) {
+ net::IOBuffer* data = contents[i].first;
+ const int data_len = contents[i].second;
+ if (download_file)
+ download_file->AppendDataToFile(data->data(), data_len);
+ data->Release();
+ }
+}
+
+void DownloadFileManager::OnResponseCompleted(
+ int id,
+ DownloadBuffer* buffer,
+ int os_error,
+ const std::string& security_info) {
+ VLOG(20) << __FUNCTION__ << "()" << " id = " << id
+ << " os_error = " << os_error
+ << " security_info = \"" << security_info << "\"";
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ delete buffer;
+ DownloadFile* download_file = GetDownloadFile(id);
+ if (!download_file)
+ return;
+
+ download_file->Finish();
+
+ DownloadManager* download_manager = download_file->GetDownloadManager();
+ if (!download_manager) {
+ CancelDownload(id);
+ return;
+ }
+
+ std::string hash;
+ if (!download_file->GetSha256Hash(&hash))
+ hash.clear();
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ download_manager, &DownloadManager::OnResponseCompleted,
+ id, download_file->bytes_so_far(), os_error, hash));
+ // We need to keep the download around until the UI thread has finalized
+ // the name.
+}
+
+// This method will be sent via a user action, or shutdown on the UI thread, and
+// run on the download thread. Since this message has been sent from the UI
+// thread, the download may have already completed and won't exist in our map.
+void DownloadFileManager::CancelDownload(int id) {
+ VLOG(20) << __FUNCTION__ << "()" << " id = " << id;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DownloadFileMap::iterator it = downloads_.find(id);
+ if (it == downloads_.end())
+ return;
+
+ DownloadFile* download_file = it->second;
+ VLOG(20) << __FUNCTION__ << "()"
+ << " download_file = " << download_file->DebugString();
+ download_file->Cancel();
+
+ EraseDownload(id);
+}
+
+void DownloadFileManager::CompleteDownload(int id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ if (!ContainsKey(downloads_, id))
+ return;
+
+ DownloadFile* download_file = downloads_[id];
+
+ VLOG(20) << " " << __FUNCTION__ << "()"
+ << " id = " << id
+ << " download_file = " << download_file->DebugString();
+
+ download_file->Detach();
+
+ EraseDownload(id);
+}
+
+void DownloadFileManager::OnDownloadManagerShutdown(DownloadManager* manager) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(manager);
+
+ std::set<DownloadFile*> to_remove;
+
+ for (DownloadFileMap::iterator i = downloads_.begin();
+ i != downloads_.end(); ++i) {
+ DownloadFile* download_file = i->second;
+ if (download_file->GetDownloadManager() == manager) {
+ download_file->CancelDownloadRequest();
+ to_remove.insert(download_file);
+ }
+ }
+
+ for (std::set<DownloadFile*>::iterator i = to_remove.begin();
+ i != to_remove.end(); ++i) {
+ downloads_.erase((*i)->id());
+ delete *i;
+ }
+}
+
+// Actions from the UI thread and run on the download thread
+
+// The DownloadManager in the UI thread has provided an intermediate .crdownload
+// name for the download specified by 'id'. Rename the in progress download.
+//
+// There are 2 possible rename cases where this method can be called:
+// 1. tmp -> foo.crdownload (not final, safe)
+// 2. tmp-> Unconfirmed.xxx.crdownload (not final, dangerous)
+void DownloadFileManager::RenameInProgressDownloadFile(
+ int id, const FilePath& full_path) {
+ VLOG(20) << __FUNCTION__ << "()" << " id = " << id
+ << " full_path = \"" << full_path.value() << "\"";
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ DownloadFile* download_file = GetDownloadFile(id);
+ if (!download_file)
+ return;
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " download_file = " << download_file->DebugString();
+
+ if (!download_file->Rename(full_path)) {
+ // Error. Between the time the UI thread generated 'full_path' to the time
+ // this code runs, something happened that prevents us from renaming.
+ CancelDownloadOnRename(id);
+ }
+}
+
+// The DownloadManager in the UI thread has provided a final name for the
+// download specified by 'id'. Rename the download that's in the process
+// of completing.
+//
+// There are 2 possible rename cases where this method can be called:
+// 1. foo.crdownload -> foo (final, safe)
+// 2. Unconfirmed.xxx.crdownload -> xxx (final, validated)
+void DownloadFileManager::RenameCompletingDownloadFile(
+ int id, const FilePath& full_path, bool overwrite_existing_file) {
+ VLOG(20) << __FUNCTION__ << "()" << " id = " << id
+ << " overwrite_existing_file = " << overwrite_existing_file
+ << " full_path = \"" << full_path.value() << "\"";
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ DownloadFile* download_file = GetDownloadFile(id);
+ if (!download_file)
+ return;
+
+ DCHECK(download_file->GetDownloadManager());
+ DownloadManager* download_manager = download_file->GetDownloadManager();
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " download_file = " << download_file->DebugString();
+
+ int uniquifier = 0;
+ FilePath new_path = full_path;
+ if (!overwrite_existing_file) {
+ // Make our name unique at this point, as if a dangerous file is
+ // downloading and a 2nd download is started for a file with the same
+ // name, they would have the same path. This is because we uniquify
+ // the name on download start, and at that time the first file does
+ // not exists yet, so the second file gets the same name.
+ // This should not happen in the SAFE case, and we check for that in the UI
+ // thread.
+ uniquifier = download_util::GetUniquePathNumber(new_path);
+ if (uniquifier > 0) {
+ download_util::AppendNumberToPath(&new_path, uniquifier);
+ }
+ }
+
+ // Rename the file, overwriting if necessary.
+ if (!download_file->Rename(new_path)) {
+ // Error. Between the time the UI thread generated 'full_path' to the time
+ // this code runs, something happened that prevents us from renaming.
+ CancelDownloadOnRename(id);
+ return;
+ }
+
+#if defined(OS_MACOSX)
+ // Done here because we only want to do this once; see
+ // http://crbug.com/13120 for details.
+ download_file->AnnotateWithSourceInformation();
+#endif
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id,
+ new_path, uniquifier));
+}
+
+// Called only from RenameInProgressDownloadFile and
+// RenameCompletingDownloadFile on the FILE thread.
+void DownloadFileManager::CancelDownloadOnRename(int id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ DownloadFile* download_file = GetDownloadFile(id);
+ if (!download_file)
+ return;
+
+ DownloadManager* download_manager = download_file->GetDownloadManager();
+ if (!download_manager) {
+ // Without a download manager, we can't cancel the request normally, so we
+ // need to do it here. The normal path will also update the download
+ // history before cancelling the request.
+ download_file->CancelDownloadRequest();
+ return;
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(download_manager,
+ &DownloadManager::DownloadCancelled, id));
+}
+
+void DownloadFileManager::EraseDownload(int id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ if (!ContainsKey(downloads_, id))
+ return;
+
+ DownloadFile* download_file = downloads_[id];
+
+ VLOG(20) << " " << __FUNCTION__ << "()"
+ << " id = " << id
+ << " download_file = " << download_file->DebugString();
+
+ downloads_.erase(id);
+
+ delete download_file;
+
+ if (downloads_.empty())
+ StopUpdateTimer();
+}
diff --git a/content/browser/download/download_file_manager.h b/content/browser/download/download_file_manager.h
new file mode 100644
index 0000000..45cef5e
--- /dev/null
+++ b/content/browser/download/download_file_manager.h
@@ -0,0 +1,173 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The DownloadFileManager owns a set of DownloadFile objects, each of which
+// represent one in progress download and performs the disk IO for that
+// download. The DownloadFileManager itself is a singleton object owned by the
+// ResourceDispatcherHost.
+//
+// The DownloadFileManager uses the file_thread for performing file write
+// operations, in order to avoid disk activity on either the IO (network) thread
+// and the UI thread. It coordinates the notifications from the network and UI.
+//
+// A typical download operation involves multiple threads:
+//
+// Updating an in progress download
+// io_thread
+// |----> data ---->|
+// file_thread (writes to disk)
+// |----> stats ---->|
+// ui_thread (feedback for user and
+// updates to history)
+//
+// Cancel operations perform the inverse order when triggered by a user action:
+// ui_thread (user click)
+// |----> cancel command ---->|
+// file_thread (close file)
+// |----> cancel command ---->|
+// io_thread (stops net IO
+// for download)
+//
+// The DownloadFileManager tracks download requests, mapping from a download
+// ID (unique integer created in the IO thread) to the DownloadManager for the
+// tab (profile) where the download was initiated. In the event of a tab closure
+// during a download, the DownloadFileManager will continue to route data to the
+// appropriate DownloadManager. In progress downloads are cancelled for a
+// DownloadManager that exits (such as when closing a profile).
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_MANAGER_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_MANAGER_H_
+#pragma once
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "base/timer.h"
+#include "content/browser/download/download_request_handle.h"
+#include "ui/gfx/native_widget_types.h"
+
+struct DownloadBuffer;
+struct DownloadCreateInfo;
+struct DownloadSaveInfo;
+class DownloadFile;
+class DownloadManager;
+class FilePath;
+class GURL;
+class ResourceDispatcherHost;
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+// Manages all in progress downloads.
+class DownloadFileManager
+ : public base::RefCountedThreadSafe<DownloadFileManager> {
+ public:
+ explicit DownloadFileManager(ResourceDispatcherHost* rdh);
+
+ // Called on shutdown on the UI thread.
+ void Shutdown();
+
+ // Called on the IO thread
+ int GetNextId();
+
+ // Called on UI thread to make DownloadFileManager start the download.
+ void StartDownload(DownloadCreateInfo* info);
+
+ // Handlers for notifications sent from the IO thread and run on the
+ // FILE thread.
+ void UpdateDownload(int id, DownloadBuffer* buffer);
+ // |os_error| is 0 for normal completions, and non-0 for errors.
+ // |security_info| contains SSL information (cert_id, cert_status,
+ // security_bits, ssl_connection_status), which can be used to
+ // fine-tune the error message. It is empty if the transaction
+ // was not performed securely.
+ void OnResponseCompleted(int id,
+ DownloadBuffer* buffer,
+ int os_error,
+ const std::string& security_info);
+
+ // Handlers for notifications sent from the UI thread and run on the
+ // FILE thread. These are both terminal actions with respect to the
+ // download file, as far as the DownloadFileManager is concerned -- if
+ // anything happens to the download file after they are called, it will
+ // be ignored.
+ void CancelDownload(int id);
+ void CompleteDownload(int id);
+
+ // Called on FILE thread by DownloadManager at the beginning of its shutdown.
+ void OnDownloadManagerShutdown(DownloadManager* manager);
+
+ // The DownloadManager in the UI thread has provided an intermediate
+ // .crdownload name for the download specified by |id|.
+ void RenameInProgressDownloadFile(int id, const FilePath& full_path);
+
+ // The DownloadManager in the UI thread has provided a final name for the
+ // download specified by |id|.
+ // |overwrite_existing_file| prevents uniquification, and is used for SAFE
+ // downloads, as the user may have decided to overwrite the file.
+ // Sent from the UI thread and run on the FILE thread.
+ void RenameCompletingDownloadFile(int id,
+ const FilePath& full_path,
+ bool overwrite_existing_file);
+
+ // The number of downloads currently active on the DownloadFileManager.
+ // Primarily for testing.
+ int NumberOfActiveDownloads() const {
+ return downloads_.size();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<DownloadFileManager>;
+ friend class DownloadManagerTest;
+ FRIEND_TEST_ALL_PREFIXES(DownloadManagerTest, StartDownload);
+
+ ~DownloadFileManager();
+
+ // Timer helpers for updating the UI about the current progress of a download.
+ void StartUpdateTimer();
+ void StopUpdateTimer();
+ void UpdateInProgressDownloads();
+
+ // Clean up helper that runs on the download thread.
+ void OnShutdown();
+
+ // Creates DownloadFile on FILE thread and continues starting the download
+ // process.
+ void CreateDownloadFile(DownloadCreateInfo* info,
+ DownloadManager* download_manager,
+ bool hash_needed);
+
+ // Called only on the download thread.
+ DownloadFile* GetDownloadFile(int id);
+
+ // Called only from RenameInProgressDownloadFile and
+ // RenameCompletingDownloadFile on the FILE thread.
+ void CancelDownloadOnRename(int id);
+
+ // Erases the download file with the given the download |id| and removes
+ // it from the maps.
+ void EraseDownload(int id);
+
+ // Unique ID for each DownloadFile.
+ int next_id_;
+
+ typedef base::hash_map<int, DownloadFile*> DownloadFileMap;
+
+ // A map of all in progress downloads. It owns the download files.
+ DownloadFileMap downloads_;
+
+ // Schedule periodic updates of the download progress. This timer
+ // is controlled from the FILE thread, and posts updates to the UI thread.
+ base::RepeatingTimer<DownloadFileManager> update_timer_;
+
+ ResourceDispatcherHost* resource_dispatcher_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadFileManager);
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_MANAGER_H_
diff --git a/content/browser/download/download_file_unittest.cc b/content/browser/download/download_file_unittest.cc
new file mode 100644
index 0000000..c257a237
--- /dev/null
+++ b/content/browser/download/download_file_unittest.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/scoped_temp_dir.h"
+#include "base/string_number_conversions.h"
+#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/download/mock_download_manager_delegate.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/download/download_create_info.h"
+#include "content/browser/download/download_file.h"
+#include "content/browser/download/download_manager.h"
+#include "content/browser/download/download_request_handle.h"
+#include "content/browser/download/download_status_updater.h"
+#include "content/browser/download/mock_download_manager.h"
+#include "net/base/file_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class DownloadFileTest : public testing::Test {
+ public:
+
+ static const char* kTestData1;
+ static const char* kTestData2;
+ static const char* kTestData3;
+ static const char* kDataHash;
+ static const int32 kDummyDownloadId;
+ static const int kDummyChildId;
+ static const int kDummyRequestId;
+
+ // We need a UI |BrowserThread| in order to destruct |download_manager_|,
+ // which has trait |BrowserThread::DeleteOnUIThread|. Without this,
+ // calling Release() on |download_manager_| won't ever result in its
+ // destructor being called and we get a leak.
+ DownloadFileTest() :
+ ui_thread_(BrowserThread::UI, &loop_),
+ file_thread_(BrowserThread::FILE, &loop_) {
+ }
+
+ ~DownloadFileTest() {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ download_manager_delegate_.reset(new MockDownloadManagerDelegate());
+ download_manager_ = new MockDownloadManager(
+ download_manager_delegate_.get(), &download_status_updater_);
+ }
+
+ virtual void TearDown() {
+ // When a DownloadManager's reference count drops to 0, it is not
+ // deleted immediately. Instead, a task is posted to the UI thread's
+ // message loop to delete it.
+ // So, drop the reference count to 0 and run the message loop once
+ // to ensure that all resources are cleaned up before the test exits.
+ download_manager_ = NULL;
+ ui_thread_.message_loop()->RunAllPending();
+ }
+
+ virtual void CreateDownloadFile(scoped_ptr<DownloadFile>* file, int offset) {
+ DownloadCreateInfo info;
+ info.download_id = kDummyDownloadId + offset;
+ // info.request_handle default constructed to null.
+ info.save_info.file_stream = file_stream_;
+ file->reset(new DownloadFile(&info, download_manager_));
+ }
+
+ virtual void DestroyDownloadFile(scoped_ptr<DownloadFile>* file, int offset) {
+ EXPECT_EQ(kDummyDownloadId + offset, (*file)->id());
+ EXPECT_EQ(download_manager_, (*file)->GetDownloadManager());
+ EXPECT_FALSE((*file)->in_progress());
+ EXPECT_EQ(static_cast<int64>(expected_data_.size()),
+ (*file)->bytes_so_far());
+
+ // Make sure the data has been properly written to disk.
+ std::string disk_data;
+ EXPECT_TRUE(file_util::ReadFileToString((*file)->full_path(),
+ &disk_data));
+ EXPECT_EQ(expected_data_, disk_data);
+
+ // Make sure the mock BrowserThread outlives the DownloadFile to satisfy
+ // thread checks inside it.
+ file->reset();
+ }
+
+ void AppendDataToFile(scoped_ptr<DownloadFile>* file,
+ const std::string& data) {
+ EXPECT_TRUE((*file)->in_progress());
+ (*file)->AppendDataToFile(data.data(), data.size());
+ expected_data_ += data;
+ EXPECT_EQ(static_cast<int64>(expected_data_.size()),
+ (*file)->bytes_so_far());
+ }
+
+ protected:
+ // Temporary directory for renamed downloads.
+ ScopedTempDir temp_dir_;
+
+ DownloadStatusUpdater download_status_updater_;
+ scoped_ptr<MockDownloadManagerDelegate> download_manager_delegate_;
+ scoped_refptr<DownloadManager> download_manager_;
+
+ linked_ptr<net::FileStream> file_stream_;
+
+ // DownloadFile instance we are testing.
+ scoped_ptr<DownloadFile> download_file_;
+
+ private:
+ MessageLoop loop_;
+ // UI thread.
+ BrowserThread ui_thread_;
+ // File thread to satisfy debug checks in DownloadFile.
+ BrowserThread file_thread_;
+
+ // Keep track of what data should be saved to the disk file.
+ std::string expected_data_;
+};
+
+const char* DownloadFileTest::kTestData1 =
+ "Let's write some data to the file!\n";
+const char* DownloadFileTest::kTestData2 = "Writing more data.\n";
+const char* DownloadFileTest::kTestData3 = "Final line.";
+const char* DownloadFileTest::kDataHash =
+ "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8";
+
+const int32 DownloadFileTest::kDummyDownloadId = 23;
+const int DownloadFileTest::kDummyChildId = 3;
+const int DownloadFileTest::kDummyRequestId = 67;
+
+// Rename the file before any data is downloaded, after some has, after it all
+// has, and after it's closed.
+TEST_F(DownloadFileTest, RenameFileFinal) {
+ CreateDownloadFile(&download_file_, 0);
+ ASSERT_TRUE(download_file_->Initialize(true));
+ FilePath initial_path(download_file_->full_path());
+ EXPECT_TRUE(file_util::PathExists(initial_path));
+ FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
+ FilePath path_2(initial_path.InsertBeforeExtensionASCII("_2"));
+ FilePath path_3(initial_path.InsertBeforeExtensionASCII("_3"));
+ FilePath path_4(initial_path.InsertBeforeExtensionASCII("_4"));
+
+ // Rename the file before downloading any data.
+ EXPECT_TRUE(download_file_->Rename(path_1));
+ FilePath renamed_path = download_file_->full_path();
+ EXPECT_EQ(path_1, renamed_path);
+
+ // Check the files.
+ EXPECT_FALSE(file_util::PathExists(initial_path));
+ EXPECT_TRUE(file_util::PathExists(path_1));
+
+ // Download the data.
+ AppendDataToFile(&download_file_, kTestData1);
+ AppendDataToFile(&download_file_, kTestData2);
+
+ // Rename the file after downloading some data.
+ EXPECT_TRUE(download_file_->Rename(path_2));
+ renamed_path = download_file_->full_path();
+ EXPECT_EQ(path_2, renamed_path);
+
+ // Check the files.
+ EXPECT_FALSE(file_util::PathExists(path_1));
+ EXPECT_TRUE(file_util::PathExists(path_2));
+
+ AppendDataToFile(&download_file_, kTestData3);
+
+ // Rename the file after downloading all the data.
+ EXPECT_TRUE(download_file_->Rename(path_3));
+ renamed_path = download_file_->full_path();
+ EXPECT_EQ(path_3, renamed_path);
+
+ // Check the files.
+ EXPECT_FALSE(file_util::PathExists(path_2));
+ EXPECT_TRUE(file_util::PathExists(path_3));
+
+ // Should not be able to get the hash until the file is closed.
+ std::string hash;
+ EXPECT_FALSE(download_file_->GetSha256Hash(&hash));
+
+ download_file_->Finish();
+
+ // Rename the file after downloading all the data and closing the file.
+ EXPECT_TRUE(download_file_->Rename(path_4));
+ renamed_path = download_file_->full_path();
+ EXPECT_EQ(path_4, renamed_path);
+
+ // Check the files.
+ EXPECT_FALSE(file_util::PathExists(path_3));
+ EXPECT_TRUE(file_util::PathExists(path_4));
+
+ // Check the hash.
+ EXPECT_TRUE(download_file_->GetSha256Hash(&hash));
+ EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size()));
+
+ DestroyDownloadFile(&download_file_, 0);
+}
diff --git a/content/browser/download/download_item.cc b/content/browser/download/download_item.cc
new file mode 100644
index 0000000..2a9fd6e
--- /dev/null
+++ b/content/browser/download/download_item.cc
@@ -0,0 +1,865 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_item.h"
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/format_macros.h"
+#include "base/i18n/case_conversion.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/stringprintf.h"
+#include "base/timer.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/net_util.h"
+#include "chrome/browser/download/download_crx_util.h"
+#include "chrome/browser/download/download_extensions.h"
+#include "chrome/browser/download/download_history.h"
+#include "chrome/browser/download/download_manager_delegate.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/history/download_history_info.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/pref_names.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/download/download_create_info.h"
+#include "content/browser/download/download_file_manager.h"
+#include "content/browser/download/download_manager.h"
+#include "content/common/notification_source.h"
+
+// A DownloadItem normally goes through the following states:
+// * Created (when download starts)
+// * Made visible to consumers (e.g. Javascript) after the
+// destination file has been determined.
+// * Entered into the history database.
+// * Made visible in the download shelf.
+// * All data is saved. Note that the actual data download occurs
+// in parallel with the above steps, but until those steps are
+// complete, completion of the data download will be ignored.
+// * Download file is renamed to its final name, and possibly
+// auto-opened.
+// TODO(rdsmith): This progress should be reflected in
+// DownloadItem::DownloadState and a state transition table/state diagram.
+//
+// TODO(rdsmith): This description should be updated to reflect the cancel
+// pathways.
+
+namespace {
+
+// Update frequency (milliseconds).
+const int kUpdateTimeMs = 1000;
+
+static void DeleteDownloadedFile(const FilePath& path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ // Make sure we only delete files.
+ if (!file_util::DirectoryExists(path))
+ file_util::Delete(path, false);
+}
+
+const char* DebugSafetyStateString(DownloadItem::SafetyState state) {
+ switch (state) {
+ case DownloadItem::SAFE:
+ return "SAFE";
+ case DownloadItem::DANGEROUS:
+ return "DANGEROUS";
+ case DownloadItem::DANGEROUS_BUT_VALIDATED:
+ return "DANGEROUS_BUT_VALIDATED";
+ default:
+ NOTREACHED() << "Unknown safety state " << state;
+ return "unknown";
+ };
+}
+
+const char* DebugDownloadStateString(DownloadItem::DownloadState state) {
+ switch (state) {
+ case DownloadItem::IN_PROGRESS:
+ return "IN_PROGRESS";
+ case DownloadItem::COMPLETE:
+ return "COMPLETE";
+ case DownloadItem::CANCELLED:
+ return "CANCELLED";
+ case DownloadItem::REMOVING:
+ return "REMOVING";
+ case DownloadItem::INTERRUPTED:
+ return "INTERRUPTED";
+ default:
+ NOTREACHED() << "Unknown download state " << state;
+ return "unknown";
+ };
+}
+
+DownloadItem::SafetyState GetSafetyState(bool dangerous_file,
+ bool dangerous_url) {
+ return (dangerous_url || dangerous_file) ?
+ DownloadItem::DANGEROUS : DownloadItem::SAFE;
+}
+
+// Note: When a download has both |dangerous_file| and |dangerous_url| set,
+// danger type is set to DANGEROUS_URL since the risk of dangerous URL
+// overweights that of dangerous file type.
+DownloadItem::DangerType GetDangerType(bool dangerous_file,
+ bool dangerous_url) {
+ if (dangerous_url) {
+ // dangerous URL overweights dangerous file. We check dangerous URL first.
+ return DownloadItem::DANGEROUS_URL;
+ }
+ return dangerous_file ?
+ DownloadItem::DANGEROUS_FILE : DownloadItem::NOT_DANGEROUS;
+}
+
+} // namespace
+
+// Constructor for reading from the history service.
+DownloadItem::DownloadItem(DownloadManager* download_manager,
+ const DownloadHistoryInfo& info)
+ : download_id_(-1),
+ full_path_(info.path),
+ url_chain_(1, info.url),
+ referrer_url_(info.referrer_url),
+ total_bytes_(info.total_bytes),
+ received_bytes_(info.received_bytes),
+ start_tick_(base::TimeTicks()),
+ state_(static_cast<DownloadState>(info.state)),
+ start_time_(info.start_time),
+ db_handle_(info.db_handle),
+ download_manager_(download_manager),
+ is_paused_(false),
+ open_when_complete_(false),
+ file_externally_removed_(false),
+ safety_state_(SAFE),
+ auto_opened_(false),
+ is_otr_(false),
+ is_temporary_(false),
+ all_data_saved_(false),
+ opened_(false),
+ open_enabled_(true) {
+ if (IsInProgress())
+ state_ = CANCELLED;
+ if (IsComplete())
+ all_data_saved_ = true;
+ Init(false /* not actively downloading */);
+}
+
+// Constructing for a regular download:
+DownloadItem::DownloadItem(DownloadManager* download_manager,
+ const DownloadCreateInfo& info,
+ bool is_otr)
+ : state_info_(info.original_name, info.save_info.file_path,
+ info.has_user_gesture, info.prompt_user_for_save_location,
+ info.path_uniquifier, false, false,
+ info.is_extension_install),
+ request_handle_(info.request_handle),
+ download_id_(info.download_id),
+ full_path_(info.path),
+ url_chain_(info.url_chain),
+ referrer_url_(info.referrer_url),
+ suggested_filename_(UTF16ToUTF8(info.save_info.suggested_name)),
+ content_disposition_(info.content_disposition),
+ mime_type_(info.mime_type),
+ original_mime_type_(info.original_mime_type),
+ referrer_charset_(info.referrer_charset),
+ total_bytes_(info.total_bytes),
+ received_bytes_(0),
+ last_os_error_(0),
+ start_tick_(base::TimeTicks::Now()),
+ state_(IN_PROGRESS),
+ start_time_(info.start_time),
+ db_handle_(DownloadHistory::kUninitializedHandle),
+ download_manager_(download_manager),
+ is_paused_(false),
+ open_when_complete_(false),
+ file_externally_removed_(false),
+ safety_state_(SAFE),
+ auto_opened_(false),
+ is_otr_(is_otr),
+ is_temporary_(!info.save_info.file_path.empty()),
+ all_data_saved_(false),
+ opened_(false),
+ open_enabled_(true) {
+ Init(true /* actively downloading */);
+}
+
+// Constructing for the "Save Page As..." feature:
+DownloadItem::DownloadItem(DownloadManager* download_manager,
+ const FilePath& path,
+ const GURL& url,
+ bool is_otr)
+ : download_id_(download_manager->GetNextSavePageId()),
+ full_path_(path),
+ url_chain_(1, url),
+ referrer_url_(GURL()),
+ total_bytes_(0),
+ received_bytes_(0),
+ last_os_error_(0),
+ start_tick_(base::TimeTicks::Now()),
+ state_(IN_PROGRESS),
+ start_time_(base::Time::Now()),
+ db_handle_(DownloadHistory::kUninitializedHandle),
+ download_manager_(download_manager),
+ is_paused_(false),
+ open_when_complete_(false),
+ file_externally_removed_(false),
+ safety_state_(SAFE),
+ auto_opened_(false),
+ is_otr_(is_otr),
+ is_temporary_(false),
+ all_data_saved_(false),
+ opened_(false),
+ open_enabled_(true) {
+ Init(true /* actively downloading */);
+}
+
+DownloadItem::~DownloadItem() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ TransitionTo(REMOVING);
+ download_manager_->AssertQueueStateConsistent(this);
+}
+
+void DownloadItem::AddObserver(Observer* observer) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ observers_.AddObserver(observer);
+}
+
+void DownloadItem::RemoveObserver(Observer* observer) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ observers_.RemoveObserver(observer);
+}
+
+void DownloadItem::UpdateObservers() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
+}
+
+bool DownloadItem::CanShowInFolder() {
+ return !IsCancelled() && !file_externally_removed_;
+}
+
+bool DownloadItem::CanOpenDownload() {
+ return !Extension::IsExtension(state_info_.target_name) &&
+ !file_externally_removed_;
+}
+
+bool DownloadItem::ShouldOpenFileBasedOnExtension() {
+ return download_manager_->delegate()->ShouldOpenFileBasedOnExtension(
+ GetUserVerifiedFilePath());
+}
+
+void DownloadItem::OpenFilesBasedOnExtension(bool open) {
+ DownloadPrefs* prefs = download_manager_->download_prefs();
+ if (open)
+ prefs->EnableAutoOpenBasedOnExtension(GetUserVerifiedFilePath());
+ else
+ prefs->DisableAutoOpenBasedOnExtension(GetUserVerifiedFilePath());
+}
+
+void DownloadItem::OpenDownload() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (IsPartialDownload()) {
+ open_when_complete_ = !open_when_complete_;
+ } else if (IsComplete() && !file_externally_removed_) {
+ // Ideally, we want to detect errors in opening and report them, but we
+ // don't generally have the proper interface for that to the external
+ // program that opens the file. So instead we spawn a check to update
+ // the UI if the file has been deleted in parallel with the open.
+ download_manager_->CheckForFileRemoval(this);
+ opened_ = true;
+ FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this));
+
+ // For testing: If download opening is disabled on this item,
+ // make the rest of the routine a no-op.
+ if (!open_enabled_)
+ return;
+
+ if (is_extension_install()) {
+ download_crx_util::OpenChromeExtension(download_manager_->profile(),
+ *this);
+ return;
+ }
+#if defined(OS_MACOSX)
+ // Mac OS X requires opening downloads on the UI thread.
+ platform_util::OpenItem(full_path());
+#else
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(&platform_util::OpenItem, full_path()));
+#endif
+ }
+}
+
+void DownloadItem::ShowDownloadInShell() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+#if defined(OS_MACOSX)
+ // Mac needs to run this operation on the UI thread.
+ platform_util::ShowItemInFolder(full_path());
+#else
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(&platform_util::ShowItemInFolder,
+ full_path()));
+#endif
+}
+
+void DownloadItem::DangerousDownloadValidated() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_EQ(DANGEROUS, safety_state());
+
+ UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated",
+ GetDangerType(),
+ DANGEROUS_TYPE_MAX);
+
+ safety_state_ = DANGEROUS_BUT_VALIDATED;
+ UpdateObservers();
+
+ download_manager_->MaybeCompleteDownload(this);
+}
+
+void DownloadItem::UpdateSize(int64 bytes_so_far) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ received_bytes_ = bytes_so_far;
+
+ // If we've received more data than we were expecting (bad server info?),
+ // revert to 'unknown size mode'.
+ if (received_bytes_ > total_bytes_)
+ total_bytes_ = 0;
+}
+
+void DownloadItem::StartProgressTimer() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateTimeMs), this,
+ &DownloadItem::UpdateObservers);
+}
+
+void DownloadItem::StopProgressTimer() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ update_timer_.Stop();
+}
+
+// Updates from the download thread may have been posted while this download
+// was being cancelled in the UI thread, so we'll accept them unless we're
+// complete.
+void DownloadItem::Update(int64 bytes_so_far) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!IsInProgress()) {
+ NOTREACHED();
+ return;
+ }
+ UpdateSize(bytes_so_far);
+ UpdateObservers();
+}
+
+// Triggered by a user action.
+void DownloadItem::Cancel(bool update_history) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
+ if (!IsPartialDownload()) {
+ // Small downloads might be complete before this method has
+ // a chance to run.
+ return;
+ }
+
+ download_util::RecordDownloadCount(download_util::CANCELLED_COUNT);
+
+ TransitionTo(CANCELLED);
+ StopProgressTimer();
+ if (update_history)
+ download_manager_->DownloadCancelled(download_id_);
+}
+
+void DownloadItem::MarkAsComplete() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DCHECK(all_data_saved_);
+ TransitionTo(COMPLETE);
+}
+
+void DownloadItem::OnAllDataSaved(int64 size) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DCHECK(!all_data_saved_);
+ all_data_saved_ = true;
+ UpdateSize(size);
+ StopProgressTimer();
+}
+
+void DownloadItem::OnDownloadedFileRemoved() {
+ file_externally_removed_ = true;
+ UpdateObservers();
+}
+
+void DownloadItem::Completed() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ VLOG(20) << __FUNCTION__ << "() " << DebugString(false);
+
+ DCHECK(all_data_saved_);
+ TransitionTo(COMPLETE);
+ download_manager_->DownloadCompleted(id());
+ download_util::RecordDownloadCompleted(start_tick_);
+
+ if (is_extension_install()) {
+ // Extensions should already have been unpacked and opened.
+ DCHECK(auto_opened_);
+ } else if (open_when_complete() ||
+ ShouldOpenFileBasedOnExtension() ||
+ is_temporary()) {
+ // If the download is temporary, like in drag-and-drop, do not open it but
+ // we still need to set it auto-opened so that it can be removed from the
+ // download shelf.
+ if (!is_temporary())
+ OpenDownload();
+
+ auto_opened_ = true;
+ UpdateObservers();
+ }
+}
+
+void DownloadItem::StartCrxInstall() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DCHECK(is_extension_install());
+ DCHECK(all_data_saved_);
+
+ scoped_refptr<CrxInstaller> crx_installer =
+ download_crx_util::OpenChromeExtension(
+ download_manager_->profile(),
+ *this);
+
+ // CRX_INSTALLER_DONE will fire when the install completes. Observe()
+ // will call Completed() on this item. If this DownloadItem is not
+ // around when CRX_INSTALLER_DONE fires, Complete() will not be called.
+ registrar_.Add(this,
+ chrome::NOTIFICATION_CRX_INSTALLER_DONE,
+ Source<CrxInstaller>(crx_installer.get()));
+
+ // The status text and percent complete indicator will change now
+ // that we are installing a CRX. Update observers so that they pick
+ // up the change.
+ UpdateObservers();
+}
+
+void DownloadItem::TransitionTo(DownloadState new_state) {
+ if (state_ == new_state)
+ return;
+
+ state_ = new_state;
+ UpdateObservers();
+}
+
+void DownloadItem::UpdateSafetyState() {
+ SafetyState updated_value(
+ GetSafetyState(state_info_.is_dangerous_file,
+ state_info_.is_dangerous_url));
+ if (updated_value != safety_state_) {
+ safety_state_ = updated_value;
+ UpdateObservers();
+ }
+}
+
+void DownloadItem::UpdateTarget() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (state_info_.target_name.value().empty())
+ state_info_.target_name = full_path_.BaseName();
+}
+
+// NotificationObserver implementation.
+void DownloadItem::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DCHECK(type == chrome::NOTIFICATION_CRX_INSTALLER_DONE);
+
+ // No need to listen for CRX_INSTALLER_DONE anymore.
+ registrar_.Remove(this,
+ chrome::NOTIFICATION_CRX_INSTALLER_DONE,
+ source);
+
+ auto_opened_ = true;
+ DCHECK(all_data_saved_);
+
+ Completed();
+}
+
+void DownloadItem::Interrupted(int64 size, int os_error) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!IsInProgress())
+ return;
+
+ last_os_error_ = os_error;
+ UpdateSize(size);
+ StopProgressTimer();
+ download_util::RecordDownloadInterrupted(os_error,
+ received_bytes_,
+ total_bytes_);
+ TransitionTo(INTERRUPTED);
+}
+
+void DownloadItem::Delete(DeleteReason reason) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ switch (reason) {
+ case DELETE_DUE_TO_USER_DISCARD:
+ UMA_HISTOGRAM_ENUMERATION("Download.UserDiscard", GetDangerType(),
+ DANGEROUS_TYPE_MAX);
+ break;
+ case DELETE_DUE_TO_BROWSER_SHUTDOWN:
+ UMA_HISTOGRAM_ENUMERATION("Download.Discard", GetDangerType(),
+ DANGEROUS_TYPE_MAX);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(&DeleteDownloadedFile, full_path_));
+ Remove();
+ // We have now been deleted.
+}
+
+void DownloadItem::Remove() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ download_manager_->AssertQueueStateConsistent(this);
+ Cancel(true);
+ download_manager_->AssertQueueStateConsistent(this);
+
+ TransitionTo(REMOVING);
+ download_manager_->RemoveDownload(db_handle_);
+ // We have now been deleted.
+}
+
+bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const {
+ if (total_bytes_ <= 0)
+ return false; // We never received the content_length for this download.
+
+ int64 speed = CurrentSpeed();
+ if (speed == 0)
+ return false;
+
+ *remaining = base::TimeDelta::FromSeconds(
+ (total_bytes_ - received_bytes_) / speed);
+ return true;
+}
+
+int64 DownloadItem::CurrentSpeed() const {
+ if (is_paused_)
+ return 0;
+ base::TimeDelta diff = base::TimeTicks::Now() - start_tick_;
+ int64 diff_ms = diff.InMilliseconds();
+ return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms;
+}
+
+int DownloadItem::PercentComplete() const {
+ // We don't have an accurate way to estimate the time to unpack a CRX.
+ // The slowest part is re-encoding images, and time to do this depends on
+ // the contents of the image. If a CRX is being unpacked, indicate that
+ // we do not know how close to completion we are.
+ if (IsCrxInstallRuning() || total_bytes_ <= 0)
+ return -1;
+
+ return static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
+}
+
+void DownloadItem::OnPathDetermined(const FilePath& path) {
+ full_path_ = path;
+ UpdateTarget();
+}
+
+void DownloadItem::Rename(const FilePath& full_path) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " full_path = \"" << full_path.value() << "\""
+ << " " << DebugString(true);
+ DCHECK(!full_path.empty());
+ full_path_ = full_path;
+}
+
+void DownloadItem::TogglePause() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DCHECK(IsInProgress());
+ if (is_paused_)
+ request_handle_.ResumeRequest();
+ else
+ request_handle_.PauseRequest();
+ is_paused_ = !is_paused_;
+ UpdateObservers();
+}
+
+void DownloadItem::OnDownloadCompleting(DownloadFileManager* file_manager) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " needs rename = " << NeedsRename()
+ << " " << DebugString(true);
+ DCHECK_NE(DANGEROUS, safety_state());
+ DCHECK(file_manager);
+
+ if (NeedsRename()) {
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(file_manager,
+ &DownloadFileManager::RenameCompletingDownloadFile, id(),
+ GetTargetFilePath(), safety_state() == SAFE));
+ return;
+ }
+
+ DCHECK(!is_extension_install());
+ Completed();
+
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(file_manager, &DownloadFileManager::CompleteDownload,
+ id()));
+}
+
+void DownloadItem::OnDownloadRenamedToFinalName(const FilePath& full_path) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " full_path = \"" << full_path.value() << "\""
+ << " needed rename = " << NeedsRename()
+ << " " << DebugString(false);
+ DCHECK(NeedsRename());
+
+ Rename(full_path);
+
+ if (is_extension_install()) {
+ StartCrxInstall();
+ // Completed() will be called when the installer finishes.
+ return;
+ }
+
+ Completed();
+}
+
+bool DownloadItem::MatchesQuery(const string16& query) const {
+ if (query.empty())
+ return true;
+
+ DCHECK_EQ(query, base::i18n::ToLower(query));
+
+ string16 url_raw(base::i18n::ToLower(UTF8ToUTF16(GetURL().spec())));
+ if (url_raw.find(query) != string16::npos)
+ return true;
+
+ // TODO(phajdan.jr): write a test case for the following code.
+ // A good test case would be:
+ // "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd",
+ // L"/\x4f60\x597d\x4f60\x597d",
+ // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD"
+ PrefService* prefs = download_manager_->profile()->GetPrefs();
+ std::string languages(prefs->GetString(prefs::kAcceptLanguages));
+ string16 url_formatted(
+ base::i18n::ToLower(net::FormatUrl(GetURL(), languages)));
+ if (url_formatted.find(query) != string16::npos)
+ return true;
+
+ string16 path(base::i18n::ToLower(full_path().LossyDisplayName()));
+ // This shouldn't just do a substring match; it is wrong for Unicode
+ // due to normalization and we have a fancier search-query system
+ // used elsewhere.
+ // http://code.google.com/p/chromium/issues/detail?id=71982
+ return (path.find(query) != string16::npos);
+}
+
+void DownloadItem::SetFileCheckResults(const DownloadStateInfo& state) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true);
+ state_info_ = state;
+ VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true);
+
+ UpdateSafetyState();
+}
+
+DownloadItem::DangerType DownloadItem::GetDangerType() const {
+ return ::GetDangerType(state_info_.is_dangerous_file,
+ state_info_.is_dangerous_url);
+}
+
+bool DownloadItem::IsDangerous() const {
+ return GetDangerType() != DownloadItem::NOT_DANGEROUS;
+}
+
+void DownloadItem::MarkFileDangerous() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ state_info_.is_dangerous_file = true;
+ UpdateSafetyState();
+}
+
+void DownloadItem::MarkUrlDangerous() {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ state_info_.is_dangerous_url = true;
+ UpdateSafetyState();
+}
+
+DownloadHistoryInfo DownloadItem::GetHistoryInfo() const {
+ return DownloadHistoryInfo(full_path(),
+ GetURL(),
+ referrer_url(),
+ start_time(),
+ received_bytes(),
+ total_bytes(),
+ state(),
+ db_handle());
+}
+
+FilePath DownloadItem::GetTargetFilePath() const {
+ return full_path_.DirName().Append(state_info_.target_name);
+}
+
+FilePath DownloadItem::GetFileNameToReportUser() const {
+ if (state_info_.path_uniquifier > 0) {
+ FilePath name(state_info_.target_name);
+ download_util::AppendNumberToPath(&name, state_info_.path_uniquifier);
+ return name;
+ }
+ return state_info_.target_name;
+}
+
+FilePath DownloadItem::GetUserVerifiedFilePath() const {
+ return (safety_state_ == DownloadItem::SAFE) ?
+ GetTargetFilePath() : full_path_;
+}
+
+void DownloadItem::Init(bool active) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ UpdateTarget();
+ if (active) {
+ StartProgressTimer();
+ download_util::RecordDownloadCount(download_util::START_COUNT);
+ }
+ VLOG(20) << __FUNCTION__ << "() " << DebugString(true);
+}
+
+// TODO(ahendrickson) -- Move |INTERRUPTED| from |IsCancelled()| to
+// |IsPartialDownload()|, when resuming interrupted downloads is implemented.
+bool DownloadItem::IsPartialDownload() const {
+ return (state_ == IN_PROGRESS);
+}
+
+bool DownloadItem::IsInProgress() const {
+ return (state_ == IN_PROGRESS);
+}
+
+bool DownloadItem::IsCancelled() const {
+ return (state_ == CANCELLED) ||
+ (state_ == INTERRUPTED);
+}
+
+bool DownloadItem::IsInterrupted() const {
+ return (state_ == INTERRUPTED);
+}
+
+bool DownloadItem::IsComplete() const {
+ return (state_ == COMPLETE);
+}
+
+const GURL& DownloadItem::GetURL() const {
+ return url_chain_.empty() ?
+ GURL::EmptyGURL() : url_chain_.back();
+}
+
+std::string DownloadItem::DebugString(bool verbose) const {
+ std::string description =
+ base::StringPrintf("{ id = %d"
+ " state = %s",
+ download_id_,
+ DebugDownloadStateString(state()));
+
+ // Construct a string of the URL chain.
+ std::string url_list("<none>");
+ if (!url_chain_.empty()) {
+ std::vector<GURL>::const_iterator iter = url_chain_.begin();
+ std::vector<GURL>::const_iterator last = url_chain_.end();
+ url_list = (*iter).spec();
+ ++iter;
+ for ( ; verbose && (iter != last); ++iter) {
+ url_list += " ->\n\t";
+ const GURL& next_url = *iter;
+ url_list += next_url.spec();
+ }
+ }
+
+ if (verbose) {
+ description += base::StringPrintf(
+ " db_handle = %" PRId64
+ " total_bytes = %" PRId64
+ " received_bytes = %" PRId64
+ " is_paused = %c"
+ " is_extension_install = %c"
+ " is_otr = %c"
+ " safety_state = %s"
+ " url_chain = \n\t\"%s\"\n\t"
+ " target_name = \"%" PRFilePath "\""
+ " full_path = \"%" PRFilePath "\"",
+ db_handle(),
+ total_bytes(),
+ received_bytes(),
+ is_paused() ? 'T' : 'F',
+ is_extension_install() ? 'T' : 'F',
+ is_otr() ? 'T' : 'F',
+ DebugSafetyStateString(safety_state()),
+ url_list.c_str(),
+ state_info_.target_name.value().c_str(),
+ full_path().value().c_str());
+ } else {
+ description += base::StringPrintf(" url = \"%s\"", url_list.c_str());
+ }
+
+ description += " }";
+
+ return description;
+}
diff --git a/content/browser/download/download_item.h b/content/browser/download/download_item.h
new file mode 100644
index 0000000..3a90253
--- /dev/null
+++ b/content/browser/download/download_item.h
@@ -0,0 +1,493 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Each download is represented by a DownloadItem, and all DownloadItems
+// are owned by the DownloadManager which maintains a global list of all
+// downloads. DownloadItems are created when a user initiates a download,
+// and exist for the duration of the browser life time.
+//
+// Download observers:
+// DownloadItem::Observer:
+// - allows observers to receive notifications about one download from start
+// to completion
+// Use AddObserver() / RemoveObserver() on the appropriate download object to
+// receive state updates.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_ITEM_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_ITEM_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/observer_list.h"
+#include "base/time.h"
+#include "base/timer.h"
+#include "content/browser/download/download_request_handle.h"
+#include "content/browser/download/download_state_info.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "googleurl/src/gurl.h"
+
+class CrxInstaller;
+class DownloadFileManager;
+class DownloadManager;
+struct DownloadCreateInfo;
+struct DownloadHistoryInfo;
+
+// One DownloadItem per download. This is the model class that stores all the
+// state for a download. Multiple views, such as a tab's download shelf and the
+// Destination tab's download view, may refer to a given DownloadItem.
+//
+// This is intended to be used only on the UI thread.
+class DownloadItem : public NotificationObserver {
+ public:
+ enum DownloadState {
+ // Download is actively progressing.
+ IN_PROGRESS = 0,
+
+ // Download is completely finished.
+ COMPLETE,
+
+ // Download has been cancelled.
+ CANCELLED,
+
+ // This state indicates that the download item is about to be destroyed,
+ // and observers seeing this state should release all references.
+ REMOVING,
+
+ // This state indicates that the download has been interrupted.
+ INTERRUPTED,
+
+ // Maximum value.
+ MAX_DOWNLOAD_STATE
+ };
+
+ enum SafetyState {
+ SAFE = 0,
+ DANGEROUS,
+ DANGEROUS_BUT_VALIDATED // Dangerous but the user confirmed the download.
+ };
+
+ // This enum is used by histograms. Do not change the ordering or remove
+ // items.
+ enum DangerType {
+ NOT_DANGEROUS = 0,
+
+ // A dangerous file to the system (e.g.: an executable or extension from
+ // places other than gallery).
+ DANGEROUS_FILE,
+
+ // Safebrowsing service shows this URL leads to malicious file download.
+ DANGEROUS_URL,
+
+ // Memory space for histograms is determined by the max.
+ // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
+ DANGEROUS_TYPE_MAX
+ };
+
+ // Reason for deleting the download. Passed to Delete().
+ enum DeleteReason {
+ DELETE_DUE_TO_BROWSER_SHUTDOWN = 0,
+ DELETE_DUE_TO_USER_DISCARD
+ };
+
+ // Interface that observers of a particular download must implement in order
+ // to receive updates to the download's status.
+ class Observer {
+ public:
+ virtual void OnDownloadUpdated(DownloadItem* download) = 0;
+
+ // Called when a downloaded file has been opened.
+ virtual void OnDownloadOpened(DownloadItem* download) = 0;
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ // Constructing from persistent store:
+ DownloadItem(DownloadManager* download_manager,
+ const DownloadHistoryInfo& info);
+
+ // Constructing for a regular download:
+ DownloadItem(DownloadManager* download_manager,
+ const DownloadCreateInfo& info,
+ bool is_otr);
+
+ // Constructing for the "Save Page As..." feature:
+ DownloadItem(DownloadManager* download_manager,
+ const FilePath& path,
+ const GURL& url,
+ bool is_otr);
+
+ virtual ~DownloadItem();
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Notifies our observers periodically.
+ void UpdateObservers();
+
+ // NotificationObserver implementation.
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Returns true if it is OK to open a folder which this file is inside.
+ bool CanShowInFolder();
+
+ // Returns true if it is OK to register the type of this file so that
+ // it opens automatically.
+ bool CanOpenDownload();
+
+ // Tests if a file type should be opened automatically.
+ bool ShouldOpenFileBasedOnExtension();
+
+ // Registers this file extension for automatic opening upon download
+ // completion if 'open' is true, or prevents the extension from automatic
+ // opening if 'open' is false.
+ void OpenFilesBasedOnExtension(bool open);
+
+ // Open the file associated with this download (wait for the download to
+ // complete if it is in progress).
+ void OpenDownload();
+
+ // Show the download via the OS shell.
+ void ShowDownloadInShell();
+
+ // Called when the user has validated the download of a dangerous file.
+ void DangerousDownloadValidated();
+
+ // Received a new chunk of data
+ void Update(int64 bytes_so_far);
+
+ // Cancel the download operation. We need to distinguish between cancels at
+ // exit (DownloadManager destructor) from user interface initiated cancels
+ // because at exit, the history system may not exist, and any updates to it
+ // require AddRef'ing the DownloadManager in the destructor which results in
+ // a DCHECK failure. Set 'update_history' to false when canceling from at
+ // exit to prevent this crash. This may result in a difference between the
+ // downloaded file's size on disk, and what the history system's last record
+ // of it is. At worst, we'll end up re-downloading a small portion of the file
+ // when resuming a download (assuming the server supports byte ranges).
+ void Cancel(bool update_history);
+
+ // Called by external code (SavePackage) using the DownloadItem interface
+ // to display progress when the DownloadItem should be considered complete.
+ void MarkAsComplete();
+
+ // Called when all data has been saved. Only has display effects.
+ void OnAllDataSaved(int64 size);
+
+ // Called when the downloaded file is removed.
+ void OnDownloadedFileRemoved();
+
+ // Download operation had an error.
+ // |size| is the amount of data received so far, and |os_error| is the error
+ // code that the operation received.
+ void Interrupted(int64 size, int os_error);
+
+ // Deletes the file from disk and removes the download from the views and
+ // history. |user| should be true if this is the result of the user clicking
+ // the discard button, and false if it is being deleted for other reasons like
+ // browser shutdown.
+ void Delete(DeleteReason reason);
+
+ // Removes the download from the views and history.
+ void Remove();
+
+ // Simple calculation of the amount of time remaining to completion. Fills
+ // |*remaining| with the amount of time remaining if successful. Fails and
+ // returns false if we do not have the number of bytes or the speed so can
+ // not estimate.
+ bool TimeRemaining(base::TimeDelta* remaining) const;
+
+ // Simple speed estimate in bytes/s
+ int64 CurrentSpeed() const;
+
+ // Rough percent complete, -1 means we don't know (since we didn't receive a
+ // total size).
+ int PercentComplete() const;
+
+ // Called when the final path has been determined.
+ void OnPathDetermined(const FilePath& path);
+
+ // Returns true if this download has saved all of its data.
+ bool all_data_saved() const { return all_data_saved_; }
+
+ // Update the fields that may have changed in DownloadStateInfo as a
+ // result of analyzing the file and figuring out its type, location, etc.
+ // May only be called once.
+ void SetFileCheckResults(const DownloadStateInfo& state);
+
+ // Update the download's path, the actual file is renamed on the download
+ // thread.
+ void Rename(const FilePath& full_path);
+
+ // Allow the user to temporarily pause a download or resume a paused download.
+ void TogglePause();
+
+ // Called when the download is ready to complete.
+ // This may perform final rename if necessary and will eventually call
+ // DownloadItem::Completed().
+ void OnDownloadCompleting(DownloadFileManager* file_manager);
+
+ // Called when the file name for the download is renamed to its final name.
+ void OnDownloadRenamedToFinalName(const FilePath& full_path);
+
+ // Returns true if this item matches |query|. |query| must be lower-cased.
+ bool MatchesQuery(const string16& query) const;
+
+ // Returns true if the download needs more data.
+ bool IsPartialDownload() const;
+
+ // Returns true if the download is still receiving data.
+ bool IsInProgress() const;
+
+ // Returns true if the download has been cancelled or was interrupted.
+ bool IsCancelled() const;
+
+ // Returns true if the download was interrupted.
+ bool IsInterrupted() const;
+
+ // Returns true if we have all the data and know the final file name.
+ bool IsComplete() const;
+
+ // Accessors
+ DownloadState state() const { return state_; }
+ const FilePath& full_path() const { return full_path_; }
+ void set_path_uniquifier(int uniquifier) {
+ state_info_.path_uniquifier = uniquifier;
+ }
+ const GURL& GetURL() const;
+
+ const std::vector<GURL>& url_chain() const { return url_chain_; }
+ const GURL& original_url() const { return url_chain_.front(); }
+ const GURL& referrer_url() const { return referrer_url_; }
+ std::string suggested_filename() const { return suggested_filename_; }
+ std::string content_disposition() const { return content_disposition_; }
+ std::string mime_type() const { return mime_type_; }
+ std::string original_mime_type() const { return original_mime_type_; }
+ std::string referrer_charset() const { return referrer_charset_; }
+ int64 total_bytes() const { return total_bytes_; }
+ void set_total_bytes(int64 total_bytes) {
+ total_bytes_ = total_bytes;
+ }
+ int64 received_bytes() const { return received_bytes_; }
+ int32 id() const { return download_id_; }
+ base::Time start_time() const { return start_time_; }
+ void set_db_handle(int64 handle) { db_handle_ = handle; }
+ int64 db_handle() const { return db_handle_; }
+ bool is_paused() const { return is_paused_; }
+ bool open_when_complete() const { return open_when_complete_; }
+ void set_open_when_complete(bool open) { open_when_complete_ = open; }
+ bool file_externally_removed() const { return file_externally_removed_; }
+ SafetyState safety_state() const { return safety_state_; }
+ // Why |safety_state_| is not SAFE.
+ DangerType GetDangerType() const;
+ bool IsDangerous() const;
+ void MarkFileDangerous();
+ void MarkUrlDangerous();
+
+ bool auto_opened() { return auto_opened_; }
+ const FilePath& target_name() const { return state_info_.target_name; }
+ bool prompt_user_for_save_location() const {
+ return state_info_.prompt_user_for_save_location;
+ }
+ bool is_otr() const { return is_otr_; }
+ bool is_extension_install() const {
+ return state_info_.is_extension_install;
+ }
+ const FilePath& suggested_path() const { return state_info_.suggested_path; }
+ bool is_temporary() const { return is_temporary_; }
+ void set_opened(bool opened) { opened_ = opened; }
+ bool opened() const { return opened_; }
+
+ DownloadHistoryInfo GetHistoryInfo() const;
+ DownloadStateInfo state_info() const { return state_info_; }
+ const DownloadRequestHandle& request_handle() const {
+ return request_handle_;
+ }
+
+ // Returns the final target file path for the download.
+ FilePath GetTargetFilePath() const;
+
+ // Returns the file-name that should be reported to the user, which is
+ // target_name possibly with the uniquifier number.
+ FilePath GetFileNameToReportUser() const;
+
+ // Returns the user-verified target file path for the download.
+ // This returns the same path as GetTargetFilePath() for safe downloads
+ // but does not for dangerous downloads until the name is verified.
+ FilePath GetUserVerifiedFilePath() const;
+
+ // Returns true if the current file name is not the final target name yet.
+ bool NeedsRename() const {
+ return state_info_.target_name != full_path_.BaseName();
+ }
+
+ // Is a CRX installer running on this download?
+ bool IsCrxInstallRuning() const {
+ return (is_extension_install() &&
+ all_data_saved() &&
+ state_ == IN_PROGRESS);
+ }
+
+ std::string DebugString(bool verbose) const;
+
+#ifdef UNIT_TEST
+ // Mock opening downloads (for testing only).
+ void TestMockDownloadOpen() { open_enabled_ = false; }
+#endif
+
+ private:
+ // Construction common to all constructors. |active| should be true for new
+ // downloads and false for downloads from the history.
+ void Init(bool active);
+
+ // Internal helper for maintaining consistent received and total sizes.
+ void UpdateSize(int64 size);
+
+ // Called when the entire download operation (including renaming etc)
+ // is completed.
+ void Completed();
+
+ // Start/stop sending periodic updates to our observers
+ void StartProgressTimer();
+ void StopProgressTimer();
+
+ // Call to install this item as a CRX. Should only be called on
+ // items which are CRXes. Use is_extension_install() to check.
+ void StartCrxInstall();
+
+ // Call to transition state; all state transitions should go through this.
+ void TransitionTo(DownloadState new_state);
+
+ // Called when safety_state_ should be recomputed from is_dangerous_file
+ // and is_dangerous_url.
+ void UpdateSafetyState();
+
+ // Helper function to recompute |state_info_.target_name| when
+ // it may have changed. (If it's non-null it should be left alone,
+ // otherwise updated from |full_path_|.)
+ void UpdateTarget();
+
+ // State information used by the download manager.
+ DownloadStateInfo state_info_;
+
+ // The handle to the request information. Used for operations outside the
+ // download system.
+ DownloadRequestHandle request_handle_;
+
+ // Download ID assigned by DownloadResourceHandler.
+ int32 download_id_;
+
+ // Full path to the downloaded or downloading file.
+ FilePath full_path_;
+
+ // A number that should be appended to the path to make it unique, or 0 if the
+ // path should be used as is.
+ int path_uniquifier_;
+
+ // The chain of redirects that leading up to and including the final URL.
+ std::vector<GURL> url_chain_;
+
+ // The URL of the page that initiated the download.
+ GURL referrer_url_;
+
+ // Suggested filename in 'download' attribute of an anchor. Details:
+ // http://www.whatwg.org/specs/web-apps/current-work/#downloading-hyperlinks
+ std::string suggested_filename_;
+
+ // Information from the request.
+ // Content-disposition field from the header.
+ std::string content_disposition_;
+
+ // Mime-type from the header. Subject to change.
+ std::string mime_type_;
+
+ // The value of the content type header sent with the downloaded item. It
+ // may be different from |mime_type_|, which may be set based on heuristics
+ // which may look at the file extension and first few bytes of the file.
+ std::string original_mime_type_;
+
+ // The charset of the referring page where the download request comes from.
+ // It's used to construct a suggested filename.
+ std::string referrer_charset_;
+
+ // Total bytes expected
+ int64 total_bytes_;
+
+ // Current received bytes
+ int64 received_bytes_;
+
+ // Last error.
+ int last_os_error_;
+
+ // Start time for calculating remaining time
+ base::TimeTicks start_tick_;
+
+ // The current state of this download
+ DownloadState state_;
+
+ // The views of this item in the download shelf and download tab
+ ObserverList<Observer> observers_;
+
+ // Time the download was started
+ base::Time start_time_;
+
+ // Our persistent store handle
+ int64 db_handle_;
+
+ // Timer for regularly updating our observers
+ base::RepeatingTimer<DownloadItem> update_timer_;
+
+ // Our owning object
+ DownloadManager* download_manager_;
+
+ // In progress downloads may be paused by the user, we note it here
+ bool is_paused_;
+
+ // A flag for indicating if the download should be opened at completion.
+ bool open_when_complete_;
+
+ // A flag for indicating if the downloaded file is externally removed.
+ bool file_externally_removed_;
+
+ // Indicates if the download is considered potentially safe or dangerous
+ // (executable files are typically considered dangerous).
+ SafetyState safety_state_;
+
+ // True if the download was auto-opened. We set this rather than using
+ // an observer as it's frequently possible for the download to be auto opened
+ // before the observer is added.
+ bool auto_opened_;
+
+ // True if the download was initiated in an incognito window.
+ bool is_otr_;
+
+ // True if the item was downloaded temporarily.
+ bool is_temporary_;
+
+ // True if we've saved all the data for the download.
+ bool all_data_saved_;
+
+ // Did the user open the item either directly or indirectly (such as by
+ // setting always open files of this type)? The shelf also sets this field
+ // when the user closes the shelf before the item has been opened but should
+ // be treated as though the user opened it.
+ bool opened_;
+
+ // Do we actual open downloads when requested? For testing purposes
+ // only.
+ bool open_enabled_;
+
+ // DownloadItem observes CRX installs it initiates.
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadItem);
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_ITEM_H_
diff --git a/content/browser/download/download_manager.cc b/content/browser/download/download_manager.cc
new file mode 100644
index 0000000..32d504a
--- /dev/null
+++ b/content/browser/download/download_manager.cc
@@ -0,0 +1,1080 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_manager.h"
+
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/i18n/case_conversion.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/task.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/download_history.h"
+#include "chrome/browser/download/download_manager_delegate.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/history/download_history_info.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/download/download_create_info.h"
+#include "content/browser/download/download_file_manager.h"
+#include "content/browser/download/download_item.h"
+#include "content/browser/download/download_status_updater.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "content/common/content_notification_types.h"
+#include "content/common/notification_service.h"
+
+DownloadManager::DownloadManager(DownloadManagerDelegate* delegate,
+ DownloadStatusUpdater* status_updater)
+ : shutdown_needed_(false),
+ profile_(NULL),
+ file_manager_(NULL),
+ status_updater_(status_updater->AsWeakPtr()),
+ next_save_page_id_(0),
+ delegate_(delegate) {
+ if (status_updater_)
+ status_updater_->AddDelegate(this);
+}
+
+DownloadManager::~DownloadManager() {
+ DCHECK(!shutdown_needed_);
+ if (status_updater_)
+ status_updater_->RemoveDelegate(this);
+}
+
+void DownloadManager::Shutdown() {
+ VLOG(20) << __FUNCTION__ << "()"
+ << " shutdown_needed_ = " << shutdown_needed_;
+ if (!shutdown_needed_)
+ return;
+ shutdown_needed_ = false;
+
+ FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown());
+
+ if (file_manager_) {
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(file_manager_,
+ &DownloadFileManager::OnDownloadManagerShutdown,
+ make_scoped_refptr(this)));
+ }
+
+ AssertContainersConsistent();
+
+ // Go through all downloads in downloads_. Dangerous ones we need to
+ // remove on disk, and in progress ones we need to cancel.
+ for (DownloadSet::iterator it = downloads_.begin(); it != downloads_.end();) {
+ DownloadItem* download = *it;
+
+ // Save iterator from potential erases in this set done by called code.
+ // Iterators after an erasure point are still valid for lists and
+ // associative containers such as sets.
+ it++;
+
+ if (download->safety_state() == DownloadItem::DANGEROUS &&
+ download->IsPartialDownload()) {
+ // The user hasn't accepted it, so we need to remove it
+ // from the disk. This may or may not result in it being
+ // removed from the DownloadManager queues and deleted
+ // (specifically, DownloadManager::RemoveDownload only
+ // removes and deletes it if it's known to the history service)
+ // so the only thing we know after calling this function is that
+ // the download was deleted if-and-only-if it was removed
+ // from all queues.
+ download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN);
+ } else if (download->IsPartialDownload()) {
+ download->Cancel(false);
+ download_history_->UpdateEntry(download);
+ }
+ }
+
+ // At this point, all dangerous downloads have had their files removed
+ // and all in progress downloads have been cancelled. We can now delete
+ // anything left.
+
+ // Copy downloads_ to separate container so as not to set off checks
+ // in DownloadItem destruction.
+ DownloadSet downloads_to_delete;
+ downloads_to_delete.swap(downloads_);
+
+ in_progress_.clear();
+ active_downloads_.clear();
+ history_downloads_.clear();
+ STLDeleteElements(&downloads_to_delete);
+
+ DCHECK(save_page_downloads_.empty());
+
+ file_manager_ = NULL;
+
+ download_history_.reset();
+ download_prefs_.reset();
+
+ shutdown_needed_ = false;
+}
+
+void DownloadManager::GetTemporaryDownloads(
+ const FilePath& dir_path, DownloadVector* result) {
+ DCHECK(result);
+
+ for (DownloadMap::iterator it = history_downloads_.begin();
+ it != history_downloads_.end(); ++it) {
+ if (it->second->is_temporary() &&
+ it->second->full_path().DirName() == dir_path)
+ result->push_back(it->second);
+ }
+}
+
+void DownloadManager::GetAllDownloads(
+ const FilePath& dir_path, DownloadVector* result) {
+ DCHECK(result);
+
+ for (DownloadMap::iterator it = history_downloads_.begin();
+ it != history_downloads_.end(); ++it) {
+ if (!it->second->is_temporary() &&
+ (dir_path.empty() || it->second->full_path().DirName() == dir_path))
+ result->push_back(it->second);
+ }
+}
+
+void DownloadManager::GetCurrentDownloads(
+ const FilePath& dir_path, DownloadVector* result) {
+ DCHECK(result);
+
+ for (DownloadMap::iterator it = history_downloads_.begin();
+ it != history_downloads_.end(); ++it) {
+ DownloadItem* item =it->second;
+ // Skip temporary items.
+ if (item->is_temporary())
+ continue;
+ // Skip items that have all their data, and are OK to save.
+ if (!item->IsPartialDownload() &&
+ (item->safety_state() != DownloadItem::DANGEROUS))
+ continue;
+ // Skip items that don't match |dir_path|.
+ // If |dir_path| is empty, all remaining items match.
+ if (!dir_path.empty() && (it->second->full_path().DirName() != dir_path))
+ continue;
+
+ result->push_back(item);
+ }
+
+ // If we have a parent profile, let it add its downloads to the results.
+ Profile* original_profile = profile_->GetOriginalProfile();
+ if (original_profile != profile_)
+ original_profile->GetDownloadManager()->GetCurrentDownloads(dir_path,
+ result);
+}
+
+void DownloadManager::SearchDownloads(const string16& query,
+ DownloadVector* result) {
+ DCHECK(result);
+
+ string16 query_lower(base::i18n::ToLower(query));
+
+ for (DownloadMap::iterator it = history_downloads_.begin();
+ it != history_downloads_.end(); ++it) {
+ DownloadItem* download_item = it->second;
+
+ if (download_item->is_temporary() || download_item->is_extension_install())
+ continue;
+
+ // Display Incognito downloads only in Incognito window, and vice versa.
+ // The Incognito Downloads page will get the list of non-Incognito downloads
+ // from its parent profile.
+ if (profile_->IsOffTheRecord() != download_item->is_otr())
+ continue;
+
+ if (download_item->MatchesQuery(query_lower))
+ result->push_back(download_item);
+ }
+
+ // If we have a parent profile, let it add its downloads to the results.
+ Profile* original_profile = profile_->GetOriginalProfile();
+ if (original_profile != profile_)
+ original_profile->GetDownloadManager()->SearchDownloads(query, result);
+}
+
+// Query the history service for information about all persisted downloads.
+bool DownloadManager::Init(Profile* profile) {
+ DCHECK(profile);
+ DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
+ shutdown_needed_ = true;
+
+ profile_ = profile;
+ download_history_.reset(new DownloadHistory(profile));
+ download_history_->Load(
+ NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete));
+
+ download_prefs_.reset(new DownloadPrefs(profile_->GetPrefs()));
+
+ // In test mode, there may be no ResourceDispatcherHost. In this case it's
+ // safe to avoid setting |file_manager_| because we only call a small set of
+ // functions, none of which need it.
+ ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
+ if (rdh) {
+ file_manager_ = rdh->download_file_manager();
+ DCHECK(file_manager_);
+ }
+
+ other_download_manager_observer_.reset(
+ new OtherDownloadManagerObserver(this));
+
+ return true;
+}
+
+// We have received a message from DownloadFileManager about a new download.
+void DownloadManager::StartDownload(int32 download_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (delegate_->ShouldStartDownload(download_id))
+ RestartDownload(download_id);
+}
+
+void DownloadManager::CheckForHistoryFilesRemoval() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ for (DownloadMap::iterator it = history_downloads_.begin();
+ it != history_downloads_.end(); ++it) {
+ CheckForFileRemoval(it->second);
+ }
+}
+
+void DownloadManager::CheckForFileRemoval(DownloadItem* download_item) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (download_item->IsComplete() &&
+ !download_item->file_externally_removed()) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this,
+ &DownloadManager::CheckForFileRemovalOnFileThread,
+ download_item->db_handle(),
+ download_item->GetTargetFilePath()));
+ }
+}
+
+void DownloadManager::CheckForFileRemovalOnFileThread(
+ int64 db_handle, const FilePath& path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (!file_util::PathExists(path)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &DownloadManager::OnFileRemovalDetected,
+ db_handle));
+ }
+}
+
+void DownloadManager::OnFileRemovalDetected(int64 db_handle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DownloadMap::iterator it = history_downloads_.find(db_handle);
+ if (it != history_downloads_.end()) {
+ DownloadItem* download_item = it->second;
+ download_item->OnDownloadedFileRemoved();
+ }
+}
+
+void DownloadManager::RestartDownload(
+ int32 download_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " download = " << download->DebugString(true);
+
+ FilePath suggested_path = download->suggested_path();
+
+ if (download->prompt_user_for_save_location()) {
+ // We must ask the user for the place to put the download.
+ DownloadRequestHandle request_handle = download->request_handle();
+ TabContents* contents = request_handle.GetTabContents();
+
+ // |id_ptr| will be deleted in either FileSelected() or
+ // FileSelectionCancelled().
+ int32* id_ptr = new int32;
+ *id_ptr = download_id;
+
+ delegate_->ChooseDownloadPath(
+ contents, suggested_path, reinterpret_cast<void*>(id_ptr));
+
+ FOR_EACH_OBSERVER(Observer, observers_,
+ SelectFileDialogDisplayed(download_id));
+ } else {
+ // No prompting for download, just continue with the suggested name.
+ ContinueDownloadWithPath(download, suggested_path);
+ }
+}
+
+void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DownloadItem* download = new DownloadItem(this, *info,
+ profile_->IsOffTheRecord());
+ int32 download_id = info->download_id;
+ DCHECK(!ContainsKey(in_progress_, download_id));
+ DCHECK(!ContainsKey(active_downloads_, download_id));
+ downloads_.insert(download);
+ active_downloads_[download_id] = download;
+}
+
+void DownloadManager::ContinueDownloadWithPath(DownloadItem* download,
+ const FilePath& chosen_file) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(download);
+
+ int32 download_id = download->id();
+
+ // NOTE(ahendrickson) Eventually |active_downloads_| will replace
+ // |in_progress_|, but we don't want to change the semantics yet.
+ DCHECK(!ContainsKey(in_progress_, download_id));
+ DCHECK(ContainsKey(downloads_, download));
+ DCHECK(ContainsKey(active_downloads_, download_id));
+
+ // Make sure the initial file name is set only once.
+ DCHECK(download->full_path().empty());
+ download->OnPathDetermined(chosen_file);
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " download = " << download->DebugString(true);
+
+ in_progress_[download_id] = download;
+ UpdateAppIcon(); // Reflect entry into in_progress_.
+
+ // Rename to intermediate name.
+ FilePath download_path;
+ if (download->IsDangerous()) {
+ // The download is not safe. We can now rename the file to its
+ // tentative name using RenameInProgressDownloadFile.
+ // NOTE: The |Rename| below will be a no-op for dangerous files, as we're
+ // renaming it to the same name.
+ download_path = download->full_path();
+ } else {
+ // The download is a safe download. We need to
+ // rename it to its intermediate '.crdownload' path. The final
+ // name after user confirmation will be set from
+ // DownloadItem::OnDownloadCompleting.
+ download_path =
+ download_util::GetCrDownloadPath(download->full_path());
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ file_manager_, &DownloadFileManager::RenameInProgressDownloadFile,
+ download->id(), download_path));
+
+ download->Rename(download_path);
+
+ download_history_->AddEntry(download,
+ NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
+}
+
+void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DownloadMap::iterator it = active_downloads_.find(download_id);
+ if (it != active_downloads_.end()) {
+ DownloadItem* download = it->second;
+ if (download->IsInProgress()) {
+ download->Update(size);
+ UpdateAppIcon(); // Reflect size updates.
+ download_history_->UpdateEntry(download);
+ }
+ }
+}
+
+void DownloadManager::OnResponseCompleted(int32 download_id,
+ int64 size,
+ int os_error,
+ const std::string& hash) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // ERR_CONNECTION_CLOSED is allowed since a number of servers in the wild
+ // advertise a larger Content-Length than the amount of bytes in the message
+ // body, and then close the connection. Other browsers - IE8, Firefox 4.0.1,
+ // and Safari 5.0.4 - treat the download as complete in this case, so we
+ // follow their lead.
+ if (os_error == 0 || os_error == net::ERR_CONNECTION_CLOSED) {
+ OnAllDataSaved(download_id, size, hash);
+ } else {
+ OnDownloadError(download_id, size, os_error);
+ }
+}
+
+void DownloadManager::OnAllDataSaved(int32 download_id,
+ int64 size,
+ const std::string& hash) {
+ VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
+ << " size = " << size;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // If it's not in active_downloads_, that means it was cancelled; just
+ // ignore the notification.
+ if (active_downloads_.count(download_id) == 0)
+ return;
+
+ DownloadItem* download = active_downloads_[download_id];
+ download->OnAllDataSaved(size);
+
+ MaybeCompleteDownload(download);
+}
+
+void DownloadManager::AssertQueueStateConsistent(DownloadItem* download) {
+ // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
+ if (download->state() == DownloadItem::REMOVING) {
+ CHECK(!ContainsKey(downloads_, download));
+ CHECK(!ContainsKey(active_downloads_, download->id()));
+ CHECK(!ContainsKey(in_progress_, download->id()));
+ CHECK(!ContainsKey(history_downloads_, download->db_handle()));
+ return;
+ }
+
+ // Should be in downloads_ if we're not REMOVING.
+ CHECK(ContainsKey(downloads_, download));
+
+ // Check history_downloads_ consistency.
+ if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
+ CHECK(ContainsKey(history_downloads_, download->db_handle()));
+ } else {
+ // TODO(rdsmith): Somewhat painful; make sure to disable in
+ // release builds after resolution of http://crbug.com/85408.
+ for (DownloadMap::iterator it = history_downloads_.begin();
+ it != history_downloads_.end(); ++it) {
+ CHECK(it->second != download);
+ }
+ }
+
+ CHECK(ContainsKey(active_downloads_, download->id()) ==
+ (download->state() == DownloadItem::IN_PROGRESS));
+ CHECK(ContainsKey(in_progress_, download->id()) ==
+ (download->state() == DownloadItem::IN_PROGRESS));
+}
+
+bool DownloadManager::IsDownloadReadyForCompletion(DownloadItem* download) {
+ // If we don't have all the data, the download is not ready for
+ // completion.
+ if (!download->all_data_saved())
+ return false;
+
+ // If the download is dangerous, but not yet validated, it's not ready for
+ // completion.
+ if (download->safety_state() == DownloadItem::DANGEROUS)
+ return false;
+
+ // If the download isn't active (e.g. has been cancelled) it's not
+ // ready for completion.
+ if (active_downloads_.count(download->id()) == 0)
+ return false;
+
+ // If the download hasn't been inserted into the history system
+ // (which occurs strictly after file name determination, intermediate
+ // file rename, and UI display) then it's not ready for completion.
+ if (download->db_handle() == DownloadHistory::kUninitializedHandle)
+ return false;
+
+ return true;
+}
+
+void DownloadManager::MaybeCompleteDownload(DownloadItem* download) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ VLOG(20) << __FUNCTION__ << "()" << " download = "
+ << download->DebugString(false);
+
+ if (!IsDownloadReadyForCompletion(download))
+ return;
+
+ // TODO(rdsmith): DCHECK that we only pass through this point
+ // once per download. The natural way to do this is by a state
+ // transition on the DownloadItem.
+
+ // Confirm we're in the proper set of states to be here;
+ // in in_progress_, have all data, have a history handle, (validated or safe).
+ DCHECK_NE(DownloadItem::DANGEROUS, download->safety_state());
+ DCHECK_EQ(1u, in_progress_.count(download->id()));
+ DCHECK(download->all_data_saved());
+ DCHECK(download->db_handle() != DownloadHistory::kUninitializedHandle);
+ DCHECK_EQ(1u, history_downloads_.count(download->db_handle()));
+
+ VLOG(20) << __FUNCTION__ << "()" << " executing: download = "
+ << download->DebugString(false);
+
+ // Remove the id from in_progress
+ in_progress_.erase(download->id());
+ UpdateAppIcon(); // Reflect removal from in_progress_.
+
+ download_history_->UpdateEntry(download);
+
+ // Finish the download.
+ download->OnDownloadCompleting(file_manager_);
+}
+
+void DownloadManager::DownloadCompleted(int32 download_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DownloadItem* download = GetDownloadItem(download_id);
+ DCHECK(download);
+ download_history_->UpdateEntry(download);
+ active_downloads_.erase(download_id);
+}
+
+void DownloadManager::OnDownloadRenamedToFinalName(int download_id,
+ const FilePath& full_path,
+ int uniquifier) {
+ VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
+ << " full_path = \"" << full_path.value() << "\""
+ << " uniquifier = " << uniquifier;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DownloadItem* item = GetDownloadItem(download_id);
+ if (!item)
+ return;
+
+ if (item->safety_state() == DownloadItem::SAFE) {
+ DCHECK_EQ(0, uniquifier) << "We should not uniquify SAFE downloads twice";
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ file_manager_, &DownloadFileManager::CompleteDownload, download_id));
+
+ if (uniquifier)
+ item->set_path_uniquifier(uniquifier);
+
+ item->OnDownloadRenamedToFinalName(full_path);
+ download_history_->UpdateDownloadPath(item, full_path);
+}
+
+void DownloadManager::DownloadCancelled(int32 download_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DownloadMap::iterator it = in_progress_.find(download_id);
+ if (it == in_progress_.end())
+ return;
+ DownloadItem* download = it->second;
+
+ VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id
+ << " download = " << download->DebugString(true);
+
+ // Clean up will happen when the history system create callback runs if we
+ // don't have a valid db_handle yet.
+ if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
+ in_progress_.erase(it);
+ active_downloads_.erase(download_id);
+ UpdateAppIcon(); // Reflect removal from in_progress_.
+ download_history_->UpdateEntry(download);
+ }
+
+ DownloadCancelledInternal(download_id, download->request_handle());
+}
+
+void DownloadManager::DownloadCancelledInternal(
+ int download_id, const DownloadRequestHandle& request_handle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ request_handle.CancelRequest();
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ file_manager_, &DownloadFileManager::CancelDownload, download_id));
+}
+
+void DownloadManager::OnDownloadError(int32 download_id,
+ int64 size,
+ int os_error) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DownloadMap::iterator it = active_downloads_.find(download_id);
+ // A cancel at the right time could remove the download from the
+ // |active_downloads_| map before we get here.
+ if (it == active_downloads_.end())
+ return;
+
+ DownloadItem* download = it->second;
+
+ VLOG(20) << __FUNCTION__ << "()" << " Error " << os_error
+ << " at offset " << download->received_bytes()
+ << " for download = " << download->DebugString(true);
+
+ download->Interrupted(size, os_error);
+
+ // TODO(ahendrickson) - Remove this when we add resuming of interrupted
+ // downloads, as we will keep the download item around in that case.
+ //
+ // Clean up will happen when the history system create callback runs if we
+ // don't have a valid db_handle yet.
+ if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
+ in_progress_.erase(download_id);
+ active_downloads_.erase(download_id);
+ UpdateAppIcon(); // Reflect removal from in_progress_.
+ download_history_->UpdateEntry(download);
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ file_manager_, &DownloadFileManager::CancelDownload, download_id));
+}
+
+void DownloadManager::UpdateAppIcon() {
+ if (status_updater_)
+ status_updater_->Update();
+}
+
+int DownloadManager::RemoveDownloadItems(
+ const DownloadVector& pending_deletes) {
+ if (pending_deletes.empty())
+ return 0;
+
+ // Delete from internal maps.
+ for (DownloadVector::const_iterator it = pending_deletes.begin();
+ it != pending_deletes.end();
+ ++it) {
+ DownloadItem* download = *it;
+ DCHECK(download);
+ history_downloads_.erase(download->db_handle());
+ save_page_downloads_.erase(download->id());
+ downloads_.erase(download);
+ }
+
+ // Tell observers to refresh their views.
+ NotifyModelChanged();
+
+ // Delete the download items themselves.
+ const int num_deleted = static_cast<int>(pending_deletes.size());
+ STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
+ return num_deleted;
+}
+
+void DownloadManager::RemoveDownload(int64 download_handle) {
+ DownloadMap::iterator it = history_downloads_.find(download_handle);
+ if (it == history_downloads_.end())
+ return;
+
+ // Make history update.
+ DownloadItem* download = it->second;
+ download_history_->RemoveEntry(download);
+
+ // Remove from our tables and delete.
+ int downloads_count = RemoveDownloadItems(DownloadVector(1, download));
+ DCHECK_EQ(1, downloads_count);
+}
+
+int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin,
+ const base::Time remove_end) {
+ download_history_->RemoveEntriesBetween(remove_begin, remove_end);
+
+ // All downloads visible to the user will be in the history,
+ // so scan that map.
+ DownloadVector pending_deletes;
+ for (DownloadMap::const_iterator it = history_downloads_.begin();
+ it != history_downloads_.end();
+ ++it) {
+ DownloadItem* download = it->second;
+ if (download->start_time() >= remove_begin &&
+ (remove_end.is_null() || download->start_time() < remove_end) &&
+ (download->IsComplete() || download->IsCancelled())) {
+ AssertQueueStateConsistent(download);
+ pending_deletes.push_back(download);
+ }
+ }
+ return RemoveDownloadItems(pending_deletes);
+}
+
+int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
+ return RemoveDownloadsBetween(remove_begin, base::Time());
+}
+
+int DownloadManager::RemoveAllDownloads() {
+ if (this != profile_->GetOriginalProfile()->GetDownloadManager()) {
+ // This is an incognito downloader. Clear All should clear main download
+ // manager as well.
+ profile_->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads();
+ }
+ // The null times make the date range unbounded.
+ return RemoveDownloadsBetween(base::Time(), base::Time());
+}
+
+// Initiate a download of a specific URL. We send the request to the
+// ResourceDispatcherHost, and let it send us responses like a regular
+// download.
+void DownloadManager::DownloadUrl(const GURL& url,
+ const GURL& referrer,
+ const std::string& referrer_charset,
+ TabContents* tab_contents) {
+ DownloadUrlToFile(url, referrer, referrer_charset, DownloadSaveInfo(),
+ tab_contents);
+}
+
+void DownloadManager::DownloadUrlToFile(const GURL& url,
+ const GURL& referrer,
+ const std::string& referrer_charset,
+ const DownloadSaveInfo& save_info,
+ TabContents* tab_contents) {
+ DCHECK(tab_contents);
+ // We send a pointer to content::ResourceContext, instead of the usual
+ // reference, so that a copy of the object isn't made.
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ NewRunnableFunction(&download_util::DownloadUrl,
+ url,
+ referrer,
+ referrer_charset,
+ save_info,
+ g_browser_process->resource_dispatcher_host(),
+ tab_contents->GetRenderProcessHost()->id(),
+ tab_contents->render_view_host()->routing_id(),
+ &tab_contents->browser_context()->
+ GetResourceContext()));
+}
+
+void DownloadManager::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+ observer->ModelChanged();
+}
+
+void DownloadManager::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool DownloadManager::IsDownloadProgressKnown() {
+ for (DownloadMap::iterator i = in_progress_.begin();
+ i != in_progress_.end(); ++i) {
+ if (i->second->total_bytes() <= 0)
+ return false;
+ }
+
+ return true;
+}
+
+int64 DownloadManager::GetInProgressDownloadCount() {
+ return in_progress_.size();
+}
+
+int64 DownloadManager::GetReceivedDownloadBytes() {
+ DCHECK(IsDownloadProgressKnown());
+ int64 received_bytes = 0;
+ for (DownloadMap::iterator i = in_progress_.begin();
+ i != in_progress_.end(); ++i) {
+ received_bytes += i->second->received_bytes();
+ }
+ return received_bytes;
+}
+
+int64 DownloadManager::GetTotalDownloadBytes() {
+ DCHECK(IsDownloadProgressKnown());
+ int64 total_bytes = 0;
+ for (DownloadMap::iterator i = in_progress_.begin();
+ i != in_progress_.end(); ++i) {
+ total_bytes += i->second->total_bytes();
+ }
+ return total_bytes;
+}
+
+void DownloadManager::FileSelected(const FilePath& path, void* params) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ int32* id_ptr = reinterpret_cast<int32*>(params);
+ DCHECK(id_ptr != NULL);
+ int32 download_id = *id_ptr;
+ delete id_ptr;
+
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+ VLOG(20) << __FUNCTION__ << "()" << " path = \"" << path.value() << "\""
+ << " download = " << download->DebugString(true);
+
+ if (download->prompt_user_for_save_location())
+ last_download_path_ = path.DirName();
+
+ // Make sure the initial file name is set only once.
+ ContinueDownloadWithPath(download, path);
+}
+
+void DownloadManager::FileSelectionCanceled(void* params) {
+ // The user didn't pick a place to save the file, so need to cancel the
+ // download that's already in progress to the temporary location.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ int32* id_ptr = reinterpret_cast<int32*>(params);
+ DCHECK(id_ptr != NULL);
+ int32 download_id = *id_ptr;
+ delete id_ptr;
+
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+
+ VLOG(20) << __FUNCTION__ << "()"
+ << " download = " << download->DebugString(true);
+
+ DownloadCancelledInternal(download_id, download->request_handle());
+}
+
+// Operations posted to us from the history service ----------------------------
+
+// The history service has retrieved all download entries. 'entries' contains
+// 'DownloadHistoryInfo's in sorted order (by ascending start_time).
+void DownloadManager::OnQueryDownloadEntriesComplete(
+ std::vector<DownloadHistoryInfo>* entries) {
+ for (size_t i = 0; i < entries->size(); ++i) {
+ DownloadItem* download = new DownloadItem(this, entries->at(i));
+ DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
+ downloads_.insert(download);
+ history_downloads_[download->db_handle()] = download;
+ VLOG(20) << __FUNCTION__ << "()" << i << ">"
+ << " download = " << download->DebugString(true);
+ }
+ NotifyModelChanged();
+ CheckForHistoryFilesRemoval();
+}
+
+void DownloadManager::AddDownloadItemToHistory(DownloadItem* download,
+ int64 db_handle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // It's not immediately obvious, but HistoryBackend::CreateDownload() can
+ // call this function with an invalid |db_handle|. For instance, this can
+ // happen when the history database is offline. We cannot have multiple
+ // DownloadItems with the same invalid db_handle, so we need to assign a
+ // unique |db_handle| here.
+ if (db_handle == DownloadHistory::kUninitializedHandle)
+ db_handle = download_history_->GetNextFakeDbHandle();
+
+ // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/84508
+ // is fixed.
+ CHECK_NE(DownloadHistory::kUninitializedHandle, db_handle);
+
+ DCHECK(download->db_handle() == DownloadHistory::kUninitializedHandle);
+ download->set_db_handle(db_handle);
+
+ DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
+ history_downloads_[download->db_handle()] = download;
+
+ // Show in the appropriate browser UI.
+ // This includes buttons to save or cancel, for a dangerous download.
+ ShowDownloadInBrowser(download);
+
+ // Inform interested objects about the new download.
+ NotifyModelChanged();
+}
+
+// Once the new DownloadItem's creation info has been committed to the history
+// service, we associate the DownloadItem with the db handle, update our
+// 'history_downloads_' map and inform observers.
+void DownloadManager::OnCreateDownloadEntryComplete(int32 download_id,
+ int64 db_handle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DownloadItem* download = GetActiveDownloadItem(download_id);
+ if (!download)
+ return;
+
+ VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle
+ << " download_id = " << download_id
+ << " download = " << download->DebugString(true);
+
+ AddDownloadItemToHistory(download, db_handle);
+
+ // If the download is still in progress, try to complete it.
+ //
+ // Otherwise, download has been cancelled or interrupted before we've
+ // received the DB handle. We post one final message to the history
+ // service so that it can be properly in sync with the DownloadItem's
+ // completion status, and also inform any observers so that they get
+ // more than just the start notification.
+ if (download->IsInProgress()) {
+ MaybeCompleteDownload(download);
+ } else {
+ DCHECK(download->IsCancelled())
+ << " download = " << download->DebugString(true);
+ in_progress_.erase(download_id);
+ active_downloads_.erase(download_id);
+ download_history_->UpdateEntry(download);
+ download->UpdateObservers();
+ }
+}
+
+void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) {
+ // The 'contents' may no longer exist if the user closed the tab before we
+ // get this start completion event.
+ DownloadRequestHandle request_handle = download->request_handle();
+ TabContents* content = request_handle.GetTabContents();
+
+ // If the contents no longer exists, we ask the embedder to suggest another
+ // tab.
+ if (!content)
+ content = delegate_->GetAlternativeTabContentsToNotifyForDownload();
+
+ if (content)
+ content->OnStartDownload(download);
+}
+
+// Clears the last download path, used to initialize "save as" dialogs.
+void DownloadManager::ClearLastDownloadPath() {
+ last_download_path_ = FilePath();
+}
+
+void DownloadManager::NotifyModelChanged() {
+ FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
+}
+
+DownloadItem* DownloadManager::GetDownloadItem(int download_id) {
+ // The |history_downloads_| map is indexed by the download's db_handle,
+ // not its id, so we have to iterate.
+ for (DownloadMap::iterator it = history_downloads_.begin();
+ it != history_downloads_.end(); ++it) {
+ DownloadItem* item = it->second;
+ if (item->id() == download_id)
+ return item;
+ }
+ return NULL;
+}
+
+DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) {
+ DCHECK(ContainsKey(active_downloads_, download_id));
+ DownloadItem* download = active_downloads_[download_id];
+ DCHECK(download != NULL);
+ return download;
+}
+
+// Confirm that everything in all maps is also in |downloads_|, and that
+// everything in |downloads_| is also in some other map.
+void DownloadManager::AssertContainersConsistent() const {
+#if !defined(NDEBUG)
+ // Turn everything into sets.
+ const DownloadMap* input_maps[] = {&active_downloads_,
+ &history_downloads_,
+ &save_page_downloads_};
+ DownloadSet active_set, history_set, save_page_set;
+ DownloadSet* all_sets[] = {&active_set, &history_set, &save_page_set};
+ DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(all_sets));
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input_maps); i++) {
+ for (DownloadMap::const_iterator it = input_maps[i]->begin();
+ it != input_maps[i]->end(); ++it) {
+ all_sets[i]->insert(&*it->second);
+ }
+ }
+
+ // Check if each set is fully present in downloads, and create a union.
+ DownloadSet downloads_union;
+ for (int i = 0; i < static_cast<int>(ARRAYSIZE_UNSAFE(all_sets)); i++) {
+ DownloadSet remainder;
+ std::insert_iterator<DownloadSet> insert_it(remainder, remainder.begin());
+ std::set_difference(all_sets[i]->begin(), all_sets[i]->end(),
+ downloads_.begin(), downloads_.end(),
+ insert_it);
+ DCHECK(remainder.empty());
+ std::insert_iterator<DownloadSet>
+ insert_union(downloads_union, downloads_union.end());
+ std::set_union(downloads_union.begin(), downloads_union.end(),
+ all_sets[i]->begin(), all_sets[i]->end(),
+ insert_union);
+ }
+
+ // Is everything in downloads_ present in one of the other sets?
+ DownloadSet remainder;
+ std::insert_iterator<DownloadSet>
+ insert_remainder(remainder, remainder.begin());
+ std::set_difference(downloads_.begin(), downloads_.end(),
+ downloads_union.begin(), downloads_union.end(),
+ insert_remainder);
+ DCHECK(remainder.empty());
+#endif
+}
+
+// DownloadManager::OtherDownloadManagerObserver implementation ----------------
+
+DownloadManager::OtherDownloadManagerObserver::OtherDownloadManagerObserver(
+ DownloadManager* observing_download_manager)
+ : observing_download_manager_(observing_download_manager),
+ observed_download_manager_(NULL) {
+ if (observing_download_manager->profile_->GetOriginalProfile() ==
+ observing_download_manager->profile_) {
+ return;
+ }
+
+ observed_download_manager_ = observing_download_manager_->
+ profile_->GetOriginalProfile()->GetDownloadManager();
+ observed_download_manager_->AddObserver(this);
+}
+
+DownloadManager::OtherDownloadManagerObserver::~OtherDownloadManagerObserver() {
+ if (observed_download_manager_)
+ observed_download_manager_->RemoveObserver(this);
+}
+
+void DownloadManager::OtherDownloadManagerObserver::ModelChanged() {
+ observing_download_manager_->NotifyModelChanged();
+}
+
+void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() {
+ observed_download_manager_ = NULL;
+}
+
+void DownloadManager::SavePageDownloadStarted(DownloadItem* download) {
+ DCHECK(!ContainsKey(save_page_downloads_, download->id()));
+ downloads_.insert(download);
+ save_page_downloads_[download->id()] = download;
+
+ // Add this entry to the history service.
+ // Additionally, the UI is notified in the callback.
+ download_history_->AddEntry(download,
+ NewCallback(this, &DownloadManager::OnSavePageDownloadEntryAdded));
+}
+
+// SavePackage will call SavePageDownloadFinished upon completion/cancellation.
+// The history callback will call OnSavePageDownloadEntryAdded.
+// If the download finishes before the history callback,
+// OnSavePageDownloadEntryAdded calls SavePageDownloadFinished, ensuring that
+// the history event is update regardless of the order in which these two events
+// complete.
+// If something removes the download item from the download manager (Remove,
+// Shutdown) the result will be that the SavePage system will not be able to
+// properly update the download item (which no longer exists) or the download
+// history, but the action will complete properly anyway. This may lead to the
+// history entry being wrong on a reload of chrome (specifically in the case of
+// Initiation -> History Callback -> Removal -> Completion), but there's no way
+// to solve that without canceling on Remove (which would then update the DB).
+
+void DownloadManager::OnSavePageDownloadEntryAdded(int32 download_id,
+ int64 db_handle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DownloadMap::const_iterator it = save_page_downloads_.find(download_id);
+ // This can happen if the download manager is shutting down and all maps
+ // have been cleared.
+ if (it == save_page_downloads_.end())
+ return;
+
+ DownloadItem* download = it->second;
+ if (!download) {
+ NOTREACHED();
+ return;
+ }
+
+ AddDownloadItemToHistory(download, db_handle);
+
+ // Finalize this download if it finished before the history callback.
+ if (!download->IsInProgress())
+ SavePageDownloadFinished(download);
+}
+
+void DownloadManager::SavePageDownloadFinished(DownloadItem* download) {
+ if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
+ download_history_->UpdateEntry(download);
+ DCHECK(ContainsKey(save_page_downloads_, download->id()));
+ save_page_downloads_.erase(download->id());
+
+ if (download->IsComplete())
+ NotificationService::current()->Notify(
+ content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
+ Source<DownloadManager>(this),
+ Details<DownloadItem>(download));
+ }
+}
+
+int32 DownloadManager::GetNextSavePageId() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return next_save_page_id_++;
+}
+
diff --git a/content/browser/download/download_manager.h b/content/browser/download/download_manager.h
new file mode 100644
index 0000000..ee9d4d74
--- /dev/null
+++ b/content/browser/download/download_manager.h
@@ -0,0 +1,418 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The DownloadManager object manages the process of downloading, including
+// updates to the history system and providing the information for displaying
+// the downloads view in the Destinations tab. There is one DownloadManager per
+// active profile in Chrome.
+//
+// Download observers:
+// Objects that are interested in notifications about new downloads, or progress
+// updates for a given download must implement one of the download observer
+// interfaces:
+// DownloadManager::Observer:
+// - allows observers, primarily views, to be notified when changes to the
+// set of all downloads (such as new downloads, or deletes) occur
+// Use AddObserver() / RemoveObserver() on the appropriate download object to
+// receive state updates.
+//
+// Download state persistence:
+// The DownloadManager uses the history service for storing persistent
+// information about the state of all downloads. The history system maintains a
+// separate table for this called 'downloads'. At the point that the
+// DownloadManager is constructed, we query the history service for the state of
+// all persisted downloads.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/time.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/download/download_item.h"
+#include "content/browser/download/download_request_handle.h"
+#include "content/browser/download/download_status_updater_delegate.h"
+
+class DownloadFileManager;
+class DownloadHistory;
+class DownloadManagerDelegate;
+class DownloadPrefs;
+class DownloadStatusUpdater;
+class GURL;
+class Profile;
+class ResourceDispatcherHost;
+class TabContents;
+struct DownloadCreateInfo;
+struct DownloadHistoryInfo;
+struct DownloadSaveInfo;
+
+// Browser's download manager: manages all downloads and destination view.
+class DownloadManager
+ : public base::RefCountedThreadSafe<DownloadManager,
+ BrowserThread::DeleteOnUIThread>,
+ public DownloadStatusUpdaterDelegate {
+ public:
+ DownloadManager(DownloadManagerDelegate* delegate,
+ DownloadStatusUpdater* status_updater);
+
+ // Shutdown the download manager. Must be called before destruction.
+ void Shutdown();
+
+ // Interface to implement for observers that wish to be informed of changes
+ // to the DownloadManager's collection of downloads.
+ class Observer {
+ public:
+ // New or deleted download, observers should query us for the current set
+ // of downloads.
+ virtual void ModelChanged() = 0;
+
+ // Called when the DownloadManager is being destroyed to prevent Observers
+ // from calling back to a stale pointer.
+ virtual void ManagerGoingDown() {}
+
+ // Called immediately after the DownloadManager puts up a select file
+ // dialog.
+ // |id| indicates which download opened the dialog.
+ virtual void SelectFileDialogDisplayed(int32 id) {}
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ typedef std::vector<DownloadItem*> DownloadVector;
+
+ // Return all temporary downloads that reside in the specified directory.
+ void GetTemporaryDownloads(const FilePath& dir_path, DownloadVector* result);
+
+ // Return all non-temporary downloads in the specified directory that are
+ // are in progress or have completed.
+ void GetAllDownloads(const FilePath& dir_path, DownloadVector* result);
+
+ // Return all non-temporary downloads in the specified directory that are
+ // in-progress (including dangerous downloads waiting for user confirmation).
+ void GetCurrentDownloads(const FilePath& dir_path, DownloadVector* result);
+
+ // Returns all non-temporary downloads matching |query|. Empty query matches
+ // everything.
+ void SearchDownloads(const string16& query, DownloadVector* result);
+
+ // Returns true if initialized properly.
+ bool Init(Profile* profile);
+
+ // Notifications sent from the download thread to the UI thread
+ void StartDownload(int32 id);
+ void UpdateDownload(int32 download_id, int64 size);
+ // |hash| is sha256 hash for the downloaded file. It is empty when the hash
+ // is not available.
+ void OnResponseCompleted(int32 download_id, int64 size, int os_error,
+ const std::string& hash);
+
+ // Called from a view when a user clicks a UI button or link.
+ void DownloadCancelled(int32 download_id);
+ void RemoveDownload(int64 download_handle);
+
+ // Determine if the download is ready for completion, i.e. has had
+ // all data saved, and completed the filename determination and
+ // history insertion.
+ bool IsDownloadReadyForCompletion(DownloadItem* download);
+
+ // If all pre-requisites have been met, complete download processing, i.e.
+ // do internal cleanup, file rename, and potentially auto-open.
+ // (Dangerous downloads still may block on user acceptance after this
+ // point.)
+ void MaybeCompleteDownload(DownloadItem* download);
+
+ // Called when the download is renamed to its final name.
+ // |uniquifier| is a number used to make unique names for the file. It is
+ // only valid for the DANGEROUS_BUT_VALIDATED state of the download item.
+ void OnDownloadRenamedToFinalName(int download_id,
+ const FilePath& full_path,
+ int uniquifier);
+
+ // Remove downloads after remove_begin (inclusive) and before remove_end
+ // (exclusive). You may pass in null Time values to do an unbounded delete
+ // in either direction.
+ int RemoveDownloadsBetween(const base::Time remove_begin,
+ const base::Time remove_end);
+
+ // Remove downloads will delete all downloads that have a timestamp that is
+ // the same or more recent than |remove_begin|. The number of downloads
+ // deleted is returned back to the caller.
+ int RemoveDownloads(const base::Time remove_begin);
+
+ // Remove all downloads will delete all downloads. The number of downloads
+ // deleted is returned back to the caller.
+ int RemoveAllDownloads();
+
+ // Final download manager transition for download: Update the download
+ // history and remove the download from |active_downloads_|.
+ void DownloadCompleted(int32 download_id);
+
+ // Download the object at the URL. Used in cases such as "Save Link As..."
+ void DownloadUrl(const GURL& url,
+ const GURL& referrer,
+ const std::string& referrer_encoding,
+ TabContents* tab_contents);
+
+ // Download the object at the URL and save it to the specified path. The
+ // download is treated as the temporary download and thus will not appear
+ // in the download history. Used in cases such as drag and drop.
+ void DownloadUrlToFile(const GURL& url,
+ const GURL& referrer,
+ const std::string& referrer_encoding,
+ const DownloadSaveInfo& save_info,
+ TabContents* tab_contents);
+
+ // Allow objects to observe the download creation process.
+ void AddObserver(Observer* observer);
+
+ // Remove a download observer from ourself.
+ void RemoveObserver(Observer* observer);
+
+ // Methods called on completion of a query sent to the history system.
+ void OnQueryDownloadEntriesComplete(
+ std::vector<DownloadHistoryInfo>* entries);
+ void OnCreateDownloadEntryComplete(int32 download_id, int64 db_handle);
+
+ // Display a new download in the appropriate browser UI.
+ void ShowDownloadInBrowser(DownloadItem* download);
+
+ // The number of in progress (including paused) downloads.
+ int in_progress_count() const {
+ return static_cast<int>(in_progress_.size());
+ }
+
+ Profile* profile() { return profile_; }
+
+ DownloadHistory* download_history() { return download_history_.get(); }
+
+ DownloadPrefs* download_prefs() { return download_prefs_.get(); }
+
+ FilePath last_download_path() { return last_download_path_; }
+
+ // Creates the download item. Must be called on the UI thread.
+ void CreateDownloadItem(DownloadCreateInfo* info);
+
+ // Clears the last download path, used to initialize "save as" dialogs.
+ void ClearLastDownloadPath();
+
+ // Overridden from DownloadStatusUpdaterDelegate:
+ virtual bool IsDownloadProgressKnown();
+ virtual int64 GetInProgressDownloadCount();
+ virtual int64 GetReceivedDownloadBytes();
+ virtual int64 GetTotalDownloadBytes();
+
+ // Called by the delegate after the save as dialog is closed.
+ void FileSelected(const FilePath& path, void* params);
+ void FileSelectionCanceled(void* params);
+
+ // Called by the delegate if it delayed the download in
+ // DownloadManagerDelegate::ShouldStartDownload and now is ready.
+ void RestartDownload(int32 download_id);
+
+ // Checks whether downloaded files still exist. Updates state of downloads
+ // that refer to removed files. The check runs in the background and may
+ // finish asynchronously after this method returns.
+ void CheckForHistoryFilesRemoval();
+
+ // Checks whether a downloaded file still exists and updates the file's state
+ // if the file is already removed. The check runs in the background and may
+ // finish asynchronously after this method returns.
+ void CheckForFileRemoval(DownloadItem* download_item);
+
+ // Assert the named download item is on the correct queues
+ // in the DownloadManager. For debugging.
+ void AssertQueueStateConsistent(DownloadItem* download);
+
+ // Get the download item from the history map. Useful after the item has
+ // been removed from the active map, or was retrieved from the history DB.
+ DownloadItem* GetDownloadItem(int id);
+
+ // Called when Save Page download starts. Transfers ownership of |download|
+ // to the DownloadManager.
+ void SavePageDownloadStarted(DownloadItem* download);
+
+ // Callback when Save Page As entry is commited to the history system.
+ void OnSavePageDownloadEntryAdded(int32 download_id, int64 db_handle);
+
+ // Called when Save Page download is done.
+ void SavePageDownloadFinished(DownloadItem* download);
+
+ // Download Id for next Save Page.
+ int32 GetNextSavePageId();
+
+ // Get the download item from the active map. Useful when the item is not
+ // yet in the history map.
+ DownloadItem* GetActiveDownloadItem(int id);
+
+ DownloadManagerDelegate* delegate() const { return delegate_; }
+
+ private:
+ typedef std::set<DownloadItem*> DownloadSet;
+ typedef base::hash_map<int64, DownloadItem*> DownloadMap;
+
+ // For testing.
+ friend class DownloadManagerTest;
+ friend class MockDownloadManager;
+
+ // This class is used to let an incognito DownloadManager observe changes to
+ // a normal DownloadManager, to propagate ModelChanged() calls from the parent
+ // DownloadManager to the observers of the incognito DownloadManager.
+ class OtherDownloadManagerObserver : public Observer {
+ public:
+ explicit OtherDownloadManagerObserver(
+ DownloadManager* observing_download_manager);
+ virtual ~OtherDownloadManagerObserver();
+
+ // Observer interface.
+ virtual void ModelChanged();
+ virtual void ManagerGoingDown();
+
+ private:
+ // The incognito download manager.
+ DownloadManager* observing_download_manager_;
+
+ // The original profile's download manager.
+ DownloadManager* observed_download_manager_;
+ };
+
+ friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
+ friend class DeleteTask<DownloadManager>;
+ friend class OtherDownloadManagerObserver;
+
+ virtual ~DownloadManager();
+
+ // Called on the FILE thread to check the existence of a downloaded file.
+ void CheckForFileRemovalOnFileThread(int64 db_handle, const FilePath& path);
+
+ // Called on the UI thread if the FILE thread detects the removal of
+ // the downloaded file. The UI thread updates the state of the file
+ // and then notifies this update to the file's observer.
+ void OnFileRemovalDetected(int64 db_handle);
+
+ // Called back after a target path for the file to be downloaded to has been
+ // determined, either automatically based on the suggested file name, or by
+ // the user in a Save As dialog box.
+ void ContinueDownloadWithPath(DownloadItem* download,
+ const FilePath& chosen_file);
+
+ // Download cancel helper function.
+ void DownloadCancelledInternal(int download_id,
+ const DownloadRequestHandle& request_handle);
+
+ // All data has been downloaded.
+ // |hash| is sha256 hash for the downloaded file. It is empty when the hash
+ // is not available.
+ void OnAllDataSaved(int32 download_id, int64 size, const std::string& hash);
+
+ // An error occurred in the download.
+ void OnDownloadError(int32 download_id, int64 size, int os_error);
+
+ // Updates the app icon about the overall download progress.
+ void UpdateAppIcon();
+
+ // Inform observers that the model has changed.
+ void NotifyModelChanged();
+
+ // Debugging routine to confirm relationship between below
+ // containers; no-op if NDEBUG.
+ void AssertContainersConsistent() const;
+
+ // Add a DownloadItem to history_downloads_.
+ void AddDownloadItemToHistory(DownloadItem* item, int64 db_handle);
+
+ // Remove from internal maps.
+ int RemoveDownloadItems(const DownloadVector& pending_deletes);
+
+ // |downloads_| is the owning set for all downloads known to the
+ // DownloadManager. This includes downloads started by the user in
+ // this session, downloads initialized from the history system, and
+ // "save page as" downloads. All other DownloadItem containers in
+ // the DownloadManager are maps; they do not own the DownloadItems.
+ // Note that this is the only place (with any functional implications;
+ // see save_page_as_downloads_ below) that "save page as" downloads are
+ // kept, as the DownloadManager's only job is to hold onto those
+ // until destruction.
+ //
+ // |history_downloads_| is map of all downloads in this profile. The key
+ // is the handle returned by the history system, which is unique
+ // across sessions.
+ //
+ // |active_downloads_| is a map of all downloads that are currently being
+ // processed. The key is the ID assigned by the ResourceDispatcherHost,
+ // which is unique for the current session.
+ //
+ // |in_progress_| is a map of all downloads that are in progress and that have
+ // not yet received a valid history handle. The key is the ID assigned by the
+ // ResourceDispatcherHost, which is unique for the current session.
+ //
+ // |save_page_as_downloads_| (if defined) is a collection of all the
+ // downloads the "save page as" system has given to us to hold onto
+ // until we are destroyed. It is only used for debugging.
+ //
+ // When a download is created through a user action, the corresponding
+ // DownloadItem* is placed in |active_downloads_| and remains there until the
+ // download is in a terminal state (COMPLETE or CANCELLED). It is also
+ // placed in |in_progress_| and remains there until it has received a
+ // valid handle from the history system. Once it has a valid handle, the
+ // DownloadItem* is placed in the |history_downloads_| map. When the
+ // download reaches a terminal state, it is removed from |in_progress_|.
+ // Downloads from past sessions read from a persisted state from the
+ // history system are placed directly into |history_downloads_| since
+ // they have valid handles in the history system.
+
+ DownloadSet downloads_;
+ DownloadMap history_downloads_;
+ DownloadMap in_progress_;
+ DownloadMap active_downloads_;
+ DownloadMap save_page_downloads_;
+
+ // True if the download manager has been initialized and requires a shutdown.
+ bool shutdown_needed_;
+
+ // Observers that want to be notified of changes to the set of downloads.
+ ObserverList<Observer> observers_;
+
+ // The current active profile.
+ Profile* profile_;
+
+ scoped_ptr<DownloadHistory> download_history_;
+
+ scoped_ptr<DownloadPrefs> download_prefs_;
+
+ // Non-owning pointer for handling file writing on the download_thread_.
+ DownloadFileManager* file_manager_;
+
+ // Non-owning pointer for updating the download status.
+ base::WeakPtr<DownloadStatusUpdater> status_updater_;
+
+ // The user's last choice for download directory. This is only used when the
+ // user wants us to prompt for a save location for each download.
+ FilePath last_download_path_;
+
+ // Download Id for next Save Page.
+ int32 next_save_page_id_;
+
+ scoped_ptr<OtherDownloadManagerObserver> other_download_manager_observer_;
+
+ // Allows an embedder to control behavior. Guaranteed to outlive this object.
+ DownloadManagerDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadManager);
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_H_
diff --git a/content/browser/download/download_request_handle.cc b/content/browser/download/download_request_handle.cc
new file mode 100644
index 0000000..83b0df60
--- /dev/null
+++ b/content/browser/download/download_request_handle.cc
@@ -0,0 +1,113 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_request_handle.h"
+
+#include "base/stringprintf.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/tab_contents/tab_contents.h"
+
+// IO Thread indirections to resource dispatcher host.
+// Provided as targets for PostTask from within this object
+// only.
+static void ResourceDispatcherHostPauseRequest(
+ ResourceDispatcherHost* rdh,
+ int process_unique_id,
+ int request_id,
+ bool pause) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ rdh->PauseRequest(process_unique_id, request_id, pause);
+}
+
+static void ResourceDispatcherHostCancelRequest(
+ ResourceDispatcherHost* rdh,
+ int process_unique_id,
+ int request_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ rdh->CancelRequest(process_unique_id, request_id, false);
+}
+
+DownloadRequestHandle::DownloadRequestHandle()
+ : rdh_(NULL),
+ child_id_(-1),
+ render_view_id_(-1),
+ request_id_(-1) {
+}
+
+DownloadRequestHandle::DownloadRequestHandle(ResourceDispatcherHost* rdh,
+ int child_id,
+ int render_view_id,
+ int request_id)
+ : rdh_(rdh),
+ child_id_(child_id),
+ render_view_id_(render_view_id),
+ request_id_(request_id) {
+ // ResourceDispatcherHost should not be null for non-default instances
+ // of DownloadRequestHandle.
+ DCHECK(rdh);
+}
+
+TabContents* DownloadRequestHandle::GetTabContents() const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return tab_util::GetTabContentsByID(child_id_, render_view_id_);
+}
+
+DownloadManager* DownloadRequestHandle::GetDownloadManager() const {
+ TabContents* contents = GetTabContents();
+ if (!contents)
+ return NULL;
+
+ content::BrowserContext* browser_context = contents->browser_context();
+ if (!browser_context)
+ return NULL;
+
+ return browser_context->GetDownloadManager();
+}
+
+void DownloadRequestHandle::PauseRequest() const {
+ // The post is safe because ResourceDispatcherHost is guaranteed
+ // to outlive the IO thread.
+ if (rdh_) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableFunction(&ResourceDispatcherHostPauseRequest,
+ rdh_, child_id_, request_id_, true));
+ }
+}
+
+void DownloadRequestHandle::ResumeRequest() const {
+ // The post is safe because ResourceDispatcherHost is guaranteed
+ // to outlive the IO thread.
+ if (rdh_) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableFunction(&ResourceDispatcherHostPauseRequest,
+ rdh_, child_id_, request_id_, false));
+ }
+}
+
+void DownloadRequestHandle::CancelRequest() const {
+ // The post is safe because ResourceDispatcherHost is guaranteed
+ // to outlive the IO thread.
+ if (rdh_) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableFunction(&ResourceDispatcherHostCancelRequest,
+ rdh_, child_id_, request_id_));
+ }
+}
+
+std::string DownloadRequestHandle::DebugString() const {
+ return base::StringPrintf("{"
+ " child_id = %d"
+ " render_view_id = %d"
+ " request_id = %d"
+ "}",
+ child_id_,
+ render_view_id_,
+ request_id_);
+}
diff --git a/content/browser/download/download_request_handle.h b/content/browser/download/download_request_handle.h
new file mode 100644
index 0000000..c65ebe6
--- /dev/null
+++ b/content/browser/download/download_request_handle.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_HANDLE_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_HANDLE_H_
+#pragma once
+
+#include <string>
+
+class DownloadManager;
+class ResourceDispatcherHost;
+class TabContents;
+
+// A handle used by the download system for operations on the URLRequest
+// or objects conditional on it (e.g. TabContents).
+// This class needs to be copyable, so we can pass it across threads and not
+// worry about lifetime or const-ness.
+class DownloadRequestHandle {
+ public:
+ // Create a null DownloadRequestHandle: getters will return null, and
+ // all actions are no-ops.
+ // TODO(rdsmith): Ideally, actions would be forbidden rather than
+ // no-ops, to confirm that no non-testing code actually uses
+ // a null DownloadRequestHandle. But for now, we need the no-op
+ // behavior for unit tests. Long-term, this should be fixed by
+ // allowing mocking of ResourceDispatcherHost in unit tests.
+ DownloadRequestHandle();
+
+ // Note that |rdh| is required to be non-null.
+ DownloadRequestHandle(ResourceDispatcherHost* rdh,
+ int child_id,
+ int render_view_id,
+ int request_id);
+
+ // These functions must be called on the UI thread.
+ TabContents* GetTabContents() const;
+ DownloadManager* GetDownloadManager() const;
+
+ // Pause or resume the matching URL request.
+ void PauseRequest() const;
+ void ResumeRequest() const;
+
+ // Cancel the request
+ void CancelRequest() const;
+
+ std::string DebugString() const;
+
+ private:
+ // The resource dispatcher host.
+ ResourceDispatcherHost* rdh_;
+
+ // The ID of the child process that started the download.
+ int child_id_;
+
+ // The ID of the render view that started the download.
+ int render_view_id_;
+
+ // The ID associated with the request used for the download.
+ int request_id_;
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_HANDLE_H_
diff --git a/content/browser/download/download_state_info.cc b/content/browser/download/download_state_info.cc
new file mode 100644
index 0000000..0631129e
--- /dev/null
+++ b/content/browser/download/download_state_info.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_state_info.h"
+
+#include "content/browser/download/download_item.h"
+
+DownloadStateInfo::DownloadStateInfo()
+ : path_uniquifier(0),
+ has_user_gesture(false),
+ prompt_user_for_save_location(false),
+ is_dangerous_file(false),
+ is_dangerous_url(false),
+ is_extension_install(false) {
+}
+
+DownloadStateInfo::DownloadStateInfo(
+ bool has_user_gesture,
+ bool prompt_user_for_save_location)
+ : path_uniquifier(0),
+ has_user_gesture(has_user_gesture),
+ prompt_user_for_save_location(prompt_user_for_save_location),
+ is_dangerous_file(false),
+ is_dangerous_url(false),
+ is_extension_install(false) {
+}
+
+DownloadStateInfo::DownloadStateInfo(
+ const FilePath& target,
+ const FilePath& forced_name,
+ bool has_user_gesture,
+ bool prompt_user_for_save_location,
+ int uniquifier,
+ bool dangerous_file,
+ bool dangerous_url,
+ bool extension_install)
+ : target_name(target),
+ path_uniquifier(uniquifier),
+ has_user_gesture(has_user_gesture),
+ prompt_user_for_save_location(prompt_user_for_save_location),
+ is_dangerous_file(dangerous_file),
+ is_dangerous_url(dangerous_url),
+ is_extension_install(extension_install),
+ force_file_name(forced_name) {
+}
+
+bool DownloadStateInfo::IsDangerous() const {
+ return is_dangerous_url || is_dangerous_file;
+}
diff --git a/content/browser/download/download_state_info.h b/content/browser/download/download_state_info.h
new file mode 100644
index 0000000..5ca4c40
--- /dev/null
+++ b/content/browser/download/download_state_info.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_STATE_INFO_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_STATE_INFO_H_
+#pragma once
+
+#include "base/file_path.h"
+
+// Contains information relating to the process of determining what to do with
+// the download.
+struct DownloadStateInfo {
+ DownloadStateInfo();
+ DownloadStateInfo(bool has_user_gesture,
+ bool prompt_user_for_save_location);
+ DownloadStateInfo(const FilePath& target,
+ const FilePath& forced_name,
+ bool has_user_gesture,
+ bool prompt_user_for_save_location,
+ int uniquifier,
+ bool dangerous_file,
+ bool dangerous_url,
+ bool extension_install);
+
+ // Indicates if the download is dangerous.
+ bool IsDangerous() const;
+
+ // The original name for a dangerous download, specified by the request.
+ FilePath target_name;
+
+ // The path where we save the download. Typically generated.
+ FilePath suggested_path;
+
+ // A number that should be added to the suggested path to make it unique.
+ // 0 means no number should be appended. It is eventually incorporated
+ // into the final file name.
+ int path_uniquifier;
+
+ // True if the download is the result of user action.
+ bool has_user_gesture;
+
+ // True if we should display the 'save as...' UI and prompt the user
+ // for the download location.
+ // False if the UI should be suppressed and the download performed to the
+ // default location.
+ bool prompt_user_for_save_location;
+
+ // True if this download file is potentially dangerous (ex: exe, dll, ...).
+ bool is_dangerous_file;
+
+ // If safebrowsing believes this URL leads to malware.
+ bool is_dangerous_url;
+
+ // True if this download is for extension install.
+ bool is_extension_install;
+
+ // True if this download's file name was specified initially.
+ FilePath force_file_name;
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_STATE_INFO_H_
diff --git a/content/browser/download/download_status_updater.cc b/content/browser/download/download_status_updater.cc
new file mode 100644
index 0000000..c2988cf0
--- /dev/null
+++ b/content/browser/download/download_status_updater.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_status_updater.h"
+
+#include "base/logging.h"
+#include "chrome/browser/download/download_util.h"
+#include "content/browser/download/download_status_updater_delegate.h"
+
+DownloadStatusUpdater::DownloadStatusUpdater() {
+}
+
+DownloadStatusUpdater::~DownloadStatusUpdater() {
+}
+
+void DownloadStatusUpdater::AddDelegate(
+ DownloadStatusUpdaterDelegate* delegate) {
+ delegates_.insert(delegate);
+ Update();
+}
+
+void DownloadStatusUpdater::RemoveDelegate(
+ DownloadStatusUpdaterDelegate* delegate) {
+ delegates_.erase(delegate);
+ Update();
+}
+
+void DownloadStatusUpdater::Update() {
+ float progress = 0;
+ bool progress_known = GetProgress(&progress);
+ download_util::UpdateAppIconDownloadProgress(
+ static_cast<int>(GetInProgressDownloadCount()),
+ progress_known,
+ progress);
+}
+
+bool DownloadStatusUpdater::GetProgress(float* progress) {
+ *progress = 0;
+
+ int64 received_bytes = 0;
+ int64 total_bytes = 0;
+ for (DelegateSet::iterator i = delegates_.begin();
+ i != delegates_.end(); ++i) {
+ if (!(*i)->IsDownloadProgressKnown())
+ return false;
+ received_bytes += (*i)->GetReceivedDownloadBytes();
+ total_bytes += (*i)->GetTotalDownloadBytes();
+ }
+
+ if (total_bytes > 0)
+ *progress = static_cast<float>(received_bytes) / total_bytes;
+ return true;
+}
+
+int64 DownloadStatusUpdater::GetInProgressDownloadCount() {
+ int64 download_count = 0;
+ for (DelegateSet::iterator i = delegates_.begin();
+ i != delegates_.end(); ++i) {
+ download_count += (*i)->GetInProgressDownloadCount();
+ }
+
+ return download_count;
+}
diff --git a/content/browser/download/download_status_updater.h b/content/browser/download/download_status_updater.h
new file mode 100644
index 0000000..0b834ec
--- /dev/null
+++ b/content/browser/download/download_status_updater.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_STATUS_UPDATER_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_STATUS_UPDATER_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
+
+class DownloadStatusUpdaterDelegate;
+
+// Keeps track of download progress for the entire browser.
+class DownloadStatusUpdater
+ : public base::SupportsWeakPtr<DownloadStatusUpdater> {
+ public:
+ DownloadStatusUpdater();
+ ~DownloadStatusUpdater();
+
+ void AddDelegate(DownloadStatusUpdaterDelegate* delegate);
+ void RemoveDelegate(DownloadStatusUpdaterDelegate* delegate);
+
+ // Updates the download status based on data from delegates.
+ void Update();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(DownloadStatusUpdaterTest, Basic);
+ FRIEND_TEST_ALL_PREFIXES(DownloadStatusUpdaterTest, OneDelegate);
+ FRIEND_TEST_ALL_PREFIXES(DownloadStatusUpdaterTest, MultipleDelegates);
+
+ // If the progress is known (i.e. we know the final size of all downloads),
+ // returns true and puts a percentage (in range [0-1]) in |progress|.
+ bool GetProgress(float* progress);
+
+ // Returns the number of downloads that are in progress.
+ int64 GetInProgressDownloadCount();
+
+ typedef std::set<DownloadStatusUpdaterDelegate*> DelegateSet;
+ DelegateSet delegates_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadStatusUpdater);
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_STATUS_UPDATER_H_
diff --git a/content/browser/download/download_status_updater_delegate.h b/content/browser/download/download_status_updater_delegate.h
new file mode 100644
index 0000000..72c2eda
--- /dev/null
+++ b/content/browser/download/download_status_updater_delegate.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_STATUS_UPDATER_DELEGATE_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_STATUS_UPDATER_DELEGATE_H_
+
+#include "base/basictypes.h"
+
+class DownloadStatusUpdaterDelegate {
+ public:
+ // Returns true if the progress is known (i.e. we know the final size
+ // of all downloads).
+ virtual bool IsDownloadProgressKnown() = 0;
+
+ // Returns the number of downloads that are in progress.
+ virtual int64 GetInProgressDownloadCount() = 0;
+
+ // Returns the amount of received data, in bytes.
+ virtual int64 GetReceivedDownloadBytes() = 0;
+
+ // Returns the final size of all downloads, in bytes.
+ virtual int64 GetTotalDownloadBytes() = 0;
+
+ protected:
+ virtual ~DownloadStatusUpdaterDelegate() {}
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_STATUS_UPDATER_DELEGATE_H_
diff --git a/content/browser/download/download_status_updater_unittest.cc b/content/browser/download/download_status_updater_unittest.cc
new file mode 100644
index 0000000..368137b
--- /dev/null
+++ b/content/browser/download/download_status_updater_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/download/download_status_updater.h"
+#include "content/browser/download/download_status_updater_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockDelegate : public DownloadStatusUpdaterDelegate {
+ public:
+ explicit MockDelegate(DownloadStatusUpdater* updater)
+ : updater_(updater->AsWeakPtr()),
+ is_download_progress_known_(true),
+ in_progress_download_count_(0),
+ received_bytes_(0),
+ total_bytes_(0) {
+ EXPECT_TRUE(updater);
+ if (updater_)
+ updater_->AddDelegate(this);
+ }
+
+ ~MockDelegate() {
+ EXPECT_TRUE(updater_);
+ if (updater_)
+ updater_->RemoveDelegate(this);
+ }
+
+ // Overriden from DownloadStatusUpdaterDelegate:
+ virtual bool IsDownloadProgressKnown() {
+ return is_download_progress_known_;
+ }
+
+ virtual int64 GetInProgressDownloadCount() {
+ return in_progress_download_count_;
+ }
+
+ virtual int64 GetReceivedDownloadBytes() {
+ return received_bytes_;
+ }
+
+ virtual int64 GetTotalDownloadBytes() {
+ return total_bytes_;
+ }
+
+ void set_is_download_progress_known(bool progress_known) {
+ is_download_progress_known_ = progress_known;
+ }
+
+ void set_in_progress_download_count(int64 download_count) {
+ in_progress_download_count_ = download_count;
+ }
+
+ void set_received_bytes(int64 received_bytes) {
+ received_bytes_ = received_bytes;
+ }
+
+ void set_total_bytes(int64 total_bytes) {
+ total_bytes_ = total_bytes;
+ }
+
+ private:
+ base::WeakPtr<DownloadStatusUpdater> updater_;
+
+ bool is_download_progress_known_;
+ int64 in_progress_download_count_;
+ int64 received_bytes_;
+ int64 total_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockDelegate);
+};
+
+} // namespace
+
+class DownloadStatusUpdaterTest : public testing::Test {
+ protected:
+ DownloadStatusUpdater updater_;
+};
+
+TEST_F(DownloadStatusUpdaterTest, Basic) {
+ float progress = -1;
+ EXPECT_TRUE(updater_.GetProgress(&progress));
+ EXPECT_FLOAT_EQ(0, progress);
+ EXPECT_EQ(0, updater_.GetInProgressDownloadCount());
+}
+
+TEST_F(DownloadStatusUpdaterTest, OneDelegate) {
+ MockDelegate delegate(&updater_);
+
+ {
+ float progress = -1;
+ EXPECT_TRUE(updater_.GetProgress(&progress));
+ EXPECT_FLOAT_EQ(0, progress);
+ EXPECT_EQ(0, updater_.GetInProgressDownloadCount());
+ }
+
+ delegate.set_in_progress_download_count(1);
+ delegate.set_received_bytes(21);
+ delegate.set_total_bytes(42);
+
+ {
+ float progress = -1;
+ EXPECT_TRUE(updater_.GetProgress(&progress));
+ EXPECT_FLOAT_EQ(static_cast<float>(1) / 2, progress);
+ EXPECT_EQ(1, updater_.GetInProgressDownloadCount());
+ }
+
+ delegate.set_is_download_progress_known(false);
+
+ {
+ float progress = -1;
+ EXPECT_FALSE(updater_.GetProgress(&progress));
+ EXPECT_FLOAT_EQ(0, progress);
+ EXPECT_EQ(1, updater_.GetInProgressDownloadCount());
+ }
+}
+
+TEST_F(DownloadStatusUpdaterTest, MultipleDelegates) {
+ scoped_ptr<MockDelegate> delegate1(new MockDelegate(&updater_));
+ scoped_ptr<MockDelegate> delegate2(new MockDelegate(&updater_));
+
+ {
+ float progress = -1;
+ EXPECT_TRUE(updater_.GetProgress(&progress));
+ EXPECT_FLOAT_EQ(0, progress);
+ EXPECT_EQ(0, updater_.GetInProgressDownloadCount());
+ }
+
+ delegate1->set_in_progress_download_count(1);
+ delegate1->set_received_bytes(14);
+ delegate1->set_total_bytes(21);
+
+ delegate2->set_in_progress_download_count(2);
+ delegate2->set_received_bytes(7);
+ delegate2->set_total_bytes(21);
+
+ {
+ float progress = -1;
+ EXPECT_TRUE(updater_.GetProgress(&progress));
+ EXPECT_FLOAT_EQ(static_cast<float>(1) / 2, progress);
+ EXPECT_EQ(3, updater_.GetInProgressDownloadCount());
+ }
+
+ delegate1->set_is_download_progress_known(false);
+
+ {
+ float progress = -1;
+ EXPECT_FALSE(updater_.GetProgress(&progress));
+ EXPECT_FLOAT_EQ(0, progress);
+ EXPECT_EQ(3, updater_.GetInProgressDownloadCount());
+ }
+
+ delegate1.reset();
+
+ {
+ float progress = -1;
+ EXPECT_TRUE(updater_.GetProgress(&progress));
+ EXPECT_FLOAT_EQ(static_cast<float>(1) / 3, progress);
+ EXPECT_EQ(2, updater_.GetInProgressDownloadCount());
+ }
+}
diff --git a/content/browser/download/download_types.cc b/content/browser/download/download_types.cc
new file mode 100644
index 0000000..b54d7f20
--- /dev/null
+++ b/content/browser/download/download_types.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_types.h"
+
+DownloadBuffer::DownloadBuffer() {
+}
+
+DownloadBuffer::~DownloadBuffer() {
+}
+
+DownloadSaveInfo::DownloadSaveInfo() {
+}
+
+DownloadSaveInfo::DownloadSaveInfo(const DownloadSaveInfo& info)
+ : file_path(info.file_path),
+ file_stream(info.file_stream),
+ suggested_name(info.suggested_name) {
+}
+
+DownloadSaveInfo::~DownloadSaveInfo() {
+}
+
+DownloadSaveInfo& DownloadSaveInfo::operator=(const DownloadSaveInfo& info) {
+ file_path = info.file_path;
+ file_stream = info.file_stream;
+ suggested_name = info.suggested_name;
+ return *this;
+}
diff --git a/content/browser/download/download_types.h b/content/browser/download/download_types.h
new file mode 100644
index 0000000..b4f4a366
--- /dev/null
+++ b/content/browser/download/download_types.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_TYPES_H_
+#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_TYPES_H_
+#pragma once
+
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/memory/linked_ptr.h"
+#include "base/synchronization/lock.h"
+#include "net/base/file_stream.h"
+
+namespace net {
+class IOBuffer;
+}
+
+// DownloadBuffer is created and populated on the IO thread, and passed to the
+// file thread for writing. In order to avoid flooding the file thread with too
+// many small write messages, each write is appended to the DownloadBuffer while
+// waiting for the task to run on the file thread. Access to the write buffers
+// is synchronized via the lock. Each entry in 'contents' represents one data
+// buffer and its size in bytes.
+struct DownloadBuffer {
+ DownloadBuffer();
+ ~DownloadBuffer();
+
+ base::Lock lock;
+ typedef std::pair<net::IOBuffer*, int> Contents;
+ std::vector<Contents> contents;
+};
+
+// Holds the information about how to save a download file.
+struct DownloadSaveInfo {
+ DownloadSaveInfo();
+ DownloadSaveInfo(const DownloadSaveInfo& info);
+ ~DownloadSaveInfo();
+ DownloadSaveInfo& operator=(const DownloadSaveInfo& info);
+
+ FilePath file_path;
+ linked_ptr<net::FileStream> file_stream;
+ string16 suggested_name;
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_TYPES_H_
diff --git a/content/browser/download/mock_download_manager.h b/content/browser/download/mock_download_manager.h
new file mode 100644
index 0000000..48689b5
--- /dev/null
+++ b/content/browser/download/mock_download_manager.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_MOCK_DOWNLOAD_MANAGER_H_
+#define CONTENT_BROWSER_DOWNLOAD_MOCK_DOWNLOAD_MANAGER_H_
+#pragma once
+
+#include "content/browser/download/download_manager.h"
+
+class DownloadStatusUpdater;
+class DownloadItem;
+
+class MockDownloadManager : public DownloadManager {
+ public:
+ explicit MockDownloadManager(DownloadManagerDelegate* delegate,
+ DownloadStatusUpdater* updater)
+ : DownloadManager(delegate, updater) {
+ }
+
+ // Override some functions.
+ virtual void UpdateHistoryForDownload(DownloadItem*) { }
+};
+
+#endif // CONTENT_BROWSER_DOWNLOAD_MOCK_DOWNLOAD_MANAGER_H_
diff --git a/content/browser/download/save_package.cc b/content/browser/download/save_package.cc
index 82b4153..e831b40 100644
--- a/content/browser/download/save_package.cc
+++ b/content/browser/download/save_package.cc
@@ -19,14 +19,14 @@
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/download/download_history.h"
-#include "chrome/browser/download/download_item.h"
#include "chrome/browser/download/download_item_model.h"
-#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/download/download_manager_delegate.h"
#include "chrome/browser/download/download_util.h"
#include "content/browser/browser_context.h"
#include "content/browser/browser_thread.h"
#include "content/browser/content_browser_client.h"
+#include "content/browser/download/download_item.h"
+#include "content/browser/download/download_manager.h"
#include "content/browser/download/save_file.h"
#include "content/browser/download/save_file_manager.h"
#include "content/browser/download/save_item.h"
diff --git a/content/browser/download/save_package.h b/content/browser/download/save_package.h
index 76e4b62..824a336 100644
--- a/content/browser/download/save_package.h
+++ b/content/browser/download/save_package.h
@@ -17,7 +17,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/task.h"
-#include "chrome/browser/download/download_manager.h"
+#include "content/browser/download/download_manager.h"
#include "content/browser/tab_contents/tab_contents_observer.h"
#include "googleurl/src/gurl.h"