This source file includes following definitions.
- Get
- state
- SetState
- info
- set_info
- clear_info
- GetDownloadRow
- ShouldUpdateHistory
- QueryDownloads
- CreateDownload
- UpdateDownload
- RemoveDownloads
- IsPersisted
- weak_ptr_factory_
- AddObserver
- RemoveObserver
- QueryCallback
- MaybeAddToHistory
- ItemAdded
- OnDownloadCreated
- OnDownloadUpdated
- OnDownloadOpened
- OnDownloadRemoved
- ScheduleRemoveDownload
- RemoveDownloadsBatch
#include "chrome/browser/download/download_history.h"
#include "base/metrics/histogram.h"
#include "chrome/browser/download/download_crx_util.h"
#include "chrome/browser/history/download_database.h"
#include "chrome/browser/history/download_row.h"
#include "chrome/browser/history/history_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager.h"
#if !defined(OS_ANDROID)
#include "chrome/browser/extensions/api/downloads/downloads_api.h"
#endif
namespace {
class DownloadHistoryData : public base::SupportsUserData::Data {
public:
enum PersistenceState {
NOT_PERSISTED,
PERSISTING,
PERSISTED,
};
static DownloadHistoryData* Get(content::DownloadItem* item) {
base::SupportsUserData::Data* data = item->GetUserData(kKey);
return (data == NULL) ? NULL :
static_cast<DownloadHistoryData*>(data);
}
explicit DownloadHistoryData(content::DownloadItem* item)
: state_(NOT_PERSISTED) {
item->SetUserData(kKey, this);
}
virtual ~DownloadHistoryData() {
}
PersistenceState state() const { return state_; }
void SetState(PersistenceState s) { state_ = s; }
history::DownloadRow* info() { return info_.get(); }
void set_info(const history::DownloadRow& i) {
info_.reset(new history::DownloadRow(i));
}
void clear_info() {
info_.reset();
}
private:
static const char kKey[];
PersistenceState state_;
scoped_ptr<history::DownloadRow> info_;
DISALLOW_COPY_AND_ASSIGN(DownloadHistoryData);
};
const char DownloadHistoryData::kKey[] =
"DownloadItem DownloadHistoryData";
history::DownloadRow GetDownloadRow(
content::DownloadItem* item) {
std::string by_ext_id, by_ext_name;
#if !defined(OS_ANDROID)
DownloadedByExtension* by_ext = DownloadedByExtension::Get(item);
if (by_ext) {
by_ext_id = by_ext->id();
by_ext_name = by_ext->name();
}
#endif
return history::DownloadRow(
item->GetFullPath(),
item->GetTargetFilePath(),
item->GetUrlChain(),
item->GetReferrerUrl(),
item->GetStartTime(),
item->GetEndTime(),
item->GetETag(),
item->GetLastModifiedTime(),
item->GetReceivedBytes(),
item->GetTotalBytes(),
item->GetState(),
item->GetDangerType(),
item->GetLastReason(),
item->GetId(),
item->GetOpened(),
by_ext_id,
by_ext_name);
}
bool ShouldUpdateHistory(const history::DownloadRow* previous,
const history::DownloadRow& current) {
return ((previous == NULL) ||
(previous->current_path != current.current_path) ||
(previous->target_path != current.target_path) ||
(previous->end_time != current.end_time) ||
(previous->received_bytes != current.received_bytes) ||
(previous->total_bytes != current.total_bytes) ||
(previous->etag != current.etag) ||
(previous->last_modified != current.last_modified) ||
(previous->state != current.state) ||
(previous->danger_type != current.danger_type) ||
(previous->interrupt_reason != current.interrupt_reason) ||
(previous->opened != current.opened) ||
(previous->by_ext_id != current.by_ext_id) ||
(previous->by_ext_name != current.by_ext_name));
}
typedef std::vector<history::DownloadRow> InfoVector;
}
DownloadHistory::HistoryAdapter::HistoryAdapter(HistoryService* history)
: history_(history) {
}
DownloadHistory::HistoryAdapter::~HistoryAdapter() {}
void DownloadHistory::HistoryAdapter::QueryDownloads(
const HistoryService::DownloadQueryCallback& callback) {
history_->QueryDownloads(callback);
}
void DownloadHistory::HistoryAdapter::CreateDownload(
const history::DownloadRow& info,
const HistoryService::DownloadCreateCallback& callback) {
history_->CreateDownload(info, callback);
}
void DownloadHistory::HistoryAdapter::UpdateDownload(
const history::DownloadRow& data) {
history_->UpdateDownload(data);
}
void DownloadHistory::HistoryAdapter::RemoveDownloads(
const std::set<uint32>& ids) {
history_->RemoveDownloads(ids);
}
DownloadHistory::Observer::Observer() {}
DownloadHistory::Observer::~Observer() {}
bool DownloadHistory::IsPersisted(content::DownloadItem* item) {
DownloadHistoryData* data = DownloadHistoryData::Get(item);
return data && (data->state() == DownloadHistoryData::PERSISTED);
}
DownloadHistory::DownloadHistory(content::DownloadManager* manager,
scoped_ptr<HistoryAdapter> history)
: notifier_(manager, this),
history_(history.Pass()),
loading_id_(content::DownloadItem::kInvalidId),
history_size_(0),
weak_ptr_factory_(this) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
content::DownloadManager::DownloadVector items;
notifier_.GetManager()->GetAllDownloads(&items);
for (content::DownloadManager::DownloadVector::const_iterator
it = items.begin(); it != items.end(); ++it) {
OnDownloadCreated(notifier_.GetManager(), *it);
}
history_->QueryDownloads(base::Bind(
&DownloadHistory::QueryCallback, weak_ptr_factory_.GetWeakPtr()));
}
DownloadHistory::~DownloadHistory() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
FOR_EACH_OBSERVER(Observer, observers_, OnDownloadHistoryDestroyed());
observers_.Clear();
}
void DownloadHistory::AddObserver(DownloadHistory::Observer* observer) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
observers_.AddObserver(observer);
}
void DownloadHistory::RemoveObserver(DownloadHistory::Observer* observer) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
observers_.RemoveObserver(observer);
}
void DownloadHistory::QueryCallback(scoped_ptr<InfoVector> infos) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (!notifier_.GetManager())
return;
for (InfoVector::const_iterator it = infos->begin();
it != infos->end(); ++it) {
loading_id_ = it->id;
content::DownloadItem* item = notifier_.GetManager()->CreateDownloadItem(
loading_id_,
it->current_path,
it->target_path,
it->url_chain,
it->referrer_url,
it->start_time,
it->end_time,
it->etag,
it->last_modified,
it->received_bytes,
it->total_bytes,
it->state,
it->danger_type,
it->interrupt_reason,
it->opened);
#if !defined(OS_ANDROID)
if (!it->by_ext_id.empty() && !it->by_ext_name.empty()) {
new DownloadedByExtension(item, it->by_ext_id, it->by_ext_name);
item->UpdateObservers();
}
#endif
DCHECK_EQ(DownloadHistoryData::Get(item)->state(),
DownloadHistoryData::PERSISTED);
++history_size_;
}
notifier_.GetManager()->CheckForHistoryFilesRemoval();
}
void DownloadHistory::MaybeAddToHistory(content::DownloadItem* item) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
uint32 download_id = item->GetId();
DownloadHistoryData* data = DownloadHistoryData::Get(item);
bool removing = removing_ids_.find(download_id) != removing_ids_.end();
if (download_crx_util::IsExtensionDownload(*item) ||
item->IsTemporary() ||
(data->state() != DownloadHistoryData::NOT_PERSISTED) ||
removing)
return;
data->SetState(DownloadHistoryData::PERSISTING);
if (data->info() == NULL) {
data->set_info(GetDownloadRow(item));
}
history_->CreateDownload(*data->info(), base::Bind(
&DownloadHistory::ItemAdded, weak_ptr_factory_.GetWeakPtr(),
download_id));
FOR_EACH_OBSERVER(Observer, observers_, OnDownloadStored(
item, *data->info()));
}
void DownloadHistory::ItemAdded(uint32 download_id, bool success) {
if (removed_while_adding_.find(download_id) !=
removed_while_adding_.end()) {
removed_while_adding_.erase(download_id);
if (success)
ScheduleRemoveDownload(download_id);
return;
}
if (!notifier_.GetManager())
return;
content::DownloadItem* item = notifier_.GetManager()->GetDownload(
download_id);
if (!item) {
return;
}
DownloadHistoryData* data = DownloadHistoryData::Get(item);
if (!success) {
DVLOG(20) << __FUNCTION__ << " INSERT failed id=" << download_id;
data->SetState(DownloadHistoryData::NOT_PERSISTED);
return;
}
data->SetState(DownloadHistoryData::PERSISTED);
UMA_HISTOGRAM_CUSTOM_COUNTS("Download.HistorySize2",
history_size_,
0,
(1 << 23),
(1 << 7));
++history_size_;
OnDownloadUpdated(notifier_.GetManager(), item);
}
void DownloadHistory::OnDownloadCreated(
content::DownloadManager* manager, content::DownloadItem* item) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
CHECK(!DownloadHistoryData::Get(item));
DownloadHistoryData* data = new DownloadHistoryData(item);
if (item->GetId() == loading_id_) {
data->SetState(DownloadHistoryData::PERSISTED);
loading_id_ = content::DownloadItem::kInvalidId;
}
if (item->GetState() == content::DownloadItem::IN_PROGRESS) {
data->set_info(GetDownloadRow(item));
}
MaybeAddToHistory(item);
}
void DownloadHistory::OnDownloadUpdated(
content::DownloadManager* manager, content::DownloadItem* item) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DownloadHistoryData* data = DownloadHistoryData::Get(item);
if (data->state() == DownloadHistoryData::NOT_PERSISTED) {
MaybeAddToHistory(item);
return;
}
if (item->IsTemporary()) {
OnDownloadRemoved(notifier_.GetManager(), item);
return;
}
history::DownloadRow current_info(GetDownloadRow(item));
bool should_update = ShouldUpdateHistory(data->info(), current_info);
UMA_HISTOGRAM_ENUMERATION("Download.HistoryPropagatedUpdate",
should_update, 2);
if (should_update) {
history_->UpdateDownload(current_info);
FOR_EACH_OBSERVER(Observer, observers_, OnDownloadStored(
item, current_info));
}
if (item->GetState() == content::DownloadItem::IN_PROGRESS) {
data->set_info(current_info);
} else {
data->clear_info();
}
}
void DownloadHistory::OnDownloadOpened(
content::DownloadManager* manager, content::DownloadItem* item) {
OnDownloadUpdated(manager, item);
}
void DownloadHistory::OnDownloadRemoved(
content::DownloadManager* manager, content::DownloadItem* item) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DownloadHistoryData* data = DownloadHistoryData::Get(item);
if (data->state() != DownloadHistoryData::PERSISTED) {
if (data->state() == DownloadHistoryData::PERSISTING) {
removed_while_adding_.insert(item->GetId());
}
return;
}
ScheduleRemoveDownload(item->GetId());
data->SetState(DownloadHistoryData::NOT_PERSISTED);
--history_size_;
}
void DownloadHistory::ScheduleRemoveDownload(uint32 download_id) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (removing_ids_.empty()) {
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::Bind(&DownloadHistory::RemoveDownloadsBatch,
weak_ptr_factory_.GetWeakPtr()));
}
removing_ids_.insert(download_id);
}
void DownloadHistory::RemoveDownloadsBatch() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
IdSet remove_ids;
removing_ids_.swap(remove_ids);
history_->RemoveDownloads(remove_ids);
FOR_EACH_OBSERVER(Observer, observers_, OnDownloadsRemoved(remove_ids));
}