blob: 7577208cf20139e4b2942b230b6cc5bc71d5b035 [file] [log] [blame]
// Copyright (c) 2012 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 <set>
#include <string>
#include "base/bind.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/utf_string_conversions.h"
#include "content/browser/download/base_file.h"
#include "content/browser/download/download_create_info.h"
#include "content/browser/download/download_file_impl.h"
#include "content/browser/download/download_interrupt_reasons_impl.h"
#include "content/browser/download/download_request_handle.h"
#include "content/browser/download/download_stats.h"
#include "content/browser/power_save_blocker.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/download_manager_delegate.h"
#include "googleurl/src/gurl.h"
#include "net/base/io_buffer.h"
using content::BrowserThread;
using content::DownloadFile;
using content::DownloadId;
using content::DownloadManager;
DownloadFileManager::DownloadFileManager(content::DownloadFileFactory* factory)
: download_file_factory_(factory) {
if (download_file_factory_ == NULL)
download_file_factory_.reset(new content::DownloadFileFactory);
}
DownloadFileManager::~DownloadFileManager() {
DCHECK(downloads_.empty());
}
void DownloadFileManager::Shutdown() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&DownloadFileManager::OnShutdown, this));
}
void DownloadFileManager::OnShutdown() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
STLDeleteValues(&downloads_);
}
void DownloadFileManager::CreateDownloadFile(
scoped_ptr<DownloadCreateInfo> info,
scoped_ptr<content::ByteStreamReader> stream,
scoped_refptr<DownloadManager> download_manager, bool get_hash,
const net::BoundNetLog& bound_net_log,
const CreateDownloadFileCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
DCHECK(info.get());
VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString();
scoped_ptr<DownloadFile> download_file(download_file_factory_->CreateFile(
info.get(), stream.Pass(), download_manager, get_hash, bound_net_log));
content::DownloadInterruptReason interrupt_reason(
download_file->Initialize());
if (interrupt_reason == content::DOWNLOAD_INTERRUPT_REASON_NONE) {
DCHECK(GetDownloadFile(info->download_id) == NULL);
downloads_[info->download_id] = download_file.release();
}
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(callback, interrupt_reason));
}
DownloadFile* DownloadFileManager::GetDownloadFile(
DownloadId global_id) {
DownloadFileMap::iterator it = downloads_.find(global_id);
return it == downloads_.end() ? NULL : it->second;
}
// 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(DownloadId global_id) {
VLOG(20) << __FUNCTION__ << "()" << " id = " << global_id;
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
DownloadFileMap::iterator it = downloads_.find(global_id);
if (it == downloads_.end())
return;
DownloadFile* download_file = it->second;
VLOG(20) << __FUNCTION__ << "()"
<< " download_file = " << download_file->DebugString();
download_file->Cancel();
EraseDownload(global_id);
}
void DownloadFileManager::CompleteDownload(
DownloadId global_id, const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
if (!ContainsKey(downloads_, global_id))
return;
DownloadFile* download_file = downloads_[global_id];
VLOG(20) << " " << __FUNCTION__ << "()"
<< " id = " << global_id
<< " download_file = " << download_file->DebugString();
download_file->Detach(callback);
EraseDownload(global_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)->GlobalId());
delete *i;
}
}
// Actions from the UI thread and run on the download thread
void DownloadFileManager::RenameDownloadFile(
DownloadId global_id,
const FilePath& full_path,
bool overwrite_existing_file,
const RenameCompletionCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
DownloadFile* download_file = GetDownloadFile(global_id);
if (!download_file) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(callback, content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED,
FilePath()));
return;
}
download_file->Rename(full_path, overwrite_existing_file, callback);
}
int DownloadFileManager::NumberOfActiveDownloads() const {
return downloads_.size();
}
void DownloadFileManager::EraseDownload(DownloadId global_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
if (!ContainsKey(downloads_, global_id))
return;
DownloadFile* download_file = downloads_[global_id];
VLOG(20) << " " << __FUNCTION__ << "()"
<< " id = " << global_id
<< " download_file = " << download_file->DebugString();
downloads_.erase(global_id);
delete download_file;
}