Index: chrome/browser/history/expire_history_backend.cc |
diff --git a/chrome/browser/history/expire_history_backend.cc b/chrome/browser/history/expire_history_backend.cc |
deleted file mode 100644 |
index 1e33dd185c035f35008b03b201e10159563ed542..0000000000000000000000000000000000000000 |
--- a/chrome/browser/history/expire_history_backend.cc |
+++ /dev/null |
@@ -1,515 +0,0 @@ |
-// 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 "chrome/browser/history/expire_history_backend.h" |
- |
-#include <algorithm> |
-#include <functional> |
-#include <limits> |
- |
-#include "base/bind.h" |
-#include "base/compiler_specific.h" |
-#include "base/files/file_enumerator.h" |
-#include "base/files/file_util.h" |
-#include "base/logging.h" |
-#include "base/message_loop/message_loop.h" |
-#include "components/history/core/browser/history_backend_notifier.h" |
-#include "components/history/core/browser/history_client.h" |
-#include "components/history/core/browser/history_database.h" |
-#include "components/history/core/browser/thumbnail_database.h" |
- |
-namespace history { |
- |
-// Helpers -------------------------------------------------------------------- |
- |
-namespace { |
- |
-// The number of days by which the expiration threshold is advanced for items |
-// that we want to expire early, such as those of AUTO_SUBFRAME transition type. |
-// |
-// Early expiration stuff is kept around only for edge cases, as subframes |
-// don't appear in history and the vast majority of them are ads anyway. The |
-// main use case for these is if you're on a site with links to different |
-// frames, you'll be able to see those links as visited, and we'll also be |
-// able to get redirect information for those URLs. |
-// |
-// But since these uses are most valuable when you're actually on the site, |
-// and because these can take up the bulk of your history, we get a lot of |
-// space savings by deleting them quickly. |
-const int kEarlyExpirationAdvanceDays = 3; |
- |
-// Reads all types of visits starting from beginning of time to the given end |
-// time. This is the most general reader. |
-class AllVisitsReader : public ExpiringVisitsReader { |
- public: |
- bool Read(base::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(base::Time(), end_time, max_visits, visits); |
- // When we got the maximum number of visits we asked for, we say there could |
- // be additional things to expire now. |
- return static_cast<int>(visits->size()) == max_visits; |
- } |
-}; |
- |
-// Reads only AUTO_SUBFRAME visits, within a computed range. The range is |
-// computed as follows: |
-// * |begin_time| is read from the meta table. This value is updated whenever |
-// there are no more additional visits to expire by this reader. |
-// * |end_time| is advanced forward by a constant (kEarlyExpirationAdvanceDay), |
-// but not past the current time. |
-class AutoSubframeVisitsReader : public ExpiringVisitsReader { |
- public: |
- bool Read(base::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"; |
- |
- base::Time begin_time = db->GetEarlyExpirationThreshold(); |
- // Advance |end_time| to expire early. |
- base::Time early_end_time = end_time + |
- base::TimeDelta::FromDays(kEarlyExpirationAdvanceDays); |
- |
- // We don't want to set the early expiration threshold to a time in the |
- // future. |
- base::Time now = base::Time::Now(); |
- if (early_end_time > now) |
- early_end_time = now; |
- |
- db->GetVisitsInRangeForTransition(begin_time, early_end_time, |
- max_visits, |
- ui::PAGE_TRANSITION_AUTO_SUBFRAME, |
- visits); |
- bool more = static_cast<int>(visits->size()) == max_visits; |
- if (!more) |
- db->UpdateEarlyExpirationThreshold(early_end_time); |
- |
- return more; |
- } |
-}; |
- |
-// The number of visits we will expire very time we check for old items. This |
-// Prevents us from doing too much work any given time. |
-const int kNumExpirePerIteration = 32; |
- |
-// The number of seconds between checking for items that should be expired when |
-// we think there might be more items to expire. This timeout is used when the |
-// last expiration found at least kNumExpirePerIteration and we want to check |
-// again "soon." |
-const int kExpirationDelaySec = 30; |
- |
-// The number of minutes between checking, as with kExpirationDelaySec, but |
-// when we didn't find enough things to expire last time. If there was no |
-// history to expire last iteration, it's likely there is nothing next |
-// iteration, so we want to wait longer before checking to avoid wasting CPU. |
-const int kExpirationEmptyDelayMin = 5; |
- |
-} // namespace |
- |
- |
-// ExpireHistoryBackend::DeleteEffects ---------------------------------------- |
- |
-ExpireHistoryBackend::DeleteEffects::DeleteEffects() { |
-} |
- |
-ExpireHistoryBackend::DeleteEffects::~DeleteEffects() { |
-} |
- |
- |
-// ExpireHistoryBackend ------------------------------------------------------- |
- |
-ExpireHistoryBackend::ExpireHistoryBackend( |
- HistoryBackendNotifier* notifier, |
- HistoryClient* history_client) |
- : notifier_(notifier), |
- main_db_(NULL), |
- thumb_db_(NULL), |
- history_client_(history_client), |
- weak_factory_(this) { |
- DCHECK(notifier_); |
-} |
- |
-ExpireHistoryBackend::~ExpireHistoryBackend() { |
-} |
- |
-void ExpireHistoryBackend::SetDatabases(HistoryDatabase* main_db, |
- ThumbnailDatabase* thumb_db) { |
- main_db_ = main_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; |
- |
- DeleteEffects effects; |
- HistoryClient* history_client = GetHistoryClient(); |
- for (std::vector<GURL>::const_iterator url = urls.begin(); url != urls.end(); |
- ++url) { |
- URLRow url_row; |
- if (!main_db_->GetRowForURL(*url, &url_row)) |
- continue; // Nothing to delete. |
- |
- // Collect all the visits and delete them. Note that we don't give up if |
- // there are no visits, since the URL could still have an entry that we |
- // should delete. |
- VisitVector visits; |
- main_db_->GetVisitsForURL(url_row.id(), &visits); |
- |
- DeleteVisitRelatedInfo(visits, &effects); |
- |
- // We skip ExpireURLsForVisits (since we are deleting from the |
- // URL, and not starting with visits in a given time range). We |
- // therefore need to call the deletion and favicon update |
- // functions manually. |
- DeleteOneURL(url_row, |
- history_client && history_client->IsBookmarked(*url), |
- &effects); |
- } |
- |
- DeleteFaviconsIfPossible(&effects); |
- |
- BroadcastNotifications(&effects, DELETION_USER_INITIATED); |
-} |
- |
-void ExpireHistoryBackend::ExpireHistoryBetween( |
- const std::set<GURL>& restrict_urls, |
- base::Time begin_time, |
- base::Time end_time) { |
- if (!main_db_) |
- return; |
- |
- // Find the affected visits and delete them. |
- 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) { |
- // |times| must be in reverse chronological order and have no |
- // duplicates, i.e. each member must be earlier than the one before |
- // it. |
- DCHECK( |
- std::adjacent_find( |
- times.begin(), times.end(), std::less_equal<base::Time>()) == |
- times.end()); |
- |
- if (!main_db_) |
- return; |
- |
- // Find the affected visits and delete them. |
- VisitVector visits; |
- main_db_->GetVisitsForTimes(times, &visits); |
- ExpireVisits(visits); |
-} |
- |
-void ExpireHistoryBackend::ExpireVisits(const VisitVector& visits) { |
- if (visits.empty()) |
- return; |
- |
- DeleteEffects effects; |
- DeleteVisitRelatedInfo(visits, &effects); |
- |
- // Delete or update the URLs affected. We want to update the visit counts |
- // since this is called by the user who wants to delete their recent history, |
- // and we don't want to leave any evidence. |
- ExpireURLsForVisits(visits, &effects); |
- DeleteFaviconsIfPossible(&effects); |
- BroadcastNotifications(&effects, DELETION_USER_INITIATED); |
- |
- // Pick up any bits possibly left over. |
- ParanoidExpireHistory(); |
-} |
- |
-void ExpireHistoryBackend::ExpireHistoryBefore(base::Time end_time) { |
- if (!main_db_) |
- return; |
- |
- // Expire as much history as possible before the given date. |
- ExpireSomeOldHistory(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::StartExpiringOldStuff( |
- base::TimeDelta expiration_threshold) { |
- expiration_threshold_ = expiration_threshold; |
- |
- // Remove all readers, just in case this was method was called before. |
- readers_.clear(); |
- // For now, we explicitly add all known readers. If we come up with more |
- // reader types (in case we want to expire different types of visits in |
- // different ways), we can make it be populated by creator/owner of |
- // ExpireHistoryBackend. |
- readers_.push_back(GetAllVisitsReader()); |
- readers_.push_back(GetAutoSubframeVisitsReader()); |
- |
- // Initialize the queue with all tasks for the first set of iterations. |
- InitWorkQueue(); |
- ScheduleExpire(); |
-} |
- |
-void ExpireHistoryBackend::DeleteFaviconsIfPossible(DeleteEffects* effects) { |
- if (!thumb_db_) |
- return; |
- |
- for (std::set<favicon_base::FaviconID>::const_iterator i = |
- effects->affected_favicons.begin(); |
- i != effects->affected_favicons.end(); ++i) { |
- if (!thumb_db_->HasMappingFor(*i)) { |
- GURL icon_url; |
- favicon_base::IconType icon_type; |
- if (thumb_db_->GetFaviconHeader(*i, |
- &icon_url, |
- &icon_type) && |
- thumb_db_->DeleteFavicon(*i)) { |
- effects->deleted_favicons.insert(icon_url); |
- } |
- } |
- } |
-} |
- |
-void ExpireHistoryBackend::BroadcastNotifications(DeleteEffects* effects, |
- DeletionType type) { |
- if (!effects->modified_urls.empty()) { |
- notifier_->NotifyURLsModified(effects->modified_urls); |
- } |
- if (!effects->deleted_urls.empty()) { |
- notifier_->NotifyURLsDeleted(false, |
- type == DELETION_EXPIRED, |
- effects->deleted_urls, |
- effects->deleted_favicons); |
- } |
-} |
- |
-void ExpireHistoryBackend::DeleteVisitRelatedInfo(const VisitVector& visits, |
- DeleteEffects* effects) { |
- for (size_t i = 0; i < visits.size(); i++) { |
- // Delete the visit itself. |
- main_db_->DeleteVisit(visits[i]); |
- |
- // Add the URL row to the affected URL list. |
- if (!effects->affected_urls.count(visits[i].url_id)) { |
- URLRow row; |
- if (main_db_->GetURLRow(visits[i].url_id, &row)) |
- effects->affected_urls[visits[i].url_id] = row; |
- } |
- } |
-} |
- |
-void ExpireHistoryBackend::DeleteOneURL(const URLRow& url_row, |
- bool is_bookmarked, |
- DeleteEffects* effects) { |
- main_db_->DeleteSegmentForURL(url_row.id()); |
- |
- if (!is_bookmarked) { |
- effects->deleted_urls.push_back(url_row); |
- |
- // Delete stuff that references this URL. |
- if (thumb_db_) { |
- // Collect shared information. |
- 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) { |
- effects->affected_favicons.insert(m->icon_id); |
- } |
- // Delete the mapping entries for the url. |
- thumb_db_->DeleteIconMappings(url_row.url()); |
- } |
- } |
- // Last, delete the URL entry. |
- main_db_->DeleteURLRow(url_row.id()); |
- } |
-} |
- |
-namespace { |
- |
-struct ChangedURL { |
- ChangedURL() : visit_count(0), typed_count(0) {} |
- int visit_count; |
- int typed_count; |
-}; |
- |
-} // namespace |
- |
-void ExpireHistoryBackend::ExpireURLsForVisits(const VisitVector& visits, |
- DeleteEffects* effects) { |
- // First find all unique URLs and the number of visits we're deleting for |
- // each one. |
- std::map<URLID, ChangedURL> changed_urls; |
- for (size_t i = 0; i < visits.size(); i++) { |
- ChangedURL& cur = changed_urls[visits[i].url_id]; |
- // NOTE: This code must stay in sync with HistoryBackend::AddPageVisit(). |
- // TODO(pkasting): http://b/1148304 We shouldn't be marking so many URLs as |
- // typed, which would help eliminate the need for this code (we still would |
- // need to handle RELOAD transitions specially, though). |
- ui::PageTransition transition = |
- ui::PageTransitionStripQualifier(visits[i].transition); |
- if (transition != ui::PAGE_TRANSITION_RELOAD) |
- cur.visit_count++; |
- if ((transition == ui::PAGE_TRANSITION_TYPED && |
- !ui::PageTransitionIsRedirect(visits[i].transition)) || |
- transition == ui::PAGE_TRANSITION_KEYWORD_GENERATED) |
- cur.typed_count++; |
- } |
- |
- // Check each unique URL with deleted visits. |
- HistoryClient* history_client = GetHistoryClient(); |
- for (std::map<URLID, ChangedURL>::const_iterator i = changed_urls.begin(); |
- i != changed_urls.end(); ++i) { |
- // The unique URL rows should already be filled in. |
- URLRow& url_row = effects->affected_urls[i->first]; |
- if (!url_row.id()) |
- continue; // URL row doesn't exist in the database. |
- |
- // Check if there are any other visits for this URL and update the time |
- // (the time change may not actually be synced to disk below when we're |
- // archiving). |
- 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(base::Time()); |
- |
- // Don't delete URLs with visits still in the DB, or bookmarked. |
- bool is_bookmarked = |
- (history_client && history_client->IsBookmarked(url_row.url())); |
- if (!is_bookmarked && url_row.last_visit().is_null()) { |
- // Not bookmarked and no more visits. Nuke the url. |
- DeleteOneURL(url_row, is_bookmarked, effects); |
- } else { |
- // NOTE: The calls to std::max() below are a backstop, but they should |
- // never actually be needed unless the database is corrupt (I think). |
- 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)); |
- |
- // Update the db with the new details. |
- main_db_->UpdateURLRow(url_row.id(), url_row); |
- |
- effects->modified_urls.push_back(url_row); |
- } |
- } |
-} |
- |
-void ExpireHistoryBackend::ScheduleExpire() { |
- base::TimeDelta delay; |
- if (work_queue_.empty()) { |
- // If work queue is empty, reset the work queue to contain all tasks and |
- // schedule next iteration after a longer delay. |
- InitWorkQueue(); |
- delay = base::TimeDelta::FromMinutes(kExpirationEmptyDelayMin); |
- } else { |
- delay = base::TimeDelta::FromSeconds(kExpirationDelaySec); |
- } |
- |
- base::MessageLoop::current()->PostDelayedTask( |
- FROM_HERE, |
- base::Bind(&ExpireHistoryBackend::DoExpireIteration, |
- weak_factory_.GetWeakPtr()), |
- delay); |
-} |
- |
-void ExpireHistoryBackend::DoExpireIteration() { |
- DCHECK(!work_queue_.empty()) << "queue has to be non-empty"; |
- |
- const ExpiringVisitsReader* reader = work_queue_.front(); |
- bool more_to_expire = ExpireSomeOldHistory( |
- GetCurrentExpirationTime(), reader, kNumExpirePerIteration); |
- |
- work_queue_.pop(); |
- // If there are more items to expire, add the reader back to the queue, thus |
- // creating a new task for future iterations. |
- if (more_to_expire) |
- work_queue_.push(reader); |
- |
- ScheduleExpire(); |
-} |
- |
-bool ExpireHistoryBackend::ExpireSomeOldHistory( |
- base::Time end_time, |
- const ExpiringVisitsReader* reader, |
- int max_visits) { |
- if (!main_db_) |
- return false; |
- |
- // Add an extra time unit to given end time, because |
- // GetAllVisitsInRange, et al. queries' end value is non-inclusive. |
- base::Time effective_end_time = |
- base::Time::FromInternalValue(end_time.ToInternalValue() + 1); |
- |
- VisitVector deleted_visits; |
- bool more_to_expire = reader->Read(effective_end_time, main_db_, |
- &deleted_visits, max_visits); |
- |
- DeleteEffects deleted_effects; |
- DeleteVisitRelatedInfo(deleted_visits, &deleted_effects); |
- ExpireURLsForVisits(deleted_visits, &deleted_effects); |
- DeleteFaviconsIfPossible(&deleted_effects); |
- |
- BroadcastNotifications(&deleted_effects, DELETION_EXPIRED); |
- |
- return more_to_expire; |
-} |
- |
-void ExpireHistoryBackend::ParanoidExpireHistory() { |
- // TODO(brettw): Bug 1067331: write this to clean up any errors. |
-} |
- |
-HistoryClient* ExpireHistoryBackend::GetHistoryClient() { |
- // We use the history client to determine if a URL is bookmarked. The data is |
- // loaded on a separate thread and may not be done by the time we get here. |
- // We therefore block until the bookmarks have finished loading. |
- if (history_client_) |
- history_client_->BlockUntilBookmarksLoaded(); |
- return history_client_; |
-} |
- |
-} // namespace history |