Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1681)

Unified Diff: chrome/browser/history/expire_history_backend.cc

Issue 870593003: Componentize expire_history_backend.{cc,h} (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix a typo Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « chrome/browser/history/expire_history_backend.h ('k') | chrome/browser/history/expire_history_backend_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698