This source file includes following definitions.
- Read
- Read
- ShouldArchiveVisit
- bookmark_service_
- SetDatabases
- DeleteURL
- DeleteURLs
- ExpireHistoryBetween
- ExpireHistoryForTimes
- ExpireVisits
- ArchiveHistoryBefore
- InitWorkQueue
- GetAllVisitsReader
- GetAutoSubframeVisitsReader
- StartArchivingOldStuff
- DeleteFaviconsIfPossible
- BroadcastDeleteNotifications
- DeleteVisitRelatedInfo
- DeleteOneURL
- ArchiveOneURL
- ExpireURLsForVisits
- ArchiveURLsAndVisits
- ScheduleArchive
- DoArchiveIteration
- ArchiveSomeOldHistory
- ParanoidExpireHistory
- GetBookmarkService
#include "chrome/browser/history/expire_history_backend.h"
#include <algorithm>
#include <functional>
#include <limits>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "chrome/browser/bookmarks/bookmark_service.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/history/archived_database.h"
#include "chrome/browser/history/history_database.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/browser/history/thumbnail_database.h"
using base::Time;
using base::TimeDelta;
namespace history {
namespace {
const int kEarlyExpirationAdvanceDays = 3;
class AllVisitsReader : public ExpiringVisitsReader {
public:
virtual bool Read(Time end_time, HistoryDatabase* db,
VisitVector* visits, int max_visits) const OVERRIDE {
DCHECK(db) << "must have a database to operate upon";
DCHECK(visits) << "visit vector has to exist in order to populate it";
db->GetAllVisitsInRange(Time(), end_time, max_visits, visits);
return static_cast<int>(visits->size()) == max_visits;
}
};
class AutoSubframeVisitsReader : public ExpiringVisitsReader {
public:
virtual bool Read(Time end_time, HistoryDatabase* db,
VisitVector* visits, int max_visits) const OVERRIDE {
DCHECK(db) << "must have a database to operate upon";
DCHECK(visits) << "visit vector has to exist in order to populate it";
Time begin_time = db->GetEarlyExpirationThreshold();
Time early_end_time = end_time +
TimeDelta::FromDays(kEarlyExpirationAdvanceDays);
Time now = Time::Now();
if (early_end_time > now)
early_end_time = now;
db->GetVisitsInRangeForTransition(begin_time, early_end_time,
max_visits,
content::PAGE_TRANSITION_AUTO_SUBFRAME,
visits);
bool more = static_cast<int>(visits->size()) == max_visits;
if (!more)
db->UpdateEarlyExpirationThreshold(early_end_time);
return more;
}
};
bool ShouldArchiveVisit(const VisitRow& visit) {
int no_qualifier = content::PageTransitionStripQualifier(visit.transition);
if (no_qualifier == content::PAGE_TRANSITION_TYPED ||
no_qualifier == content::PAGE_TRANSITION_AUTO_BOOKMARK ||
no_qualifier == content::PAGE_TRANSITION_AUTO_TOPLEVEL)
return true;
if ((no_qualifier == content::PAGE_TRANSITION_LINK ||
no_qualifier == content::PAGE_TRANSITION_FORM_SUBMIT ||
no_qualifier == content::PAGE_TRANSITION_KEYWORD ||
no_qualifier == content::PAGE_TRANSITION_GENERATED) &&
visit.transition & content::PAGE_TRANSITION_CHAIN_END)
return true;
return false;
}
const int kNumExpirePerIteration = 32;
const int kExpirationDelaySec = 30;
const int kExpirationEmptyDelayMin = 5;
}
struct ExpireHistoryBackend::DeleteDependencies {
base::Time begin_time, end_time;
std::map<URLID, URLRow> affected_urls;
URLRows deleted_urls;
std::set<chrome::FaviconID> affected_favicons;
std::set<GURL> expired_favicons;
};
ExpireHistoryBackend::ExpireHistoryBackend(
BroadcastNotificationDelegate* delegate,
BookmarkService* bookmark_service)
: delegate_(delegate),
main_db_(NULL),
archived_db_(NULL),
thumb_db_(NULL),
weak_factory_(this),
bookmark_service_(bookmark_service) {
}
ExpireHistoryBackend::~ExpireHistoryBackend() {
}
void ExpireHistoryBackend::SetDatabases(HistoryDatabase* main_db,
ArchivedDatabase* archived_db,
ThumbnailDatabase* thumb_db) {
main_db_ = main_db;
archived_db_ = archived_db;
thumb_db_ = thumb_db;
}
void ExpireHistoryBackend::DeleteURL(const GURL& url) {
DeleteURLs(std::vector<GURL>(1, url));
}
void ExpireHistoryBackend::DeleteURLs(const std::vector<GURL>& urls) {
if (!main_db_)
return;
DeleteDependencies dependencies;
for (std::vector<GURL>::const_iterator url = urls.begin(); url != urls.end();
++url) {
URLRow url_row;
if (!main_db_->GetRowForURL(*url, &url_row))
continue;
VisitVector visits;
main_db_->GetVisitsForURL(url_row.id(), &visits);
DeleteVisitRelatedInfo(visits, &dependencies);
BookmarkService* bookmark_service = GetBookmarkService();
bool is_bookmarked =
(bookmark_service && bookmark_service->IsBookmarked(*url));
DeleteOneURL(url_row, is_bookmarked, &dependencies);
}
DeleteFaviconsIfPossible(dependencies.affected_favicons,
&dependencies.expired_favicons);
BroadcastDeleteNotifications(&dependencies, DELETION_USER_INITIATED);
}
void ExpireHistoryBackend::ExpireHistoryBetween(
const std::set<GURL>& restrict_urls, Time begin_time, Time end_time) {
if (!main_db_)
return;
VisitVector visits;
main_db_->GetAllVisitsInRange(begin_time, end_time, 0, &visits);
if (!restrict_urls.empty()) {
std::set<URLID> url_ids;
for (std::set<GURL>::const_iterator url = restrict_urls.begin();
url != restrict_urls.end(); ++url)
url_ids.insert(main_db_->GetRowForURL(*url, NULL));
VisitVector all_visits;
all_visits.swap(visits);
for (VisitVector::iterator visit = all_visits.begin();
visit != all_visits.end(); ++visit) {
if (url_ids.find(visit->url_id) != url_ids.end())
visits.push_back(*visit);
}
}
ExpireVisits(visits);
}
void ExpireHistoryBackend::ExpireHistoryForTimes(
const std::vector<base::Time>& times) {
DCHECK(
std::adjacent_find(
times.begin(), times.end(), std::less_equal<base::Time>()) ==
times.end());
if (!main_db_)
return;
VisitVector visits;
main_db_->GetVisitsForTimes(times, &visits);
ExpireVisits(visits);
}
void ExpireHistoryBackend::ExpireVisits(const VisitVector& visits) {
if (visits.empty())
return;
DeleteDependencies dependencies;
DeleteVisitRelatedInfo(visits, &dependencies);
ExpireURLsForVisits(visits, &dependencies);
DeleteFaviconsIfPossible(dependencies.affected_favicons,
&dependencies.expired_favicons);
BroadcastDeleteNotifications(&dependencies, DELETION_USER_INITIATED);
ParanoidExpireHistory();
}
void ExpireHistoryBackend::ArchiveHistoryBefore(Time end_time) {
if (!main_db_)
return;
ArchiveSomeOldHistory(end_time, GetAllVisitsReader(),
std::numeric_limits<int>::max());
ParanoidExpireHistory();
}
void ExpireHistoryBackend::InitWorkQueue() {
DCHECK(work_queue_.empty()) << "queue has to be empty prior to init";
for (size_t i = 0; i < readers_.size(); i++)
work_queue_.push(readers_[i]);
}
const ExpiringVisitsReader* ExpireHistoryBackend::GetAllVisitsReader() {
if (!all_visits_reader_)
all_visits_reader_.reset(new AllVisitsReader());
return all_visits_reader_.get();
}
const ExpiringVisitsReader*
ExpireHistoryBackend::GetAutoSubframeVisitsReader() {
if (!auto_subframe_visits_reader_)
auto_subframe_visits_reader_.reset(new AutoSubframeVisitsReader());
return auto_subframe_visits_reader_.get();
}
void ExpireHistoryBackend::StartArchivingOldStuff(
TimeDelta expiration_threshold) {
expiration_threshold_ = expiration_threshold;
readers_.clear();
readers_.push_back(GetAllVisitsReader());
readers_.push_back(GetAutoSubframeVisitsReader());
InitWorkQueue();
ScheduleArchive();
}
void ExpireHistoryBackend::DeleteFaviconsIfPossible(
const std::set<chrome::FaviconID>& favicon_set,
std::set<GURL>* expired_favicons) {
if (!thumb_db_)
return;
for (std::set<chrome::FaviconID>::const_iterator i = favicon_set.begin();
i != favicon_set.end(); ++i) {
if (!thumb_db_->HasMappingFor(*i)) {
GURL icon_url;
chrome::IconType icon_type;
if (thumb_db_->GetFaviconHeader(*i,
&icon_url,
&icon_type) &&
thumb_db_->DeleteFavicon(*i)) {
expired_favicons->insert(icon_url);
}
}
}
}
void ExpireHistoryBackend::BroadcastDeleteNotifications(
DeleteDependencies* dependencies, DeletionType type) {
if (!dependencies->deleted_urls.empty()) {
scoped_ptr<URLsDeletedDetails> details(new URLsDeletedDetails);
details->all_history = false;
details->archived = (type == DELETION_ARCHIVED);
details->rows = dependencies->deleted_urls;
details->favicon_urls = dependencies->expired_favicons;
delegate_->NotifySyncURLsDeleted(false, details->archived, &details->rows);
delegate_->BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
details.PassAs<HistoryDetails>());
}
}
void ExpireHistoryBackend::DeleteVisitRelatedInfo(
const VisitVector& visits,
DeleteDependencies* dependencies) {
for (size_t i = 0; i < visits.size(); i++) {
main_db_->DeleteVisit(visits[i]);
std::map<URLID, URLRow>::const_iterator found =
dependencies->affected_urls.find(visits[i].url_id);
if (found == dependencies->affected_urls.end()) {
URLRow row;
if (!main_db_->GetURLRow(visits[i].url_id, &row))
continue;
dependencies->affected_urls[visits[i].url_id] = row;
}
}
}
void ExpireHistoryBackend::DeleteOneURL(
const URLRow& url_row,
bool is_bookmarked,
DeleteDependencies* dependencies) {
main_db_->DeleteSegmentForURL(url_row.id());
if (!is_bookmarked) {
dependencies->deleted_urls.push_back(url_row);
if (thumb_db_) {
std::vector<IconMapping> icon_mappings;
if (thumb_db_->GetIconMappingsForPageURL(url_row.url(), &icon_mappings)) {
for (std::vector<IconMapping>::iterator m = icon_mappings.begin();
m != icon_mappings.end(); ++m) {
dependencies->affected_favicons.insert(m->icon_id);
}
thumb_db_->DeleteIconMappings(url_row.url());
}
}
main_db_->DeleteURLRow(url_row.id());
}
}
URLID ExpireHistoryBackend::ArchiveOneURL(const URLRow& url_row) {
if (!archived_db_)
return 0;
URLRow archived_row;
if (archived_db_->GetRowForURL(url_row.url(), &archived_row)) {
archived_row.set_last_visit(url_row.last_visit());
archived_db_->UpdateURLRow(archived_row.id(), archived_row);
return archived_row.id();
}
return archived_db_->AddURL(url_row);
}
namespace {
struct ChangedURL {
ChangedURL() : visit_count(0), typed_count(0) {}
int visit_count;
int typed_count;
};
}
void ExpireHistoryBackend::ExpireURLsForVisits(
const VisitVector& visits,
DeleteDependencies* dependencies) {
std::map<URLID, ChangedURL> changed_urls;
for (size_t i = 0; i < visits.size(); i++) {
ChangedURL& cur = changed_urls[visits[i].url_id];
content::PageTransition transition =
content::PageTransitionStripQualifier(visits[i].transition);
if (transition != content::PAGE_TRANSITION_RELOAD)
cur.visit_count++;
if ((transition == content::PAGE_TRANSITION_TYPED &&
!content::PageTransitionIsRedirect(visits[i].transition)) ||
transition == content::PAGE_TRANSITION_KEYWORD_GENERATED)
cur.typed_count++;
}
BookmarkService* bookmark_service = GetBookmarkService();
for (std::map<URLID, ChangedURL>::const_iterator i = changed_urls.begin();
i != changed_urls.end(); ++i) {
URLRow& url_row = dependencies->affected_urls[i->first];
if (!url_row.id())
continue;
VisitRow last_visit;
if (main_db_->GetMostRecentVisitForURL(url_row.id(), &last_visit))
url_row.set_last_visit(last_visit.visit_time);
else
url_row.set_last_visit(Time());
bool is_bookmarked =
(bookmark_service && bookmark_service->IsBookmarked(url_row.url()));
if (!is_bookmarked && url_row.last_visit().is_null()) {
DeleteOneURL(url_row, is_bookmarked, dependencies);
} else {
url_row.set_visit_count(
std::max(0, url_row.visit_count() - i->second.visit_count));
url_row.set_typed_count(
std::max(0, url_row.typed_count() - i->second.typed_count));
main_db_->UpdateURLRow(url_row.id(), url_row);
}
}
}
void ExpireHistoryBackend::ArchiveURLsAndVisits(
const VisitVector& visits,
DeleteDependencies* dependencies) {
if (!archived_db_ || !main_db_)
return;
std::map<URLID, URLID> main_id_to_archived_id;
for (size_t i = 0; i < visits.size(); i++) {
std::map<URLID, URLRow>::const_iterator found =
dependencies->affected_urls.find(visits[i].url_id);
if (found == dependencies->affected_urls.end()) {
URLRow row;
URLID archived_id;
if (!main_db_->GetURLRow(visits[i].url_id, &row) ||
!(archived_id = ArchiveOneURL(row))) {
continue;
}
main_id_to_archived_id[row.id()] = archived_id;
dependencies->affected_urls[row.id()] = row;
}
}
VisitSourceMap visit_sources;
main_db_->GetVisitsSource(visits, &visit_sources);
for (size_t i = 0; i < visits.size(); i++) {
VisitRow cur_visit(visits[i]);
cur_visit.url_id = main_id_to_archived_id[cur_visit.url_id];
cur_visit.referring_visit = 0;
VisitSourceMap::iterator iter = visit_sources.find(visits[i].visit_id);
archived_db_->AddVisit(
&cur_visit,
iter == visit_sources.end() ? SOURCE_BROWSED : iter->second);
}
}
void ExpireHistoryBackend::ScheduleArchive() {
TimeDelta delay;
if (work_queue_.empty()) {
InitWorkQueue();
delay = TimeDelta::FromMinutes(kExpirationEmptyDelayMin);
} else {
delay = TimeDelta::FromSeconds(kExpirationDelaySec);
}
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&ExpireHistoryBackend::DoArchiveIteration,
weak_factory_.GetWeakPtr()),
delay);
}
void ExpireHistoryBackend::DoArchiveIteration() {
DCHECK(!work_queue_.empty()) << "queue has to be non-empty";
const ExpiringVisitsReader* reader = work_queue_.front();
bool more_to_expire = ArchiveSomeOldHistory(GetCurrentArchiveTime(), reader,
kNumExpirePerIteration);
work_queue_.pop();
if (more_to_expire)
work_queue_.push(reader);
ScheduleArchive();
}
bool ExpireHistoryBackend::ArchiveSomeOldHistory(
base::Time end_time,
const ExpiringVisitsReader* reader,
int max_visits) {
if (!main_db_)
return false;
Time effective_end_time =
Time::FromInternalValue(end_time.ToInternalValue() + 1);
VisitVector affected_visits;
bool more_to_expire = reader->Read(effective_end_time, main_db_,
&affected_visits, max_visits);
VisitVector deleted_visits, archived_visits;
for (size_t i = 0; i < affected_visits.size(); i++) {
if (ShouldArchiveVisit(affected_visits[i]))
archived_visits.push_back(affected_visits[i]);
else
deleted_visits.push_back(affected_visits[i]);
}
DeleteDependencies archived_dependencies;
ArchiveURLsAndVisits(archived_visits, &archived_dependencies);
DeleteVisitRelatedInfo(archived_visits, &archived_dependencies);
DeleteDependencies deleted_dependencies;
DeleteVisitRelatedInfo(deleted_visits, &deleted_dependencies);
ExpireURLsForVisits(deleted_visits, &deleted_dependencies);
ExpireURLsForVisits(archived_visits, &archived_dependencies);
std::set<chrome::FaviconID> affected_favicons(
archived_dependencies.affected_favicons);
for (std::set<chrome::FaviconID>::const_iterator i =
deleted_dependencies.affected_favicons.begin();
i != deleted_dependencies.affected_favicons.end(); ++i) {
affected_favicons.insert(*i);
}
DeleteFaviconsIfPossible(affected_favicons,
&deleted_dependencies.expired_favicons);
BroadcastDeleteNotifications(&deleted_dependencies, DELETION_ARCHIVED);
return more_to_expire;
}
void ExpireHistoryBackend::ParanoidExpireHistory() {
}
BookmarkService* ExpireHistoryBackend::GetBookmarkService() {
if (bookmark_service_)
bookmark_service_->BlockTillLoaded();
return bookmark_service_;
}
}