diff options
Diffstat (limited to 'chromium/components/history')
47 files changed, 1232 insertions, 411 deletions
diff --git a/chromium/components/history/content/browser/download_conversions.cc b/chromium/components/history/content/browser/download_conversions.cc index 3a6fd5c21a5..60dc386d973 100644 --- a/chromium/components/history/content/browser/download_conversions.cc +++ b/chromium/components/history/content/browser/download_conversions.cc @@ -69,6 +69,8 @@ download::DownloadDangerType ToContentDownloadDangerType( return download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST; case DownloadDangerType::POTENTIALLY_UNWANTED: return download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED; + case DownloadDangerType::WHITELISTED_BY_POLICY: + return download::DOWNLOAD_DANGER_TYPE_WHITELISTED_BY_POLICY; case DownloadDangerType::INVALID: NOTREACHED(); return download::DOWNLOAD_DANGER_TYPE_MAX; @@ -98,6 +100,8 @@ DownloadDangerType ToHistoryDownloadDangerType( return DownloadDangerType::DANGEROUS_HOST; case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: return DownloadDangerType::POTENTIALLY_UNWANTED; + case download::DOWNLOAD_DANGER_TYPE_WHITELISTED_BY_POLICY: + return DownloadDangerType::WHITELISTED_BY_POLICY; default: NOTREACHED(); return DownloadDangerType::INVALID; diff --git a/chromium/components/history/core/browser/BUILD.gn b/chromium/components/history/core/browser/BUILD.gn index d3a9b446dbd..10fd62ac60e 100644 --- a/chromium/components/history/core/browser/BUILD.gn +++ b/chromium/components/history/core/browser/BUILD.gn @@ -11,6 +11,8 @@ static_library("browser") { "default_top_sites_provider.h", "delete_directive_handler.cc", "delete_directive_handler.h", + "domain_mixing_metrics.cc", + "domain_mixing_metrics.h", "download_constants.h", "download_database.cc", "download_database.h", @@ -103,6 +105,7 @@ static_library("browser") { "//base:i18n", "//components/data_use_measurement/core", "//components/favicon_base", + "//components/google/core/browser", "//components/history/core/common", "//components/keyed_service/core", "//components/prefs", @@ -171,6 +174,7 @@ bundle_data("unit_tests_bundle_data") { "//components/test/data/history/history.31.sql", "//components/test/data/history/history.32.sql", "//components/test/data/history/history.38.sql", + "//components/test/data/history/history.39.sql", "//components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable", "//components/test/data/history/thumbnail_wild/Favicons.v2.init.sql", "//components/test/data/history/thumbnail_wild/Favicons.v3.init.sql", @@ -191,6 +195,7 @@ source_set("unit_tests") { testonly = true sources = [ "browsing_history_service_unittest.cc", + "domain_mixing_metrics_unittest.cc", "download_slice_info_unittest.cc", "expire_history_backend_unittest.cc", "history_backend_db_unittest.cc", diff --git a/chromium/components/history/core/browser/DEPS b/chromium/components/history/core/browser/DEPS index d8117751610..26e3a1cc0b1 100644 --- a/chromium/components/history/core/browser/DEPS +++ b/chromium/components/history/core/browser/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+components/data_use_measurement/core", + "+components/google/core/browser/google_util.h", "+components/variations", "+services/identity/public", ] diff --git a/chromium/components/history/core/browser/android/visit_sql_handler.cc b/chromium/components/history/core/browser/android/visit_sql_handler.cc index 97222dd4339..5af06e70487 100644 --- a/chromium/components/history/core/browser/android/visit_sql_handler.cc +++ b/chromium/components/history/core/browser/android/visit_sql_handler.cc @@ -121,7 +121,7 @@ bool VisitSQLHandler::AddVisit(URLID url_id, const Time& visit_time) { // TODO : Is 'ui::PAGE_TRANSITION_AUTO_BOOKMARK' proper? // if not, a new ui::PageTransition type will need. VisitRow visit_row(url_id, visit_time, 0, - ui::PAGE_TRANSITION_AUTO_BOOKMARK, 0); + ui::PAGE_TRANSITION_AUTO_BOOKMARK, 0, false); return visit_db_->AddVisit(&visit_row, SOURCE_BROWSED); } diff --git a/chromium/components/history/core/browser/browsing_history_service.cc b/chromium/components/history/core/browser/browsing_history_service.cc index fbb4f4a9c7e..153dad43e1d 100644 --- a/chromium/components/history/core/browser/browsing_history_service.cc +++ b/chromium/components/history/core/browser/browsing_history_service.cc @@ -742,11 +742,9 @@ static bool DeletionsDiffer(const URLRows& deleted_rows, } void BrowsingHistoryService::OnURLsDeleted(HistoryService* history_service, - bool all_history, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) { - if (all_history || DeletionsDiffer(deleted_rows, urls_to_be_deleted_)) + const DeletionInfo& deletion_info) { + if (deletion_info.IsAllHistory() || + DeletionsDiffer(deletion_info.deleted_rows(), urls_to_be_deleted_)) driver_->HistoryDeleted(); } diff --git a/chromium/components/history/core/browser/browsing_history_service.h b/chromium/components/history/core/browser/browsing_history_service.h index 41d15e9dae2..b768c8a4744 100644 --- a/chromium/components/history/core/browser/browsing_history_service.h +++ b/chromium/components/history/core/browser/browsing_history_service.h @@ -202,10 +202,7 @@ class BrowsingHistoryService : public HistoryServiceObserver, // HistoryServiceObserver implementation. void OnURLsDeleted(HistoryService* history_service, - bool all_history, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) override; + const DeletionInfo& deletion_info) override; // WebHistoryServiceObserver implementation. void OnWebHistoryDeleted() override; diff --git a/chromium/components/history/core/browser/delete_directive_handler.cc b/chromium/components/history/core/browser/delete_directive_handler.cc index de730f6de8b..a0b737bb02b 100644 --- a/chromium/components/history/core/browser/delete_directive_handler.cc +++ b/chromium/components/history/core/browser/delete_directive_handler.cc @@ -178,7 +178,7 @@ bool DeleteDirectiveHandler::DeleteDirectiveTask::RunOnDBThread( } void DeleteDirectiveHandler::DeleteDirectiveTask::DoneRunOnMainThread() { - if (delete_directive_handler_.get()) { + if (delete_directive_handler_) { delete_directive_handler_->FinishProcessing(post_processing_action_, delete_directives_); } diff --git a/chromium/components/history/core/browser/domain_mixing_metrics.cc b/chromium/components/history/core/browser/domain_mixing_metrics.cc new file mode 100644 index 00000000000..88742939722 --- /dev/null +++ b/chromium/components/history/core/browser/domain_mixing_metrics.cc @@ -0,0 +1,150 @@ +// Copyright 2018 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 "components/history/core/browser/domain_mixing_metrics.h" + +#include "base/containers/flat_map.h" +#include "base/logging.h" +#include "base/metrics/histogram_macros.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" + +namespace history { +namespace { + +// For readability, represents days as times rounded down to a multiple in +// days from begin_time. +using Day = base::Time; +using DomainVisits = base::flat_map<std::string, int>; +using DomainVisitsPerDay = base::flat_map<Day, DomainVisits>; + +// The time intervals in days to compute domain mixing metrics for, sorted +// in ascending order. +std::vector<int> NumDaysForMetrics() { + return {1, 7, 14, 30}; +} + +// Maps a time to the start of a day using ref_start_of_day as the reference +// time for the start of a day. Some examples: +// +// time = 2018-01-02 13:00:00 UTC +// ref = 2018-01-04 04:00:00 UTC +// result = 2018-01-02 04:00:00 UTC +// +// time = 2018-01-06 03:00:00 UTC +// ref = 2018-01-04 04:00:00 UTC +// result = 2018-01-05 04:00:00 UTC +Day ToStartOfDay(base::Time time, Day ref_start_of_day) { + return ref_start_of_day + + base::TimeDelta::FromDays((time - ref_start_of_day).InDaysFloored()); +} + +// Counts the number of visits per day and per domain as a nested map +// day -> domain -> num_visits. +// start_of_day is used as the reference time for the start of a day. +DomainVisitsPerDay CountDomainVisitsPerDay( + base::Time start_of_day, + const std::vector<DomainVisit>& domain_visits) { + DomainVisitsPerDay domain_visits_per_day; + for (const DomainVisit& visit : domain_visits) { + const Day day = ToStartOfDay(visit.visit_time(), start_of_day); + DomainVisits& domain_visits_for_day = domain_visits_per_day[day]; + ++domain_visits_for_day[visit.domain()]; + } + return domain_visits_per_day; +} + +// Computes the domain mixing ratio given the number of visits for each domain. +double ComputeDomainMixingRatio(const DomainVisits& domain_visits) { + // First, we extract the domain with the most visits. + const auto top_domain = std::max_element( + domain_visits.begin(), domain_visits.end(), + [](const DomainVisits::value_type& a, const DomainVisits::value_type& b) { + return a.second < b.second; + }); + + // Then we compute the number of visits that are not on the top domain + // (secondary domains). + int other_visits = 0; + for (const auto& domain_num_visits : domain_visits) { + if (domain_num_visits.first != top_domain->first) + other_visits += domain_num_visits.second; + } + + // Finally, we compute the domain mixing ratio which is the ratio of the + // number of visits on secondary domains to the total number of visits. + // This ratio is equal to 0 if all visits are on the top domain (no domain + // mixing) and is close to 1 if most visits are on secondary domains. + DCHECK_GT(other_visits + top_domain->second, 0) + << "Tried to compute domain mixing for a time range with no domain " + "visits, this should never happen as we only compute domain mixing " + "for active days."; + return static_cast<double>(other_visits) / + (other_visits + top_domain->second); +} + +void EmitDomainMixingMetric(const DomainVisits& domain_visits, int num_days) { + double domain_mixing_ratio = ComputeDomainMixingRatio(domain_visits); + int percentage = gfx::ToRoundedInt(100 * domain_mixing_ratio); + switch (num_days) { + case 1: + UMA_HISTOGRAM_PERCENTAGE("DomainMixing.OneDay", percentage); + break; + case 7: + UMA_HISTOGRAM_PERCENTAGE("DomainMixing.OneWeek", percentage); + break; + case 14: + UMA_HISTOGRAM_PERCENTAGE("DomainMixing.TwoWeeks", percentage); + break; + case 30: + UMA_HISTOGRAM_PERCENTAGE("DomainMixing.OneMonth", percentage); + break; + default: + // This should never happen. + NOTREACHED(); + } +} + +void EmitDomainMixingMetricsForDay( + const DomainVisitsPerDay::const_iterator& active_day, + const DomainVisitsPerDay& domain_visits_per_day) { + DomainVisits domain_visits = active_day->second; + // To efficiently compute domain mixing for each of the time periods, we + // aggregate domain visits preceding the active day in a single pass. + // The metrics to emit are sorted by increasing time period lengths. + // We take them in order, aggregate the number of activity days required + // for the current one, then move on to the next one. + // Reverse iterator, starting at the day before active_day. + auto it = std::make_reverse_iterator(active_day); + for (const int num_days : NumDaysForMetrics()) { + const Day first_day = + active_day->first - base::TimeDelta::FromDays(num_days - 1); + for (; it != domain_visits_per_day.rend() && it->first >= first_day; ++it) { + for (const auto& domain_num_visits : it->second) { + domain_visits[domain_num_visits.first] += domain_num_visits.second; + } + } + // We have aggregated all the days within the time window for the current + // metric. + EmitDomainMixingMetric(domain_visits, num_days); + } +} + +} // namespace + +void EmitDomainMixingMetrics(const std::vector<DomainVisit>& domain_visits, + base::Time start_of_first_day_to_emit) { + // We count the visits per domain for each day of user activity. + DomainVisitsPerDay domain_visits_per_day = + CountDomainVisitsPerDay(start_of_first_day_to_emit, domain_visits); + + // We then compute domain mixing metrics for each day of activity within + // [start_of_first_day_to_emit, last_day]. + for (auto active_day_it = + domain_visits_per_day.lower_bound(start_of_first_day_to_emit); + active_day_it != domain_visits_per_day.end(); ++active_day_it) { + EmitDomainMixingMetricsForDay(active_day_it, domain_visits_per_day); + } +} + +} // namespace history
\ No newline at end of file diff --git a/chromium/components/history/core/browser/domain_mixing_metrics.h b/chromium/components/history/core/browser/domain_mixing_metrics.h new file mode 100644 index 00000000000..7ae2e514446 --- /dev/null +++ b/chromium/components/history/core/browser/domain_mixing_metrics.h @@ -0,0 +1,35 @@ +// Copyright 2018 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. + +#ifndef COMPONENTS_HISTORY_CORE_BROWSER_DOMAIN_MIXING_METRICS_H_ +#define COMPONENTS_HISTORY_CORE_BROWSER_DOMAIN_MIXING_METRICS_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/time/time.h" +#include "components/history/core/browser/history_types.h" + +namespace history { + +// Emits domain mixing metrics given a list of domain visits and the start of +// the first day to compute metrics for. +// +// See http://goto.google.com/chrome-no-searchdomaincheck for more details on +// what domain mixing metrics are and how they are computed. +// +// The domain_visits vector is expected to contain exactly all the domain visits +// made by the user from start_of_first_day_to_emit - 29 days (to compute the 30 +// day domain mixing metric for the first day) until the end of the last day to +// compute metrics for. +// +// This method wraps the business logic required to compute domain mixing +// metrics and is exposed for testing purposes to decouple testing the logic +// that computes the metrics from the database logic. +void EmitDomainMixingMetrics(const std::vector<DomainVisit>& domain_visits, + base::Time start_of_first_day_to_emit); + +} // namespace history + +#endif
\ No newline at end of file diff --git a/chromium/components/history/core/browser/domain_mixing_metrics_unittest.cc b/chromium/components/history/core/browser/domain_mixing_metrics_unittest.cc new file mode 100644 index 00000000000..b03bdbf4aec --- /dev/null +++ b/chromium/components/history/core/browser/domain_mixing_metrics_unittest.cc @@ -0,0 +1,89 @@ +// Copyright 2018 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 "components/history/core/browser/domain_mixing_metrics.h" + +#include <memory> +#include <vector> + +#include "base/bind.h" +#include "base/test/histogram_tester.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace history { + +class DomainMixingMetricsTest : public testing::Test { + protected: + base::HistogramTester tester_; +}; + +TEST_F(DomainMixingMetricsTest, NoVisits) { + base::Time now = + base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(1523432317); + EmitDomainMixingMetrics({}, now); + + // Check that no metrics were emitted. + tester_.ExpectTotalCount("DomainMixing.OneDay", 0); + tester_.ExpectTotalCount("DomainMixing.OneWeek", 0); + tester_.ExpectTotalCount("DomainMixing.TwoWeeks", 0); + tester_.ExpectTotalCount("DomainMixing.OneMonth", 0); +} + +TEST_F(DomainMixingMetricsTest, WithVisits) { + // Given the following Google domain visits: + // - Day 1, 1am, www.google.com + // - Day 2, 11pm, www.google.ch + // - Day 8, 2am, www.google.ch + // - Day 8, 10pm, www.google.fr + // this test checks that the correct domain mixing metrics for Day 8 are + // emitted: + // DomainMixing.OneDay 50% (Day 8 has one query on .ch, one on .fr) + // DomainMixing.OneWeek 33% (2 on .ch, 1 on .fr, day 1 is out of range) + // DomainMixing.TwoWeeks 50% (2 on .ch, 2 on other domains - 1.fr and 1 .com) + // DomainMixing.OneMonth 50% (ditto) + base::Time day1 = + base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(1523432317); + base::Time day2 = day1 + base::TimeDelta::FromDays(1); + base::Time day8 = day1 + base::TimeDelta::FromDays(7); + EmitDomainMixingMetrics( + { + DomainVisit("www.google.com", day1 + base::TimeDelta::FromHours(1)), + DomainVisit("www.google.ch", day2 + base::TimeDelta::FromHours(23)), + DomainVisit("www.google.ch", day8 + base::TimeDelta::FromHours(2)), + DomainVisit("www.google.fr", day8 + base::TimeDelta::FromHours(22)), + }, + day8); + + tester_.ExpectUniqueSample("DomainMixing.OneDay", 50, 1); + tester_.ExpectUniqueSample("DomainMixing.OneWeek", 33, 1); + tester_.ExpectUniqueSample("DomainMixing.TwoWeeks", 50, 1); + tester_.ExpectUniqueSample("DomainMixing.OneMonth", 50, 1); +} + +TEST_F(DomainMixingMetricsTest, WithInactiveDays) { + base::Time day1 = + base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(1523432317); + base::Time day3 = day1 + base::TimeDelta::FromDays(2); + EmitDomainMixingMetrics( + { + DomainVisit("www.google.com", day1), + DomainVisit("www.google.ch", day3), + }, + day1); + + // Check that no metrics are emitted for day2 when the user was inactive. + tester_.ExpectTotalCount("DomainMixing.OneDay", 2); + tester_.ExpectBucketCount("DomainMixing.OneDay", 0, 2); // Day 1 and 3. + tester_.ExpectTotalCount("DomainMixing.OneWeek", 2); + tester_.ExpectBucketCount("DomainMixing.OneWeek", 0, 1); // Day 1. + tester_.ExpectBucketCount("DomainMixing.OneWeek", 50, 1); // Day 3. + tester_.ExpectTotalCount("DomainMixing.TwoWeeks", 2); + tester_.ExpectBucketCount("DomainMixing.TwoWeeks", 0, 1); // Day 1. + tester_.ExpectBucketCount("DomainMixing.TwoWeeks", 50, 1); // Day 3. + tester_.ExpectTotalCount("DomainMixing.OneMonth", 2); + tester_.ExpectBucketCount("DomainMixing.OneMonth", 0, 1); // Day 1. + tester_.ExpectBucketCount("DomainMixing.OneMonth", 50, 1); // Day 3. +} + +} // namespace history
\ No newline at end of file diff --git a/chromium/components/history/core/browser/download_constants.h b/chromium/components/history/core/browser/download_constants.h index ea21725fafa..78530f340ce 100644 --- a/chromium/components/history/core/browser/download_constants.h +++ b/chromium/components/history/core/browser/download_constants.h @@ -35,6 +35,7 @@ enum class DownloadDangerType { USER_VALIDATED = 6, DANGEROUS_HOST = 7, POTENTIALLY_UNWANTED = 8, + WHITELISTED_BY_POLICY = 9, }; // DownloadId represents the id of a DownloadRow into the DownloadDatabase. diff --git a/chromium/components/history/core/browser/download_types.cc b/chromium/components/history/core/browser/download_types.cc index e75081e5b1c..1c740856649 100644 --- a/chromium/components/history/core/browser/download_types.cc +++ b/chromium/components/history/core/browser/download_types.cc @@ -63,6 +63,7 @@ DownloadDangerType IntToDownloadDangerType(int danger_type) { case DownloadDangerType::USER_VALIDATED: case DownloadDangerType::DANGEROUS_HOST: case DownloadDangerType::POTENTIALLY_UNWANTED: + case DownloadDangerType::WHITELISTED_BY_POLICY: return static_cast<DownloadDangerType>(danger_type); case DownloadDangerType::INVALID: @@ -100,6 +101,8 @@ std::ostream& operator<<(std::ostream& stream, DownloadDangerType danger_type) { return stream << "history::DownloadDangerType::DANGEROUS_HOST"; case DownloadDangerType::POTENTIALLY_UNWANTED: return stream << "history::DownloadDangerType::POTENTIALLY_UNWANTED"; + case DownloadDangerType::WHITELISTED_BY_POLICY: + return stream << "history::DownloadDangerType::WHITELISTED_BY_POLICY"; } NOTREACHED(); return stream; diff --git a/chromium/components/history/core/browser/expire_history_backend.cc b/chromium/components/history/core/browser/expire_history_backend.cc index e55dc56bf4e..ef0b2e3dc22 100644 --- a/chromium/components/history/core/browser/expire_history_backend.cc +++ b/chromium/components/history/core/browser/expire_history_backend.cc @@ -13,13 +13,13 @@ #include "base/bind.h" #include "base/compiler_specific.h" #include "base/containers/flat_set.h" -#include "base/feature_list.h" #include "base/files/file_enumerator.h" #include "base/files/file_util.h" #include "base/location.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/sequenced_task_runner.h" +#include "build/build_config.h" #include "components/history/core/browser/history_backend_client.h" #include "components/history/core/browser/history_backend_notifier.h" #include "components/history/core/browser/history_database.h" @@ -117,6 +117,11 @@ const int kExpirationDelaySec = 30; // iteration, so we want to wait longer before checking to avoid wasting CPU. const int kExpirationEmptyDelayMin = 5; +// If the expiration timer is delayed by over an hour, then assume that the +// machine went to sleep. +constexpr base::TimeDelta kExpirationSleepWakeupThreshold = + base::TimeDelta::FromHours(1); + // The minimum number of hours between checking for old on-demand favicons that // should be cleared. const int kClearOnDemandFaviconsIntervalHours = 24; @@ -134,8 +139,12 @@ bool IsAnyURLBookmarked(HistoryBackendClient* backend_client, namespace internal { -const base::Feature kClearOldOnDemandFavicons{ - "ClearOldOnDemandFavicons", base::FEATURE_DISABLED_BY_DEFAULT}; +// Clearing old on-demand favicons is only enabled on mobile. +#if defined(OS_ANDROID) || defined(OS_IOS) +constexpr bool kClearOldOnDemandFaviconsEnabled = true; +#else +constexpr bool kClearOldOnDemandFaviconsEnabled = false; +#endif const int kOnDemandFaviconIsOldAfterDays = 30; @@ -213,7 +222,7 @@ void ExpireHistoryBackend::DeleteURLs(const std::vector<GURL>& urls) { DeleteFaviconsIfPossible(&effects); BroadcastNotifications(&effects, DELETION_USER_INITIATED, - DeletionTimeRange::Invalid()); + DeletionTimeRange::Invalid(), base::nullopt); } void ExpireHistoryBackend::ExpireHistoryBetween( @@ -239,10 +248,8 @@ void ExpireHistoryBackend::ExpireHistoryBetween( visits.push_back(*visit); } } - DeletionTimeRange time_range = restrict_urls.empty() - ? DeletionTimeRange(begin_time, end_time) - : DeletionTimeRange::Invalid(); - ExpireVisitsInternal(visits, time_range); + DeletionTimeRange time_range(begin_time, end_time); + ExpireVisitsInternal(visits, time_range, restrict_urls); } void ExpireHistoryBackend::ExpireHistoryForTimes( @@ -265,12 +272,13 @@ void ExpireHistoryBackend::ExpireHistoryForTimes( } void ExpireHistoryBackend::ExpireVisits(const VisitVector& visits) { - ExpireVisitsInternal(visits, DeletionTimeRange::Invalid()); + ExpireVisitsInternal(visits, DeletionTimeRange::Invalid(), {}); } void ExpireHistoryBackend::ExpireVisitsInternal( const VisitVector& visits, - const DeletionTimeRange& time_range) { + const DeletionTimeRange& time_range, + const std::set<GURL>& restrict_urls) { if (visits.empty()) return; @@ -287,7 +295,9 @@ void ExpireHistoryBackend::ExpireVisitsInternal( // and we don't want to leave any evidence. ExpireURLsForVisits(visits_and_redirects, &effects); DeleteFaviconsIfPossible(&effects); - BroadcastNotifications(&effects, DELETION_USER_INITIATED, time_range); + BroadcastNotifications( + &effects, DELETION_USER_INITIATED, time_range, + restrict_urls.empty() ? base::Optional<std::set<GURL>>() : restrict_urls); // Pick up any bits possibly left over. ParanoidExpireHistory(); @@ -301,7 +311,7 @@ void ExpireHistoryBackend::ExpireVisitsInternal( } } -void ExpireHistoryBackend::ExpireHistoryBefore(base::Time end_time) { +void ExpireHistoryBackend::ExpireHistoryBeforeForTesting(base::Time end_time) { if (!main_db_) return; @@ -372,14 +382,15 @@ void ExpireHistoryBackend::DeleteFaviconsIfPossible(DeleteEffects* effects) { void ExpireHistoryBackend::BroadcastNotifications( DeleteEffects* effects, DeletionType type, - const DeletionTimeRange& time_range) { + const DeletionTimeRange& time_range, + base::Optional<std::set<GURL>> restrict_urls) { if (!effects->modified_urls.empty()) { notifier_->NotifyURLsModified(effects->modified_urls); } if (!effects->deleted_urls.empty() || time_range.IsValid()) { - notifier_->NotifyURLsDeleted(time_range, type == DELETION_EXPIRED, - effects->deleted_urls, - effects->deleted_favicons); + notifier_->NotifyURLsDeleted(DeletionInfo( + time_range, type == DELETION_EXPIRED, std::move(effects->deleted_urls), + std::move(effects->deleted_favicons), std::move(restrict_urls))); } } @@ -465,12 +476,7 @@ void ExpireHistoryBackend::ExpireURLsForVisits(const VisitVector& visits, ui::PAGE_TRANSITION_RELOAD)) { cur.visit_count++; } - if (ui::PageTransitionIsNewNavigation(visits[i].transition) && - ((ui::PageTransitionCoreTypeIs(visits[i].transition, - ui::PAGE_TRANSITION_TYPED) && - !ui::PageTransitionIsRedirect(visits[i].transition)) || - ui::PageTransitionCoreTypeIs(visits[i].transition, - ui::PAGE_TRANSITION_KEYWORD_GENERATED))) + if (visits[i].incremented_omnibox_typed_score) cur.typed_count++; } @@ -524,6 +530,7 @@ void ExpireHistoryBackend::ScheduleExpire() { delay = base::TimeDelta::FromSeconds(kExpirationDelaySec); } + expected_expiration_time_ = base::Time::Now() + delay; task_runner_->PostDelayedTask( FROM_HERE, base::Bind(&ExpireHistoryBackend::DoExpireIteration, @@ -534,6 +541,21 @@ void ExpireHistoryBackend::ScheduleExpire() { void ExpireHistoryBackend::DoExpireIteration() { DCHECK(!work_queue_.empty()) << "queue has to be non-empty"; + // If the timer is firing more than an hour later than expected, than the + // machine likely just woke from sleep/hibernation. There is potentially a lot + // of expiring that needs to happen. Wait for 5 minutes before starting to do + // any expiry, to avoid conflicting with other work that happens on waking + // from sleep. + if (base::Time::Now() - expected_expiration_time_ > + kExpirationSleepWakeupThreshold) { + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(&ExpireHistoryBackend::ScheduleExpire, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMinutes(kExpirationEmptyDelayMin)); + return; + } + const ExpiringVisitsReader* reader = work_queue_.front(); bool more_to_expire = ExpireSomeOldHistory( GetCurrentExpirationTime(), reader, kNumExpirePerIteration); @@ -543,7 +565,7 @@ void ExpireHistoryBackend::DoExpireIteration() { // If there are more items to expire, add the reader back to the queue, thus // creating a new task for future iterations. work_queue_.push(reader); - } else { + } else if (internal::kClearOldOnDemandFaviconsEnabled) { // Otherwise do a final clean-up - remove old favicons not bound to visits. ClearOldOnDemandFaviconsIfPossible( base::Time::Now() - @@ -558,9 +580,6 @@ void ExpireHistoryBackend::ClearOldOnDemandFaviconsIfPossible( if (!thumb_db_) return; - if (!base::FeatureList::IsEnabled(internal::kClearOldOnDemandFavicons)) - return; - // Extra precaution to avoid repeated calls to GetOldOnDemandFavicons() close // in time, since it can be fairly expensive. if (expiration_threshold < @@ -590,7 +609,7 @@ void ExpireHistoryBackend::ClearOldOnDemandFaviconsIfPossible( } BroadcastNotifications(&effects, DELETION_EXPIRED, - DeletionTimeRange::Invalid()); + DeletionTimeRange::Invalid(), base::nullopt); } bool ExpireHistoryBackend::ExpireSomeOldHistory( @@ -613,10 +632,9 @@ bool ExpireHistoryBackend::ExpireSomeOldHistory( DeleteVisitRelatedInfo(deleted_visits, &deleted_effects); ExpireURLsForVisits(deleted_visits, &deleted_effects); DeleteFaviconsIfPossible(&deleted_effects); - DeletionTimeRange time_range = - more_to_expire ? DeletionTimeRange::Invalid() - : DeletionTimeRange(base::Time(), end_time); - BroadcastNotifications(&deleted_effects, DELETION_EXPIRED, time_range); + + BroadcastNotifications(&deleted_effects, DELETION_EXPIRED, + DeletionTimeRange::Invalid(), base::nullopt); return more_to_expire; } diff --git a/chromium/components/history/core/browser/expire_history_backend.h b/chromium/components/history/core/browser/expire_history_backend.h index 1c89f47e8fd..c936412effc 100644 --- a/chromium/components/history/core/browser/expire_history_backend.h +++ b/chromium/components/history/core/browser/expire_history_backend.h @@ -20,7 +20,6 @@ class GURL; class TestingProfile; namespace base { -struct Feature; class SequencedTaskRunner; } @@ -44,9 +43,6 @@ class ExpiringVisitsReader { typedef std::vector<const ExpiringVisitsReader*> ExpiringVisitsReaders; namespace internal { -// Feature that enables clearing old on-demand favicons. -extern const base::Feature kClearOldOnDemandFavicons; - // The minimum number of days since last use for an icon to be considered old. extern const int kOnDemandFaviconIsOldAfterDays; } // namespace internal @@ -96,8 +92,8 @@ class ExpireHistoryBackend { void ExpireVisits(const VisitVector& visits); // Expires all visits before and including the given time, updating the URLs - // accordingly. Currently only used for testing. - void ExpireHistoryBefore(base::Time end_time); + // accordingly. + void ExpireHistoryBeforeForTesting(base::Time end_time); // Returns the current cut-off time before which we will start expiring stuff. // Note that this as an absolute time rather than a delta, so the caller @@ -202,7 +198,8 @@ class ExpireHistoryBackend { void ExpireURLsForVisits(const VisitVector& visits, DeleteEffects* effects); void ExpireVisitsInternal(const VisitVector& visits, - const DeletionTimeRange& time_range); + const DeletionTimeRange& time_range, + const std::set<GURL>& restrict_urls); // Deletes the favicons listed in |effects->affected_favicons| if they are // unsued. Fails silently (we don't care about favicons so much, so don't want @@ -223,7 +220,8 @@ class ExpireHistoryBackend { // Broadcasts URL modified and deleted notifications. void BroadcastNotifications(DeleteEffects* effects, DeletionType type, - const DeletionTimeRange& time_range); + const DeletionTimeRange& time_range, + base::Optional<std::set<GURL>> restrict_urls); // Schedules a call to DoExpireIteration. void ScheduleExpire(); @@ -272,6 +270,9 @@ class ExpireHistoryBackend { // The threshold for "old" history where we will automatically delete it. base::TimeDelta expiration_threshold_; + // The time at which we expect the expiration code to run. + base::Time expected_expiration_time_; + // The lastly used threshold for "old" on-demand favicons. base::Time last_on_demand_expiration_threshold_; diff --git a/chromium/components/history/core/browser/expire_history_backend_unittest.cc b/chromium/components/history/core/browser/expire_history_backend_unittest.cc index 2e98f96b093..a0215c0a522 100644 --- a/chromium/components/history/core/browser/expire_history_backend_unittest.cc +++ b/chromium/components/history/core/browser/expire_history_backend_unittest.cc @@ -16,7 +16,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_current.h" #include "base/run_loop.h" #include "base/scoped_observer.h" #include "base/strings/string16.h" @@ -105,9 +105,10 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier { // |expired|, or manually deleted. void EnsureURLInfoGone(const URLRow& row, bool expired); - DeletionTimeRange GetLastDeletionTimeRange() { - EXPECT_FALSE(urls_deleted_notifications_.empty()); - return std::get<0>(urls_deleted_notifications_.back()); + const DeletionInfo* GetLastDeletionInfo() { + if (urls_deleted_notifications_.empty()) + return nullptr; + return &urls_deleted_notifications_.back(); } // Returns whether HistoryBackendNotifier::NotifyURLsModified was @@ -148,8 +149,7 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier { typedef std::vector<URLRows> URLsModifiedNotificationList; URLsModifiedNotificationList urls_modified_notifications_; - typedef std::vector<std::tuple<DeletionTimeRange, bool, URLRows>> - URLsDeletedNotificationList; + typedef std::vector<DeletionInfo> URLsDeletedNotificationList; URLsDeletedNotificationList urls_deleted_notifications_; private: @@ -191,7 +191,7 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier { top_sites_->ShutdownOnUIThread(); top_sites_ = nullptr; - if (base::MessageLoop::current()) + if (base::MessageLoopCurrent::Get()) base::RunLoop().RunUntilIdle(); pref_service_.reset(); @@ -207,11 +207,8 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier { void NotifyURLsModified(const URLRows& rows) override { urls_modified_notifications_.push_back(rows); } - void NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& rows, - const std::set<GURL>& favicon_urls) override { - urls_deleted_notifications_.push_back(std::tie(time_range, expired, rows)); + void NotifyURLsDeleted(DeletionInfo deletion_info) override { + urls_deleted_notifications_.push_back(std::move(deletion_info)); } }; @@ -229,7 +226,7 @@ class ExpireHistoryTest : public testing::Test, public HistoryBackendNotifier { // added to the given arrays. void ExpireHistoryTest::AddExampleData(URLID url_ids[3], base::Time visit_times[4]) { - if (!main_db_.get()) + if (!main_db_) return; // Four times for each visit. @@ -289,6 +286,7 @@ void ExpireHistoryTest::AddExampleData(URLID url_ids[3], visit_row3.url_id = url_ids[1]; visit_row3.visit_time = visit_times[2]; visit_row3.transition = ui::PAGE_TRANSITION_TYPED; + visit_row3.incremented_omnibox_typed_score = true; main_db_->AddVisit(&visit_row3, SOURCE_BROWSED); VisitRow visit_row4; @@ -311,23 +309,24 @@ void ExpireHistoryTest::AddExampleSourceData(const GURL& url, URLID* id) { // Four times for each visit. VisitRow visit_row1(url_id, last_visit_time - base::TimeDelta::FromDays(4), 0, - ui::PAGE_TRANSITION_TYPED, 0); + ui::PAGE_TRANSITION_TYPED, 0, true); main_db_->AddVisit(&visit_row1, SOURCE_SYNCED); VisitRow visit_row2(url_id, last_visit_time - base::TimeDelta::FromDays(3), 0, - ui::PAGE_TRANSITION_TYPED, 0); + ui::PAGE_TRANSITION_TYPED, 0, true); main_db_->AddVisit(&visit_row2, SOURCE_BROWSED); VisitRow visit_row3(url_id, last_visit_time - base::TimeDelta::FromDays(2), 0, - ui::PAGE_TRANSITION_TYPED, 0); + ui::PAGE_TRANSITION_TYPED, 0, true); main_db_->AddVisit(&visit_row3, SOURCE_EXTENSION); - VisitRow visit_row4(url_id, last_visit_time, 0, ui::PAGE_TRANSITION_TYPED, 0); + VisitRow visit_row4(url_id, last_visit_time, 0, ui::PAGE_TRANSITION_TYPED, 0, + true); main_db_->AddVisit(&visit_row4, SOURCE_FIREFOX_IMPORTED); } bool ExpireHistoryTest::HasFavicon(favicon_base::FaviconID favicon_id) { - if (!thumb_db_.get() || favicon_id == 0) + if (!thumb_db_ || favicon_id == 0) return false; return thumb_db_->GetFaviconHeader(favicon_id, nullptr, nullptr); } @@ -373,9 +372,9 @@ void ExpireHistoryTest::EnsureURLInfoGone(const URLRow& row, bool expired) { // EXPECT_FALSE(HasThumbnail(row.id())); bool found_delete_notification = false; - for (const auto& tuple : urls_deleted_notifications_) { - EXPECT_EQ(expired, std::get<1>(tuple)); - const history::URLRows& rows(std::get<2>(tuple)); + for (const auto& info : urls_deleted_notifications_) { + EXPECT_EQ(expired, info.is_from_expiration()); + const history::URLRows& rows(info.deleted_rows()); history::URLRows::const_iterator it_row = std::find_if( rows.begin(), rows.end(), history::URLRow::URLRowHasURL(row.url())); if (it_row != rows.end()) { @@ -622,8 +621,8 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) { // This should delete the last two visits. std::set<GURL> restrict_urls; expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], base::Time()); - EXPECT_EQ(GetLastDeletionTimeRange().begin(), visit_times[2]); - EXPECT_EQ(GetLastDeletionTimeRange().end(), base::Time()); + EXPECT_EQ(GetLastDeletionInfo()->time_range().begin(), visit_times[2]); + EXPECT_EQ(GetLastDeletionInfo()->time_range().end(), base::Time()); // Verify that the middle URL had its last visit deleted only. visits.clear(); @@ -813,7 +812,7 @@ TEST_F(ExpireHistoryTest, FlushURLsForTimes) { times.push_back(visit_times[3]); times.push_back(visit_times[2]); expirer_.ExpireHistoryForTimes(times); - EXPECT_FALSE(GetLastDeletionTimeRange().IsValid()); + EXPECT_FALSE(GetLastDeletionInfo()->time_range().IsValid()); // Verify that the middle URL had its last visit deleted only. visits.clear(); @@ -862,9 +861,12 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) { ASSERT_EQ(1U, visits.size()); // This should delete the last two visits. - std::set<GURL> restrict_urls; - restrict_urls.insert(url_row1.url()); + std::set<GURL> restrict_urls = {url_row1.url()}; expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], base::Time()); + EXPECT_EQ(GetLastDeletionInfo()->time_range().begin(), visit_times[2]); + EXPECT_EQ(GetLastDeletionInfo()->time_range().end(), base::Time()); + EXPECT_EQ(GetLastDeletionInfo()->deleted_rows().size(), 0U); + EXPECT_EQ(GetLastDeletionInfo()->restrict_urls()->size(), 1U); // Verify that the middle URL had its last visit deleted only. visits.clear(); @@ -957,7 +959,7 @@ TEST_F(ExpireHistoryTest, ExpireHistoryBeforeUnstarred) { ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row2)); // Expire the oldest two visits. - expirer_.ExpireHistoryBefore(visit_times[1]); + expirer_.ExpireHistoryBeforeForTesting(visit_times[1]); // The first URL should be deleted along with its sole visit. The second URL // itself should not be affected, as there is still one more visit to it, but @@ -975,7 +977,7 @@ TEST_F(ExpireHistoryTest, ExpireHistoryBeforeUnstarred) { // Now expire one more visit so that the second URL should be removed. The // third URL and its visit should be intact. ClearLastNotifications(); - expirer_.ExpireHistoryBefore(visit_times[2]); + expirer_.ExpireHistoryBeforeForTesting(visit_times[2]); EnsureURLInfoGone(url_row1, true); EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row)); main_db_->GetVisitsForURL(temp_row.id(), &visits); @@ -998,7 +1000,7 @@ TEST_F(ExpireHistoryTest, ExpireHistoryBeforeStarred) { // Now expire the first three visits (first two URLs). The first three visits // should be deleted, but the URL records themselves should not, as they are // starred. - expirer_.ExpireHistoryBefore(visit_times[2]); + expirer_.ExpireHistoryBeforeForTesting(visit_times[2]); URLRow temp_row; ASSERT_TRUE(main_db_->GetURLRow(url_ids[0], &temp_row)); @@ -1031,19 +1033,19 @@ TEST_F(ExpireHistoryTest, ExpireSomeOldHistory) { // Deleting a time range with no URLs should return false (nothing found). EXPECT_FALSE(expirer_.ExpireSomeOldHistory( visit_times[0] - base::TimeDelta::FromDays(100), reader, 1)); - EXPECT_EQ(GetLastDeletionTimeRange().begin(), base::Time()); - EXPECT_EQ(GetLastDeletionTimeRange().end(), - visit_times[0] - base::TimeDelta::FromDays(100)); + EXPECT_EQ(nullptr, GetLastDeletionInfo()); // Deleting a time range with not up the the max results should also return // false (there will only be one visit deleted in this range). EXPECT_FALSE(expirer_.ExpireSomeOldHistory(visit_times[0], reader, 2)); - EXPECT_EQ(GetLastDeletionTimeRange().begin(), base::Time()); - EXPECT_EQ(GetLastDeletionTimeRange().end(), visit_times[0]); + EXPECT_EQ(1U, GetLastDeletionInfo()->deleted_rows().size()); + EXPECT_FALSE(GetLastDeletionInfo()->time_range().IsValid()); + ClearLastNotifications(); // Deleting a time range with the max number of results should return true // (max deleted). EXPECT_TRUE(expirer_.ExpireSomeOldHistory(visit_times[2], reader, 1)); + EXPECT_EQ(nullptr, GetLastDeletionInfo()); } TEST_F(ExpireHistoryTest, ExpiringVisitsReader) { @@ -1079,9 +1081,6 @@ TEST_F(ExpireHistoryTest, ExpiringVisitsReader) { // Test that ClearOldOnDemandFaviconsIfPossible() deletes favicons associated // only to unstarred page URLs. TEST_F(ExpireHistoryTest, ClearOldOnDemandFaviconsDoesDeleteUnstarred) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature(internal::kClearOldOnDemandFavicons); - // The blob does not encode any real bitmap, obviously. const unsigned char kBlob[] = "0"; scoped_refptr<base::RefCountedBytes> favicon( @@ -1108,9 +1107,6 @@ TEST_F(ExpireHistoryTest, ClearOldOnDemandFaviconsDoesDeleteUnstarred) { // Test that ClearOldOnDemandFaviconsIfPossible() deletes favicons associated to // at least one starred page URL. TEST_F(ExpireHistoryTest, ClearOldOnDemandFaviconsDoesNotDeleteStarred) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature(internal::kClearOldOnDemandFavicons); - // The blob does not encode any real bitmap, obviously. const unsigned char kBlob[] = "0"; scoped_refptr<base::RefCountedBytes> favicon( @@ -1147,9 +1143,6 @@ TEST_F(ExpireHistoryTest, ClearOldOnDemandFaviconsDoesNotDeleteStarred) { // Test that ClearOldOnDemandFaviconsIfPossible() has effect if the last // clearing was long time age (such as 2 days ago). TEST_F(ExpireHistoryTest, ClearOldOnDemandFaviconsDoesDeleteAfterLongDelay) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature(internal::kClearOldOnDemandFavicons); - // Previous clearing (2 days ago). expirer_.ClearOldOnDemandFaviconsIfPossible(GetOldFaviconThreshold() - base::TimeDelta::FromDays(2)); @@ -1181,9 +1174,6 @@ TEST_F(ExpireHistoryTest, ClearOldOnDemandFaviconsDoesDeleteAfterLongDelay) { // at least one starred page URL. TEST_F(ExpireHistoryTest, ClearOldOnDemandFaviconsDoesNotDeleteAfterShortDelay) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature(internal::kClearOldOnDemandFavicons); - // Previous clearing (5 minutes ago). expirer_.ClearOldOnDemandFaviconsIfPossible(GetOldFaviconThreshold() - base::TimeDelta::FromMinutes(5)); diff --git a/chromium/components/history/core/browser/history_backend.cc b/chromium/components/history/core/browser/history_backend.cc index 851bf1c6818..478d3985a87 100644 --- a/chromium/components/history/core/browser/history_backend.cc +++ b/chromium/components/history/core/browser/history_backend.cc @@ -171,6 +171,18 @@ HistoryBackendHelper::~HistoryBackendHelper() { // HistoryBackend -------------------------------------------------------------- +// static +bool HistoryBackend::IsTypedIncrement(ui::PageTransition transition) { + if (ui::PageTransitionIsNewNavigation(transition) && + ((ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) && + !ui::PageTransitionIsRedirect(transition)) || + ui::PageTransitionCoreTypeIs(transition, + ui::PAGE_TRANSITION_KEYWORD_GENERATED))) { + return true; + } + return false; +} + HistoryBackend::HistoryBackend( Delegate* delegate, std::unique_ptr<HistoryBackendClient> backend_client, @@ -413,6 +425,8 @@ OriginCountAndLastVisitMap HistoryBackend::GetCountsAndLastVisitForOrigins( const std::set<GURL>& origins) const { if (!db_) return OriginCountAndLastVisitMap(); + if (origins.empty()) + return OriginCountAndLastVisitMap(); URLDatabase::URLEnumerator it; if (!db_->InitURLEnumeratorForEverything(&it)) @@ -775,15 +789,7 @@ std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( ui::PageTransition transition, bool hidden, VisitSource visit_source) { - // NOTE: This code must stay in sync with - // ExpireHistoryBackend::ExpireURLsForVisits(). - int typed_increment = 0; - if (ui::PageTransitionIsNewNavigation(transition) && - ((ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) && - !ui::PageTransitionIsRedirect(transition)) || - ui::PageTransitionCoreTypeIs(transition, - ui::PAGE_TRANSITION_KEYWORD_GENERATED))) - typed_increment = 1; + const bool typed_increment = IsTypedIncrement(transition); if (!host_ranks_.empty() && visit_source == SOURCE_BROWSED && (transition & ui::PAGE_TRANSITION_CHAIN_END)) { @@ -798,7 +804,7 @@ std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( if (!ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD)) url_info.set_visit_count(url_info.visit_count() + 1); if (typed_increment) - url_info.set_typed_count(url_info.typed_count() + typed_increment); + url_info.set_typed_count(url_info.typed_count() + 1); if (url_info.last_visit() < time) url_info.set_last_visit(time); @@ -810,7 +816,7 @@ std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( } else { // Addition of a new row. url_info.set_visit_count(1); - url_info.set_typed_count(typed_increment); + url_info.set_typed_count(typed_increment ? 1 : 0); url_info.set_last_visit(time); url_info.set_hidden(hidden); @@ -823,7 +829,8 @@ std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( } // Add the visit with the time to the database. - VisitRow visit_info(url_id, time, referring_visit, transition, 0); + VisitRow visit_info(url_id, time, referring_visit, transition, 0, + typed_increment); VisitID visit_id = db_->AddVisit(&visit_info, visit_source); if (visit_info.visit_time < first_recorded_time_) @@ -879,7 +886,7 @@ void HistoryBackend::AddPagesWithDetails(const URLRows& urls, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CHAIN_START | ui::PAGE_TRANSITION_CHAIN_END), - 0); + 0, false); if (!db_->AddVisit(&visit_info, visit_source)) { NOTREACHED() << "Adding visit failed."; return; @@ -1633,11 +1640,9 @@ void HistoryBackend::MergeFavicon( favicon_base::FaviconID favicon_id = thumbnail_db_->GetFaviconIDForFaviconURL(icon_url, icon_type); - bool favicon_created = false; if (!favicon_id) { // There is no favicon at |icon_url|, create it. favicon_id = thumbnail_db_->AddFavicon(icon_url, icon_type); - favicon_created = true; } std::vector<FaviconBitmapIDSize> bitmap_id_sizes; @@ -1776,9 +1781,7 @@ void HistoryBackend::MergeFavicon( SendFaviconChangedNotificationForPageAndRedirects(page_url); } - if (!favicon_created && (!bitmap_identical || favicon_bitmaps_copied)) { - // If there was a favicon at |icon_url| prior to MergeFavicon() being - // called, there may be page URLs which also use the favicon at |icon_url|. + if (!bitmap_identical || favicon_bitmaps_copied) { // Notify the UI that the favicon has changed for |icon_url|. SendFaviconChangedNotificationForIconURL(icon_url); } @@ -1995,10 +1998,8 @@ bool HistoryBackend::SetFaviconsImpl(const base::flat_set<GURL>& page_urls, } } - if (favicon_data_modified && !favicon_created) { - // If there was a favicon at |icon_url| prior to SetFavicons() being called, - // there may be page URLs which also use the favicon at |icon_url|. Notify - // the UI that the favicon has changed for |icon_url|. + if (favicon_data_modified) { + // Notify the UI that the favicon has changed for |icon_url|. SendFaviconChangedNotificationForIconURL(icon_url); } ScheduleCommit(); @@ -2526,6 +2527,13 @@ void HistoryBackend::ExpireHistory( } } +void HistoryBackend::ExpireHistoryBeforeForTesting(base::Time end_time) { + if (!db_) + return; + + expirer_.ExpireHistoryBeforeForTesting(end_time); +} + void HistoryBackend::URLsNoLongerBookmarked(const std::set<GURL>& urls) { if (!db_) return; @@ -2549,15 +2557,13 @@ void HistoryBackend::DatabaseErrorCallback(int error, sql::Statement* stmt) { db_diagnostics_ = db_->GetDiagnosticInfo(error, stmt); - // Notify SyncBridge about storage error. It will report failure to sync - // engine and stop accepting remote updates. - if (typed_url_sync_bridge_) - typed_url_sync_bridge_->OnDatabaseError(); - // Don't just do the close/delete here, as we are being called by |db| and // that seems dangerous. - // TODO(shess): Consider changing KillHistoryDatabase() to use - // RazeAndClose(). Then it can be cleared immediately. + // TODO(https://crbug.com/854258): It is also dangerous to kill the database + // by a posted task: tasks that run before KillHistoryDatabase still can try + // to use the broken database. Consider protecting against other tasks using + // the DB or consider changing KillHistoryDatabase() to use RazeAndClose() + // (then it can be cleared immediately). task_runner_->PostTask( FROM_HERE, base::Bind(&HistoryBackend::KillHistoryDatabase, this)); } @@ -2568,6 +2574,11 @@ void HistoryBackend::KillHistoryDatabase() { if (!db_) return; + // Notify SyncBridge about storage error. It will report failure to sync + // engine and stop accepting remote updates. + if (typed_url_sync_bridge_) + typed_url_sync_bridge_->OnDatabaseError(); + // Rollback transaction because Raze() cannot be called from within a // transaction. db_->RollbackTransaction(); @@ -2635,19 +2646,22 @@ void HistoryBackend::NotifyURLsModified(const URLRows& rows) { delegate_->NotifyURLsModified(rows); } -void HistoryBackend::NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& rows, - const std::set<GURL>& favicon_urls) { - URLRows copied_rows(rows); +void HistoryBackend::NotifyURLsDeleted(DeletionInfo deletion_info) { + std::set<GURL> origins; + for (const history::URLRow& row : deletion_info.deleted_rows()) + origins.insert(row.url().GetOrigin()); + + deletion_info.set_deleted_urls_origin_map( + GetCountsAndLastVisitForOrigins(origins)); + for (HistoryBackendObserver& observer : observers_) { - observer.OnURLsDeleted(this, time_range.IsAllTime(), expired, copied_rows, - favicon_urls); + observer.OnURLsDeleted( + this, deletion_info.IsAllHistory(), deletion_info.is_from_expiration(), + deletion_info.deleted_rows(), deletion_info.favicon_urls()); } if (delegate_) - delegate_->NotifyURLsDeleted(time_range, expired, copied_rows, - favicon_urls); + delegate_->NotifyURLsDeleted(std::move(deletion_info)); } // Deleting -------------------------------------------------------------------- @@ -2704,8 +2718,7 @@ void HistoryBackend::DeleteAllHistory() { // Send out the notification that history is cleared. The in-memory database // will pick this up and clear itself. - NotifyURLsDeleted(DeletionTimeRange::AllTime(), false, URLRows(), - std::set<GURL>()); + NotifyURLsDeleted(DeletionInfo::ForAllHistory()); } bool HistoryBackend::ClearAllThumbnailHistory( diff --git a/chromium/components/history/core/browser/history_backend.h b/chromium/components/history/core/browser/history_backend.h index fa77efd2dfe..be693853337 100644 --- a/chromium/components/history/core/browser/history_backend.h +++ b/chromium/components/history/core/browser/history_backend.h @@ -143,10 +143,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, // Notify HistoryService that some or all of the URLs have been deleted. // The event will be forwarded to the HistoryServiceObservers in the correct // thread. - virtual void NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) = 0; + virtual void NotifyURLsDeleted(DeletionInfo deletion_info) = 0; // Notify HistoryService that some keyword has been searched using omnibox. // The event will be forwarded to the HistoryServiceObservers in the correct @@ -164,6 +161,9 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, virtual void DBLoaded() = 0; }; + // Check if the transition should increment the typed_count of a visit. + static bool IsTypedIncrement(ui::PageTransition transition); + // Init must be called to complete object creation. This object can be // constructed on any thread, but all other functions including Init() must // be called on the history thread. @@ -439,6 +439,10 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, // of ExpireHistoryBetween(). void ExpireHistory(const std::vector<ExpireHistoryArgs>& expire_list); + // Expires all visits before and including the given time, updating the URLs + // accordingly. + void ExpireHistoryBeforeForTesting(base::Time end_time); + // Bookmarks ----------------------------------------------------------------- // Notification that a URL is no longer bookmarked. If there are no visits @@ -818,10 +822,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, const RedirectList& redirects, base::Time visit_time) override; void NotifyURLsModified(const URLRows& rows) override; - void NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& rows, - const std::set<GURL>& favicon_urls) override; + void NotifyURLsDeleted(DeletionInfo deletion_info) override; void RecordTopHostsMetrics(const GURL& url); diff --git a/chromium/components/history/core/browser/history_backend_db_unittest.cc b/chromium/components/history/core/browser/history_backend_db_unittest.cc index 077da13c3dd..9f3ac0fad31 100644 --- a/chromium/components/history/core/browser/history_backend_db_unittest.cc +++ b/chromium/components/history/core/browser/history_backend_db_unittest.cc @@ -1583,6 +1583,121 @@ TEST_F(HistoryBackendDBTest, MigrateDownloadSliceFinished) { } } +// Test to verify the incremented_omnibox_typed_score column will be correctly +// added to visits table during migration to version 40. +TEST_F(HistoryBackendDBTest, MigrateVisitsWithoutIncrementedOmniboxTypedScore) { + ASSERT_NO_FATAL_FAILURE(CreateDBVersion(39)); + + const VisitID visit_id1 = 1; + const VisitID visit_id2 = 2; + const URLID url_id1 = 3; + const URLID url_id2 = 4; + const base::Time visit_time1(base::Time::Now()); + const base::Time visit_time2(base::Time::Now()); + const VisitID referring_visit1 = 0; + const VisitID referring_visit2 = 0; + const ui::PageTransition transition1 = ui::PAGE_TRANSITION_LINK; + const ui::PageTransition transition2 = ui::PAGE_TRANSITION_TYPED; + const SegmentID segment_id1 = 7; + const SegmentID segment_id2 = 8; + const base::TimeDelta visit_duration1(base::TimeDelta::FromSeconds(30)); + const base::TimeDelta visit_duration2(base::TimeDelta::FromSeconds(45)); + + const char kInsertStatement[] = + "INSERT INTO visits " + "(id, url, visit_time, from_visit, transition, segment_id, " + "visit_duration) VALUES (?, ?, ?, ?, ?, ?, ?)"; + + { + // Open the db for manual manipulation. + sql::Connection db; + ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename))); + + // Add entries to visits. + { + sql::Statement s(db.GetUniqueStatement(kInsertStatement)); + s.BindInt64(0, visit_id1); + s.BindInt64(1, url_id1); + s.BindInt64(2, visit_time1.ToDeltaSinceWindowsEpoch().InMicroseconds()); + s.BindInt64(3, referring_visit1); + s.BindInt64(4, transition1); + s.BindInt64(5, segment_id1); + s.BindInt64(6, visit_duration1.InMicroseconds()); + ASSERT_TRUE(s.Run()); + } + { + sql::Statement s(db.GetUniqueStatement(kInsertStatement)); + s.BindInt64(0, visit_id2); + s.BindInt64(1, url_id2); + s.BindInt64(2, visit_time2.ToDeltaSinceWindowsEpoch().InMicroseconds()); + s.BindInt64(3, referring_visit2); + s.BindInt64(4, transition2); + s.BindInt64(5, segment_id2); + s.BindInt64(6, visit_duration2.InMicroseconds()); + ASSERT_TRUE(s.Run()); + } + } + + // Re-open the db, triggering migration. + CreateBackendAndDatabase(); + + VisitRow visit_row1; + db_->GetRowForVisit(visit_id1, &visit_row1); + EXPECT_FALSE(visit_row1.incremented_omnibox_typed_score); + + VisitRow visit_row2; + db_->GetRowForVisit(visit_id2, &visit_row2); + EXPECT_TRUE(visit_row2.incremented_omnibox_typed_score); +} + +// Tests that the migration code correctly handles rows in the visit database +// that may be in an invalid state where visit_id == referring_visit. Regression +// test for https://crbug.com/847246. +TEST_F(HistoryBackendDBTest, + MigrateVisitsWithoutIncrementedOmniboxTypedScore_BadRow) { + ASSERT_NO_FATAL_FAILURE(CreateDBVersion(39)); + + const VisitID visit_id = 1; + const URLID url_id = 2; + const base::Time visit_time(base::Time::Now()); + // visit_id == referring_visit will trigger DCHECK_NE in UpdateVisitRow. + const VisitID referring_visit = 1; + const ui::PageTransition transition = ui::PAGE_TRANSITION_TYPED; + const SegmentID segment_id = 8; + const base::TimeDelta visit_duration(base::TimeDelta::FromSeconds(45)); + + const char kInsertStatement[] = + "INSERT INTO visits " + "(id, url, visit_time, from_visit, transition, segment_id, " + "visit_duration) VALUES (?, ?, ?, ?, ?, ?, ?)"; + + { + // Open the db for manual manipulation. + sql::Connection db; + ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename))); + + // Add entry to visits. + sql::Statement s(db.GetUniqueStatement(kInsertStatement)); + s.BindInt64(0, visit_id); + s.BindInt64(1, url_id); + s.BindInt64(2, visit_time.ToDeltaSinceWindowsEpoch().InMicroseconds()); + s.BindInt64(3, referring_visit); + s.BindInt64(4, transition); + s.BindInt64(5, segment_id); + s.BindInt64(6, visit_duration.InMicroseconds()); + ASSERT_TRUE(s.Run()); + } + + // Re-open the db, triggering migration. + CreateBackendAndDatabase(); + + // Field should be false since the migration won't update it from the default + // due to the invalid state of the row. + VisitRow visit_row; + db_->GetRowForVisit(visit_id, &visit_row); + EXPECT_FALSE(visit_row.incremented_omnibox_typed_score); +} + bool FilterURL(const GURL& url) { return url.SchemeIsHTTPOrHTTPS(); } diff --git a/chromium/components/history/core/browser/history_backend_notifier.h b/chromium/components/history/core/browser/history_backend_notifier.h index 668185016ec..9ede9c0b49e 100644 --- a/chromium/components/history/core/browser/history_backend_notifier.h +++ b/chromium/components/history/core/browser/history_backend_notifier.h @@ -40,17 +40,9 @@ class HistoryBackendNotifier { virtual void NotifyURLsModified(const URLRows& changed_urls) = 0; // Sends notification that some or the totality of the URLs have been - // deleted. If time_range.IsValid() is true, all URLs between - // time_range.begin() and time_range.end() have been removed. - // If time_range.IsAllTime() is true, then all the URLs in the - // history have been deleted, otherwise |deleted_urls| list the deleted URLs. - // If the URL deletion is due to expiration, then |expired| is true. - // |favicon_urls| is the list of favicon URLs that correspond to the deleted - // URLs (empty if |time_range.IsAllTime()| is true). - virtual void NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_urls, - const std::set<GURL>& favicon_urls) = 0; + // deleted. + // |deletion_info| describes the urls that have been removed from history. + virtual void NotifyURLsDeleted(DeletionInfo deletion_info) = 0; }; } // namespace history diff --git a/chromium/components/history/core/browser/history_backend_unittest.cc b/chromium/components/history/core/browser/history_backend_unittest.cc index e1eb945adff..93859a28f9b 100644 --- a/chromium/components/history/core/browser/history_backend_unittest.cc +++ b/chromium/components/history/core/browser/history_backend_unittest.cc @@ -137,10 +137,7 @@ class HistoryBackendTestDelegate : public HistoryBackend::Delegate { const RedirectList& redirects, base::Time visit_time) override; void NotifyURLsModified(const URLRows& changed_urls) override; - void NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) override; + void NotifyURLsDeleted(DeletionInfo deletion_info) override; void NotifyKeywordSearchTermUpdated(const URLRow& row, KeywordID keyword_id, const base::string16& term) override; @@ -228,14 +225,10 @@ class HistoryBackendTestBase : public testing::Test { urls_modified_notifications_.push_back(changed_urls); } - void NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) { - mem_backend_->OnURLsDeleted(nullptr, time_range.IsAllTime(), expired, - deleted_rows, favicon_urls); - urls_deleted_notifications_.push_back( - std::make_pair(time_range.IsAllTime(), expired)); + void NotifyURLsDeleted(DeletionInfo deletion_info) { + mem_backend_->OnURLsDeleted(nullptr, deletion_info); + urls_deleted_notifications_.push_back(std::make_pair( + deletion_info.IsAllHistory(), deletion_info.is_from_expiration())); } void NotifyKeywordSearchTermUpdated(const URLRow& row, @@ -268,7 +261,7 @@ class HistoryBackendTestBase : public testing::Test { } void TearDown() override { - if (backend_.get()) + if (backend_) backend_->Closing(); backend_ = nullptr; mem_backend_.reset(); @@ -317,12 +310,8 @@ void HistoryBackendTestDelegate::NotifyURLsModified( test_->NotifyURLsModified(changed_urls); } -void HistoryBackendTestDelegate::NotifyURLsDeleted( - const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) { - test_->NotifyURLsDeleted(time_range, expired, deleted_rows, favicon_urls); +void HistoryBackendTestDelegate::NotifyURLsDeleted(DeletionInfo deletion_info) { + test_->NotifyURLsDeleted(std::move(deletion_info)); } void HistoryBackendTestDelegate::NotifyKeywordSearchTermUpdated( @@ -502,8 +491,7 @@ class InMemoryHistoryBackendTest : public HistoryBackendTestBase { if (row2) rows.push_back(*row2); if (row3) rows.push_back(*row3); - NotifyURLsDeleted(DeletionTimeRange::Invalid(), false, rows, - std::set<GURL>()); + NotifyURLsDeleted(DeletionInfo::ForUrls(rows, std::set<GURL>())); } size_t GetNumberOfMatchingSearchTerms(const int keyword_id, @@ -2539,7 +2527,7 @@ TEST_F(HistoryBackendTest, FaviconChangedNotificationNewFavicon) { backend_->SetFavicons({page_url1}, IconType::kFavicon, icon_url1, bitmaps); ASSERT_EQ(1u, favicon_changed_notifications_page_urls().size()); EXPECT_EQ(page_url1, favicon_changed_notifications_page_urls()[0]); - EXPECT_EQ(0u, favicon_changed_notifications_icon_urls().size()); + EXPECT_EQ(1u, favicon_changed_notifications_icon_urls().size()); ClearBroadcastedNotifications(); } @@ -2553,7 +2541,7 @@ TEST_F(HistoryBackendTest, FaviconChangedNotificationNewFavicon) { bitmap_data, kSmallSize); ASSERT_EQ(1u, favicon_changed_notifications_page_urls().size()); EXPECT_EQ(page_url2, favicon_changed_notifications_page_urls()[0]); - EXPECT_EQ(0u, favicon_changed_notifications_icon_urls().size()); + EXPECT_EQ(1u, favicon_changed_notifications_icon_urls().size()); } } @@ -3870,6 +3858,28 @@ TEST_F(HistoryBackendTest, DatabaseError) { base::RunLoop().RunUntilIdle(); } +// Tests that calling DatabaseErrorCallback results in killing the database and +// notifying the TypedURLSyncBridge at the same time so that no further +// notification from the backend can lead to the bridge. (Regression test for +// https://crbug.com/853395) +TEST_F(HistoryBackendTest, DatabaseErrorSynchronouslyKillAndNotifyBridge) { + // Notify the backend that a database error occurred. + backend_->DatabaseErrorCallback(SQLITE_CORRUPT, nullptr); + // In-between (before the posted task finishes), we can again delete all + // history. + backend_->ExpireHistoryBetween(/*restrict_urls=*/std::set<GURL>(), + /*begin_time=*/base::Time(), + /*end_time=*/base::Time::Max()); + + // Run loop to let the posted task to kill the DB run. + base::RunLoop().RunUntilIdle(); + // After DB is destroyed, we can again try to delete all history (with no + // effect but it should not crash). + backend_->ExpireHistoryBetween(/*restrict_urls=*/std::set<GURL>(), + /*begin_time=*/base::Time(), + /*end_time=*/base::Time::Max()); +} + // Common implementation for the two tests below, given that the only difference // between them is the type of the notification sent out. void InMemoryHistoryBackendTest::TestAddingAndChangingURLRows( @@ -3955,8 +3965,7 @@ TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedEnMasse) { SimulateNotificationURLsModified(mem_backend_.get(), &row1, &row2, &row3); // Now notify the in-memory database that all history has been deleted. - mem_backend_->OnURLsDeleted(nullptr, true, false, URLRows(), - std::set<GURL>()); + mem_backend_->OnURLsDeleted(nullptr, history::DeletionInfo::ForAllHistory()); // Expect that everything goes away. EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), nullptr)); diff --git a/chromium/components/history/core/browser/history_constants.cc b/chromium/components/history/core/browser/history_constants.cc index 79cd33fa6e0..d657062d4d8 100644 --- a/chromium/components/history/core/browser/history_constants.cc +++ b/chromium/components/history/core/browser/history_constants.cc @@ -18,6 +18,8 @@ const base::FilePath::CharType kTopSitesFilename[] = const int kMaxTopHosts = 50; +const int kMaxTitleChanges = 10; + base::TimeDelta GetTitleSettingWindow() { const auto value = base::TimeDelta::FromSeconds(5); return value; diff --git a/chromium/components/history/core/browser/history_constants.h b/chromium/components/history/core/browser/history_constants.h index b89804c49c3..1f3d88896da 100644 --- a/chromium/components/history/core/browser/history_constants.h +++ b/chromium/components/history/core/browser/history_constants.h @@ -18,6 +18,11 @@ extern const base::FilePath::CharType kTopSitesFilename[]; // The maximum size of the list returned by history::HistoryService::TopHosts(). extern const int kMaxTopHosts; +// The maximum number of times a page can change it's title during the relevant +// timestamp (page is either loading is has recently loaded as per +// GetTitleSettingWindow() below). +extern const int kMaxTitleChanges; + // The span of time after load is complete during which a page may set its title // and have the title change be saved in history. base::TimeDelta GetTitleSettingWindow(); diff --git a/chromium/components/history/core/browser/history_database.cc b/chromium/components/history/core/browser/history_database.cc index 2a983559f11..d80bc2395a4 100644 --- a/chromium/components/history/core/browser/history_database.cc +++ b/chromium/components/history/core/browser/history_database.cc @@ -38,7 +38,7 @@ namespace { // Current version number. We write databases at the "current" version number, // but any previous version that can read the "compatible" one can make do with // our database without *too* many bad effects. -const int kCurrentVersionNumber = 39; +const int kCurrentVersionNumber = 40; const int kCompatibleVersionNumber = 16; const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold"; const int kMaxHostsInMemory = 10000; @@ -600,6 +600,13 @@ sql::InitStatus HistoryDatabase::EnsureCurrentVersion() { meta_table_.SetVersionNumber(cur_version); } + if (cur_version == 39) { + if (!MigrateVisitsWithoutIncrementedOmniboxTypedScore()) + return LogMigrationFailure(39); + cur_version++; + meta_table_.SetVersionNumber(cur_version); + } + // ========================= ^^ new migration code goes here ^^ // ADDING NEW MIGRATION CODE // ========================= diff --git a/chromium/components/history/core/browser/history_database_params.cc b/chromium/components/history/core/browser/history_database_params.cc index 3f02563a42a..ccc6983de91 100644 --- a/chromium/components/history/core/browser/history_database_params.cc +++ b/chromium/components/history/core/browser/history_database_params.cc @@ -7,10 +7,7 @@ namespace history { HistoryDatabaseParams::HistoryDatabaseParams() - : history_dir(), - download_interrupt_reason_none(0), - download_interrupt_reason_crash(0) { -} + : download_interrupt_reason_none(0), download_interrupt_reason_crash(0) {} HistoryDatabaseParams::HistoryDatabaseParams( const base::FilePath& history_dir, diff --git a/chromium/components/history/core/browser/history_model_worker.cc b/chromium/components/history/core/browser/history_model_worker.cc index ed8a970f4b5..c68b377ea50 100644 --- a/chromium/components/history/core/browser/history_model_worker.cc +++ b/chromium/components/history/core/browser/history_model_worker.cc @@ -42,7 +42,7 @@ void PostWorkerTask( const base::WeakPtr<history::HistoryService>& history_service, base::OnceClosure work, base::CancelableTaskTracker* cancelable_tracker) { - if (history_service.get()) { + if (history_service) { history_service->ScheduleDBTask( FROM_HERE, std::make_unique<WorkerTask>(std::move(work)), cancelable_tracker); @@ -62,7 +62,7 @@ HistoryModelWorker::HistoryModelWorker( // constructed and deleted on the same sequence. cancelable_tracker_(new base::CancelableTaskTracker, base::OnTaskRunnerDeleter(ui_thread_)) { - CHECK(history_service.get()); + CHECK(history_service); DCHECK(ui_thread_->BelongsToCurrentThread()); } diff --git a/chromium/components/history/core/browser/history_querying_unittest.cc b/chromium/components/history/core/browser/history_querying_unittest.cc index fede4e3f9e6..04f821b90e4 100644 --- a/chromium/components/history/core/browser/history_querying_unittest.cc +++ b/chromium/components/history/core/browser/history_querying_unittest.cc @@ -183,7 +183,7 @@ class HistoryQueryTest : public testing::Test { void TearDown() override { if (history_) { history_->SetOnBackendDestroyTask( - base::MessageLoop::QuitWhenIdleClosure()); + base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); history_->Cleanup(); history_.reset(); base::RunLoop().Run(); // Wait for the other thread. diff --git a/chromium/components/history/core/browser/history_service.cc b/chromium/components/history/core/browser/history_service.cc index d10919a3946..2f4975b203b 100644 --- a/chromium/components/history/core/browser/history_service.cc +++ b/chromium/components/history/core/browser/history_service.cc @@ -152,14 +152,10 @@ class HistoryService::BackendDelegate : public HistoryBackend::Delegate { history_service_, changed_urls)); } - void NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) override { + void NotifyURLsDeleted(DeletionInfo deletion_info) override { service_task_runner_->PostTask( - FROM_HERE, - base::Bind(&HistoryService::NotifyURLsDeleted, history_service_, - time_range, expired, deleted_rows, favicon_urls)); + FROM_HERE, base::BindOnce(&HistoryService::NotifyURLsDeleted, + history_service_, std::move(deletion_info))); } void NotifyKeywordSearchTermUpdated(const URLRow& row, @@ -344,7 +340,7 @@ void HistoryService::TopHosts(size_t num_hosts, callback); } -void HistoryService::GetCountsAndLastVisitForOrigins( +void HistoryService::GetCountsAndLastVisitForOriginsForTesting( const std::set<GURL>& origins, const GetCountsAndLastVisitForOriginsCallback& callback) const { DCHECK(backend_task_runner_) << "History service being called after cleanup"; @@ -903,7 +899,7 @@ void HistoryService::Cleanup() { history_client_->Shutdown(); // Unload the backend. - if (history_backend_.get()) { + if (history_backend_) { // Get rid of the in-memory backend. in_memory_backend_.reset(); @@ -1109,6 +1105,19 @@ void HistoryService::ExpireHistory( callback); } +void HistoryService::ExpireHistoryBeforeForTesting( + base::Time end_time, + base::OnceClosure callback, + base::CancelableTaskTracker* tracker) { + DCHECK(backend_task_runner_) << "History service being called after cleanup"; + DCHECK(thread_checker_.CalledOnValidThread()); + tracker->PostTaskAndReply( + backend_task_runner_.get(), FROM_HERE, + base::BindOnce(&HistoryBackend::ExpireHistoryBeforeForTesting, + history_backend_, end_time), + std::move(callback)); +} + void HistoryService::ExpireLocalAndRemoteHistoryBetween( WebHistoryService* web_history, const std::set<GURL>& restrict_urls, @@ -1182,12 +1191,8 @@ void HistoryService::NotifyURLsModified(const URLRows& changed_urls) { observer.OnURLsModified(this, changed_urls); } -void HistoryService::NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) { +void HistoryService::NotifyURLsDeleted(const DeletionInfo& deletion_info) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(!time_range.IsAllTime() || deleted_rows.empty()); if (!backend_task_runner_) return; @@ -1200,23 +1205,19 @@ void HistoryService::NotifyURLsDeleted(const DeletionTimeRange& time_range, // respectful of privacy and never tell the user something is gone when it // isn't. Therefore, we update the delete URLs after the fact. if (visit_delegate_) { - if (time_range.IsAllTime()) { + if (deletion_info.IsAllHistory()) { visit_delegate_->DeleteAllURLs(); } else { std::vector<GURL> urls; - urls.reserve(deleted_rows.size()); - for (const auto& row : deleted_rows) + urls.reserve(deletion_info.deleted_rows().size()); + for (const auto& row : deletion_info.deleted_rows()) urls.push_back(row.url()); visit_delegate_->DeleteURLs(urls); } } - for (HistoryServiceObserver& observer : observers_) { - observer.OnURLsDeleted(this, time_range.IsAllTime(), expired, deleted_rows, - favicon_urls); - observer.OnURLsDeleted(this, time_range, expired, deleted_rows, - favicon_urls); - } + for (HistoryServiceObserver& observer : observers_) + observer.OnURLsDeleted(this, deletion_info); } void HistoryService::NotifyHistoryServiceLoaded() { diff --git a/chromium/components/history/core/browser/history_service.h b/chromium/components/history/core/browser/history_service.h index d0e1a2805fb..6c4aeb6904e 100644 --- a/chromium/components/history/core/browser/history_service.h +++ b/chromium/components/history/core/browser/history_service.h @@ -161,7 +161,7 @@ class HistoryService : public syncer::SyncableService, public KeyedService { // Gets the counts and most recent visit date of URLs that belong to |origins| // in the history database. - void GetCountsAndLastVisitForOrigins( + void GetCountsAndLastVisitForOriginsForTesting( const std::set<GURL>& origins, const GetCountsAndLastVisitForOriginsCallback& callback) const; @@ -371,6 +371,12 @@ class HistoryService : public syncer::SyncableService, public KeyedService { const base::Closure& callback, base::CancelableTaskTracker* tracker); + // Expires all visits before and including the given time, updating the URLs + // accordingly. + void ExpireHistoryBeforeForTesting(base::Time end_time, + base::OnceClosure callback, + base::CancelableTaskTracker* tracker); + // Removes all visits to the given URLs in the specified time range. Calls // ExpireHistoryBetween() to delete local visits, and handles deletion of // synced visits if appropriate. @@ -605,16 +611,8 @@ class HistoryService : public syncer::SyncableService, public KeyedService { void NotifyURLsModified(const URLRows& changed_urls); // Notify all HistoryServiceObservers registered that URLs have been deleted. - // |all_history| is set to true, if all the URLs are deleted. - // When set to true, |deleted_rows| and |favicon_urls| are - // undefined. - // |expired| is set to true, if the URL deletion is due to expiration. - // |deleted_rows| list of the deleted URLs. - // |favicon_urls| list of favicon URLs that correspond to the deleted URLs. - void NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls); + // |deletion_info| describes the urls that have been removed from history. + void NotifyURLsDeleted(const DeletionInfo& deletion_info); // Notify all HistoryServiceObservers registered that the // HistoryService has finished loading. diff --git a/chromium/components/history/core/browser/history_service_observer.h b/chromium/components/history/core/browser/history_service_observer.h index 3220ce56e47..e0809335a49 100644 --- a/chromium/components/history/core/browser/history_service_observer.h +++ b/chromium/components/history/core/browser/history_service_observer.h @@ -39,36 +39,11 @@ class HistoryServiceObserver { virtual void OnURLsModified(HistoryService* history_service, const URLRows& changed_urls) {} - // Called when one or more of URLs are deleted. + // Called when one or more URLs are deleted. // - // |all_history| is set to true, if all the URLs are deleted. - // When set to true, |deleted_rows| and |favicon_urls| are - // undefined. - // |expired| is set to true, if the URL deletion is due to expiration. - // |deleted_rows| list of the deleted URLs. - // |favicon_urls| list of favicon URLs that correspond to the deleted URLs. - // DEPRECATED, use OnURLsDeleted() with |time_range| parameter. - // TODO(dullweber): Migrate observers to new OnURLsDeleted() method. + // |deletion_info| describes the urls that have been removed from history. virtual void OnURLsDeleted(HistoryService* history_service, - bool all_history, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) {} - - // Called when one or more of URLs are deleted. - // - // |time_range| If time_range.IsValid() is true, all URLs between - // time_range.begin() and time_range.end() have been removed. - // If time_range.IsAllTime() is true, all URLs are deleted and - // |deleted_rows| and |favicon_urls| are undefined. - // |expired| is set to true, if the URL deletion is due to expiration. - // |deleted_rows| list of the deleted URLs. - // |favicon_urls| list of favicon URLs that correspond to the deleted URLs. - virtual void OnURLsDeleted(HistoryService* history_service, - const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) {} + const DeletionInfo& deletion_info) {} // Is called to notify when |history_service| has finished loading. virtual void OnHistoryServiceLoaded(HistoryService* history_service) {} diff --git a/chromium/components/history/core/browser/history_service_unittest.cc b/chromium/components/history/core/browser/history_service_unittest.cc index 4ed089e1f4a..eb65c9ab1c3 100644 --- a/chromium/components/history/core/browser/history_service_unittest.cc +++ b/chromium/components/history/core/browser/history_service_unittest.cc @@ -25,7 +25,6 @@ #include "base/files/scoped_temp_dir.h" #include "base/location.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/strings/utf_string_conversions.h" @@ -85,7 +84,7 @@ class HistoryServiceTest : public testing::Test { // Make sure we don't have any event pending that could disrupt the next // test. base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); base::RunLoop().Run(); } @@ -94,7 +93,7 @@ class HistoryServiceTest : public testing::Test { history_service_->ClearCachedDataForContextID(nullptr); history_service_->SetOnBackendDestroyTask( - base::MessageLoop::QuitWhenIdleClosure()); + base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); history_service_->Cleanup(); history_service_.reset(); diff --git a/chromium/components/history/core/browser/history_types.cc b/chromium/components/history/core/browser/history_types.cc index ffbbff03376..67263b5344c 100644 --- a/chromium/components/history/core/browser/history_types.cc +++ b/chromium/components/history/core/browser/history_types.cc @@ -20,12 +20,14 @@ VisitRow::VisitRow(URLID arg_url_id, base::Time arg_visit_time, VisitID arg_referring_visit, ui::PageTransition arg_transition, - SegmentID arg_segment_id) + SegmentID arg_segment_id, + bool arg_incremented_omnibox_typed_score) : url_id(arg_url_id), visit_time(arg_visit_time), referring_visit(arg_referring_visit), transition(arg_transition), - segment_id(arg_segment_id) {} + segment_id(arg_segment_id), + incremented_omnibox_typed_score(arg_incremented_omnibox_typed_score) {} VisitRow::~VisitRow() { } @@ -369,4 +371,44 @@ bool DeletionTimeRange::IsAllTime() const { return begin_.is_null() && (end_.is_null() || end_.is_max()); } +// DeletionInfo +// ---------------------------------------------------------- + +// static +DeletionInfo DeletionInfo::ForAllHistory() { + return DeletionInfo(DeletionTimeRange::AllTime(), false, {}, {}, + base::nullopt); +} + +// static +DeletionInfo DeletionInfo::ForUrls(URLRows deleted_rows, + std::set<GURL> favicon_urls) { + return DeletionInfo(DeletionTimeRange::Invalid(), false, + std::move(deleted_rows), std::move(favicon_urls), + base::nullopt); +} + +DeletionInfo::DeletionInfo(const DeletionTimeRange& time_range, + bool is_from_expiration, + URLRows deleted_rows, + std::set<GURL> favicon_urls, + base::Optional<std::set<GURL>> restrict_urls) + : time_range_(time_range), + is_from_expiration_(is_from_expiration), + deleted_rows_(std::move(deleted_rows)), + favicon_urls_(std::move(favicon_urls)), + restrict_urls_(std::move(restrict_urls)) { + // If time_range is all time or invalid, restrict_urls should be empty. + DCHECK(!time_range_.IsAllTime() || !restrict_urls_.has_value()); + DCHECK(time_range_.IsValid() || !restrict_urls_.has_value()); + // If restrict_urls_ is defined, it should be non-empty. + DCHECK(!restrict_urls_.has_value() || !restrict_urls_->empty()); +}; + +DeletionInfo::~DeletionInfo() = default; + +DeletionInfo::DeletionInfo(DeletionInfo&& other) noexcept = default; + +DeletionInfo& DeletionInfo::operator=(DeletionInfo&& rhs) noexcept = default; + } // namespace history diff --git a/chromium/components/history/core/browser/history_types.h b/chromium/components/history/core/browser/history_types.h index ff27b0d7406..5e72a015705 100644 --- a/chromium/components/history/core/browser/history_types.h +++ b/chromium/components/history/core/browser/history_types.h @@ -17,6 +17,7 @@ #include "base/containers/stack_container.h" #include "base/macros.h" #include "base/memory/ref_counted_memory.h" +#include "base/optional.h" #include "base/strings/string16.h" #include "base/time/time.h" #include "components/favicon_base/favicon_types.h" @@ -72,7 +73,8 @@ class VisitRow { base::Time arg_visit_time, VisitID arg_referring_visit, ui::PageTransition arg_transition, - SegmentID arg_segment_id); + SegmentID arg_segment_id, + bool arg_incremented_omnibox_typed_score); ~VisitRow(); // ID of this row (visit ID, used a a referrer for other visits). @@ -100,6 +102,9 @@ class VisitRow { // the visit was present. base::TimeDelta visit_duration; + // Records whether the visit incremented the omnibox typed score. + bool incremented_omnibox_typed_score = false; + // Compares two visits based on dates, for sorting. bool operator<(const VisitRow& other) const { return visit_time < other.visit_time; @@ -622,6 +627,97 @@ class DeletionTimeRange { base::Time end_; }; +// Describes the urls that have been removed due to a history deletion. +// If |IsAllHistory()| returns true, all urls haven been deleted. +// In this case, |deleted_rows()| and |favicon_urls()| are undefined. +// Otherwise |deleted_rows()| contains the urls where all visits have been +// removed from history. +// If |expired()| returns true, this deletion is due to a regularly performed +// history expiration. Otherwise it is an explicit deletion due to a user +// action. +class DeletionInfo { + public: + // Returns a DeletionInfo that covers all history. + static DeletionInfo ForAllHistory(); + // Returns a DeletionInfo with invalid time range for the given urls. + static DeletionInfo ForUrls(URLRows deleted_rows, + std::set<GURL> favicon_urls); + + DeletionInfo(const DeletionTimeRange& time_range, + bool is_from_expiration, + URLRows deleted_rows, + std::set<GURL> favicon_urls, + base::Optional<std::set<GURL>> restrict_urls); + + ~DeletionInfo(); + // Move-only because of potentially large containers. + DeletionInfo(DeletionInfo&& other) noexcept; + DeletionInfo& operator=(DeletionInfo&& rhs) noexcept; + + // If IsAllHistory() returns true, all URLs are deleted and |deleted_rows()| + // and |favicon_urls()| are undefined. + bool IsAllHistory() const { return time_range_.IsAllTime(); } + + // If time_range.IsValid() is true, |restrict_urls| (or all URLs if empty) + // between time_range.begin() and time_range.end() have been removed. + const DeletionTimeRange& time_range() const { return time_range_; } + + // Restricts deletions within |time_range()|. + const base::Optional<std::set<GURL>>& restrict_urls() const { + return restrict_urls_; + } + + // Returns true, if the URL deletion is due to expiration. + bool is_from_expiration() const { return is_from_expiration_; } + + // Returns the list of the deleted URLs. + // Undefined if |IsAllHistory()| returns true. + const URLRows& deleted_rows() const { return deleted_rows_; } + + // Returns the list of favicon URLs that correspond to the deleted URLs. + // Undefined if |IsAllHistory()| returns true. + const std::set<GURL>& favicon_urls() const { return favicon_urls_; } + + // Returns a map from origins with deleted urls to a count of remaining URLs + // and the last visited time. + const OriginCountAndLastVisitMap& deleted_urls_origin_map() const { + // The map should only be accessed after it has been populated. + DCHECK(deleted_rows_.empty() || !deleted_urls_origin_map_.empty()); + return deleted_urls_origin_map_; + } + + // Populates deleted_urls_origin_map. + void set_deleted_urls_origin_map(OriginCountAndLastVisitMap origin_map) { + DCHECK(deleted_urls_origin_map_.empty()); + deleted_urls_origin_map_ = std::move(origin_map); + } + + private: + DeletionTimeRange time_range_; + bool is_from_expiration_; + URLRows deleted_rows_; + std::set<GURL> favicon_urls_; + base::Optional<std::set<GURL>> restrict_urls_; + OriginCountAndLastVisitMap deleted_urls_origin_map_; + + DISALLOW_COPY_AND_ASSIGN(DeletionInfo); +}; + +// Represents a visit to a domain. +class DomainVisit { + public: + DomainVisit(const std::string& domain, base::Time visit_time) + : domain_(domain), visit_time_(visit_time) {} + + const std::string& domain() const { return domain_; } + + const base::Time visit_time() const { return visit_time_; } + + private: + std::string domain_; + base::Time visit_time_; +}; + } // namespace history #endif // COMPONENTS_HISTORY_CORE_BROWSER_HISTORY_TYPES_H_ diff --git a/chromium/components/history/core/browser/in_memory_database.cc b/chromium/components/history/core/browser/in_memory_database.cc index 3ae5f679041..dfac5c5e125 100644 --- a/chromium/components/history/core/browser/in_memory_database.cc +++ b/chromium/components/history/core/browser/in_memory_database.cc @@ -13,8 +13,7 @@ namespace history { -InMemoryDatabase::InMemoryDatabase() : URLDatabase() { -} +InMemoryDatabase::InMemoryDatabase() {} InMemoryDatabase::~InMemoryDatabase() { } diff --git a/chromium/components/history/core/browser/in_memory_history_backend.cc b/chromium/components/history/core/browser/in_memory_history_backend.cc index 9e11553e1ea..8384e6b5671 100644 --- a/chromium/components/history/core/browser/in_memory_history_backend.cc +++ b/chromium/components/history/core/browser/in_memory_history_backend.cc @@ -58,13 +58,10 @@ void InMemoryHistoryBackend::OnURLsModified(HistoryService* history_service, } void InMemoryHistoryBackend::OnURLsDeleted(HistoryService* history_service, - bool all_history, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) { + const DeletionInfo& deletion_info) { DCHECK(db_); - if (all_history) { + if (deletion_info.IsAllHistory()) { // When all history is deleted, the individual URLs won't be listed. Just // create a new database to quickly clear everything out. db_.reset(new InMemoryDatabase); @@ -74,7 +71,7 @@ void InMemoryHistoryBackend::OnURLsDeleted(HistoryService* history_service, } // Delete all matching URLs in our database. - for (const auto& row : deleted_rows) { + for (const auto& row : deletion_info.deleted_rows()) { // This will also delete the corresponding keyword search term. // Ignore errors, as we typically only cache a subset of URLRows. db_->DeleteURLRow(row.id()); diff --git a/chromium/components/history/core/browser/in_memory_history_backend.h b/chromium/components/history/core/browser/in_memory_history_backend.h index 2462543c512..557467dc99c 100644 --- a/chromium/components/history/core/browser/in_memory_history_backend.h +++ b/chromium/components/history/core/browser/in_memory_history_backend.h @@ -78,10 +78,7 @@ class InMemoryHistoryBackend : public HistoryServiceObserver { void OnURLsModified(HistoryService* history_service, const URLRows& changed_urls) override; void OnURLsDeleted(HistoryService* history_service, - bool all_history, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) override; + const DeletionInfo& deletion_info) override; void OnKeywordSearchTermUpdated(HistoryService* history_service, const URLRow& row, KeywordID keyword_id, diff --git a/chromium/components/history/core/browser/thumbnail_database_unittest.cc b/chromium/components/history/core/browser/thumbnail_database_unittest.cc index 4a3f3d793bd..8555de8676f 100644 --- a/chromium/components/history/core/browser/thumbnail_database_unittest.cc +++ b/chromium/components/history/core/browser/thumbnail_database_unittest.cc @@ -928,7 +928,7 @@ TEST_F(ThumbnailDatabaseTest, HasMappingFor) { // Test loading version 3 database. TEST_F(ThumbnailDatabaseTest, Version3) { std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v3.sql"); - ASSERT_TRUE(db.get() != nullptr); + ASSERT_TRUE(db); VerifyTablesAndColumns(&db->db_); // Version 3 is deprecated, the data should all be gone. @@ -938,7 +938,7 @@ TEST_F(ThumbnailDatabaseTest, Version3) { // Test loading version 4 database. TEST_F(ThumbnailDatabaseTest, Version4) { std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v4.sql"); - ASSERT_TRUE(db.get() != nullptr); + ASSERT_TRUE(db); VerifyTablesAndColumns(&db->db_); // Version 4 is deprecated, the data should all be gone. @@ -948,7 +948,7 @@ TEST_F(ThumbnailDatabaseTest, Version4) { // Test loading version 5 database. TEST_F(ThumbnailDatabaseTest, Version5) { std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v5.sql"); - ASSERT_TRUE(db.get() != nullptr); + ASSERT_TRUE(db); VerifyTablesAndColumns(&db->db_); // Version 5 is deprecated, the data should all be gone. @@ -958,7 +958,7 @@ TEST_F(ThumbnailDatabaseTest, Version5) { // Test loading version 6 database. TEST_F(ThumbnailDatabaseTest, Version6) { std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v6.sql"); - ASSERT_TRUE(db.get() != nullptr); + ASSERT_TRUE(db); VerifyTablesAndColumns(&db->db_); // Version 6 is deprecated, the data should all be gone. @@ -968,7 +968,7 @@ TEST_F(ThumbnailDatabaseTest, Version6) { // Test loading version 7 database. TEST_F(ThumbnailDatabaseTest, Version7) { std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v7.sql"); - ASSERT_TRUE(db.get() != nullptr); + ASSERT_TRUE(db); VerifyTablesAndColumns(&db->db_); EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl1, @@ -988,7 +988,7 @@ TEST_F(ThumbnailDatabaseTest, Version7) { // Test loading version 8 database. TEST_F(ThumbnailDatabaseTest, Version8) { std::unique_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v8.sql"); - ASSERT_TRUE(db.get() != nullptr); + ASSERT_TRUE(db); VerifyTablesAndColumns(&db->db_); EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl1, diff --git a/chromium/components/history/core/browser/top_sites.cc b/chromium/components/history/core/browser/top_sites.cc index 5acf6dbf218..c24bacbf1ee 100644 --- a/chromium/components/history/core/browser/top_sites.cc +++ b/chromium/components/history/core/browser/top_sites.cc @@ -9,8 +9,7 @@ namespace history { PrepopulatedPage::PrepopulatedPage() - : most_visited(), favicon_id(-1), thumbnail_id(-1), color() { -} + : favicon_id(-1), thumbnail_id(-1), color() {} PrepopulatedPage::PrepopulatedPage(const GURL& url, const base::string16& title, diff --git a/chromium/components/history/core/browser/top_sites_impl.cc b/chromium/components/history/core/browser/top_sites_impl.cc index 0ed5797f860..eba874f4c99 100644 --- a/chromium/components/history/core/browser/top_sites_impl.cc +++ b/chromium/components/history/core/browser/top_sites_impl.cc @@ -890,19 +890,16 @@ void TopSitesImpl::OnTopSitesAvailableFromHistory( } void TopSitesImpl::OnURLsDeleted(HistoryService* history_service, - bool all_history, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) { + const DeletionInfo& deletion_info) { if (!loaded_) return; - if (all_history) { + if (deletion_info.IsAllHistory()) { SetTopSites(MostVisitedURLList(), CALL_LOCATION_FROM_OTHER_PLACES); backend_->ResetDatabase(); } else { std::set<size_t> indices_to_delete; // Indices into top_sites_. - for (const auto& row : deleted_rows) { + for (const auto& row : deletion_info.deleted_rows()) { if (cache_->IsKnownURL(row.url())) indices_to_delete.insert(cache_->GetURLIndex(row.url())); } diff --git a/chromium/components/history/core/browser/top_sites_impl.h b/chromium/components/history/core/browser/top_sites_impl.h index dc40668f6d1..ea6751b3fb3 100644 --- a/chromium/components/history/core/browser/top_sites_impl.h +++ b/chromium/components/history/core/browser/top_sites_impl.h @@ -268,10 +268,7 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver { // history::HistoryServiceObserver: void OnURLsDeleted(HistoryService* history_service, - bool all_history, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) override; + const DeletionInfo& deletion_info) override; // Ensures that non thread-safe methods are called on the correct thread. base::ThreadChecker thread_checker_; diff --git a/chromium/components/history/core/browser/typed_url_model_type_controller.cc b/chromium/components/history/core/browser/typed_url_model_type_controller.cc index 33fc6d462b4..08fa338e7bc 100644 --- a/chromium/components/history/core/browser/typed_url_model_type_controller.cc +++ b/chromium/components/history/core/browser/typed_url_model_type_controller.cc @@ -9,11 +9,13 @@ #include "components/history/core/browser/history_db_task.h" #include "components/history/core/browser/history_service.h" #include "components/prefs/pref_service.h" +#include "components/sync/driver/model_type_controller.h" #include "components/sync/driver/sync_client.h" +#include "components/sync/model/model_type_controller_delegate.h" using syncer::ModelType; using syncer::ModelTypeController; -using syncer::ModelTypeSyncBridge; +using syncer::ModelTypeControllerDelegate; using syncer::SyncClient; namespace history { @@ -25,7 +27,7 @@ namespace { // the tasks we want to run. class RunTaskOnHistoryThread : public HistoryDBTask { public: - explicit RunTaskOnHistoryThread(ModelTypeController::BridgeTask task) + explicit RunTaskOnHistoryThread(ModelTypeController::ModelTask task) : task_(std::move(task)) {} bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) override { @@ -33,17 +35,20 @@ class RunTaskOnHistoryThread : public HistoryDBTask { // around all the way until DoneRunOnMainThread() is invoked back on the // main thread - we want to release references as soon as possible to avoid // keeping them around too long during shutdown. - TypedURLSyncBridge* bridge = backend->GetTypedURLSyncBridge(); - DCHECK(bridge); + base::WeakPtr<ModelTypeControllerDelegate> delegate = + backend->GetTypedURLSyncBridge() + ->change_processor() + ->GetControllerDelegateOnUIThread(); + DCHECK(delegate); - std::move(task_).Run(bridge); + std::move(task_).Run(delegate); return true; } void DoneRunOnMainThread() override {} protected: - ModelTypeController::BridgeTask task_; + ModelTypeController::ModelTask task_; }; } // namespace @@ -68,8 +73,8 @@ bool TypedURLModelTypeController::ReadyForStart() const { history_disabled_pref_name_); } -void TypedURLModelTypeController::PostBridgeTask(const base::Location& location, - BridgeTask task) { +void TypedURLModelTypeController::PostModelTask(const base::Location& location, + ModelTask task) { history::HistoryService* history = sync_client()->GetHistoryService(); if (!history) { // History must be disabled - don't start. diff --git a/chromium/components/history/core/browser/typed_url_model_type_controller.h b/chromium/components/history/core/browser/typed_url_model_type_controller.h index d528d409fbd..df2a43dded9 100644 --- a/chromium/components/history/core/browser/typed_url_model_type_controller.h +++ b/chromium/components/history/core/browser/typed_url_model_type_controller.h @@ -23,7 +23,7 @@ class TypedURLModelTypeController : public syncer::ModelTypeController { private: // syncer::ModelTypeController implementation. - void PostBridgeTask(const base::Location& location, BridgeTask task) override; + void PostModelTask(const base::Location& location, ModelTask task) override; void OnSavingBrowserHistoryDisabledChanged(); diff --git a/chromium/components/history/core/browser/typed_url_sync_bridge.cc b/chromium/components/history/core/browser/typed_url_sync_bridge.cc index 230ac721223..edbd2b4e795 100644 --- a/chromium/components/history/core/browser/typed_url_sync_bridge.cc +++ b/chromium/components/history/core/browser/typed_url_sync_bridge.cc @@ -281,7 +281,7 @@ void TypedURLSyncBridge::GetData(StorageKeyList storage_keys, continue; std::unique_ptr<syncer::EntityData> entity_data = CreateEntityData(url_row, visits_vector); - if (!entity_data.get()) { + if (!entity_data) { // Cannot create EntityData, ex. no TYPED visits. continue; } @@ -311,7 +311,7 @@ void TypedURLSyncBridge::GetAllData(DataCallback callback) { continue; std::unique_ptr<syncer::EntityData> entity_data = CreateEntityData(url, visits_vector); - if (!entity_data.get()) { + if (!entity_data) { // Cannot create EntityData, ex. no TYPED visits. continue; } @@ -410,8 +410,20 @@ void TypedURLSyncBridge::OnURLsDeleted(HistoryBackend* history_backend, // client with a bad clock setting won't go on an expiration rampage and // delete all history from every client). The server will gracefully age out // the sync DB entries when they've been idle for long enough. - if (expired) + if (expired) { + // Delete metadata from the DB and ask the processor to untrack the entries. + for (const URLRow& row : deleted_rows) { + std::string storage_key = GetStorageKeyFromURLRow(row); + sync_metadata_database_->ClearSyncMetadata(syncer::TYPED_URLS, + storage_key); + // TODO(jkrcal): Untrack the entity from the processor, too (by + // introducing UntrackEntityForStorageKey() function into the change + // processor). Extend the integration tests to cover the crash in + // https://crbug.com/827111. Also add unit-test coverage for expired + // deletions (needs some refactoring in the tests). + } return; + } std::unique_ptr<MetadataChangeList> metadata_change_list = CreateMetadataChangeList(); @@ -431,7 +443,7 @@ void TypedURLSyncBridge::OnURLsDeleted(HistoryBackend* history_backend, } } else { // Delete rows. - for (const auto& row : deleted_rows) { + for (const URLRow& row : deleted_rows) { std::string storage_key = GetStorageKeyFromURLRow(row); change_processor()->Delete(storage_key, metadata_change_list.get()); } @@ -648,8 +660,10 @@ TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls( new_visit->first > visit_ix->visit_time) { ++visit_ix; } - visit_ix = visits->insert(visit_ix, VisitRow(url.id(), new_visit->first, - 0, new_visit->second, 0)); + visit_ix = visits->insert( + visit_ix, + VisitRow(url.id(), new_visit->first, 0, new_visit->second, 0, + HistoryBackend::IsTypedIncrement(new_visit->second))); ++visit_ix; } } @@ -740,7 +754,7 @@ void TypedURLSyncBridge::LoadMetadata() { "TypedURLSyncMetadataDatabase."}); return; } - change_processor()->ModelReadyToSync(this, std::move(batch)); + change_processor()->ModelReadyToSync(std::move(batch)); } void TypedURLSyncBridge::ClearErrorStats() { @@ -1096,7 +1110,7 @@ bool TypedURLSyncBridge::FixupURLAndGetVisits(URLRow* url, } VisitRow visit(url->id(), url->last_visit(), 0, ui::PAGE_TRANSITION_TYPED, - 0); + 0, true); visits->push_back(visit); } @@ -1200,7 +1214,7 @@ void TypedURLSyncBridge::SendTypedURLToProcessor( std::unique_ptr<syncer::EntityData> entity_data = CreateEntityData(row, visits); - if (!entity_data.get()) { + if (!entity_data) { // Cannot create EntityData, ex. no TYPED visits. return; } diff --git a/chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc b/chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc index d96c38b3188..6eb285e17c1 100644 --- a/chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc +++ b/chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc @@ -70,7 +70,9 @@ void AddNewestVisit(ui::PageTransition transition, URLRow* url, VisitVector* visits) { base::Time time = base::Time::FromInternalValue(visit_time); - visits->insert(visits->begin(), VisitRow(url->id(), time, 0, transition, 0)); + visits->insert(visits->begin(), + VisitRow(url->id(), time, 0, transition, 0, + HistoryBackend::IsTypedIncrement(transition))); if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED)) { url->set_typed_count(url->typed_count() + 1); @@ -85,7 +87,8 @@ void AddOldestVisit(ui::PageTransition transition, URLRow* url, VisitVector* visits) { base::Time time = base::Time::FromInternalValue(visit_time); - visits->push_back(VisitRow(url->id(), time, 0, transition, 0)); + visits->push_back(VisitRow(url->id(), time, 0, transition, 0, + HistoryBackend::IsTypedIncrement(transition))); if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED)) { url->set_typed_count(url->typed_count() + 1); @@ -115,11 +118,11 @@ URLRow MakeTypedUrlRow(const std::string& url, if (typed_count > 0) { // Add a typed visit for time |last_visit|. visits->push_back(VisitRow(history_url.id(), last_visit_time, 0, - ui::PAGE_TRANSITION_TYPED, 0)); + ui::PAGE_TRANSITION_TYPED, 0, true)); } else { // Add a non-typed visit for time |last_visit|. visits->push_back(VisitRow(history_url.id(), last_visit_time, 0, - ui::PAGE_TRANSITION_RELOAD, 0)); + ui::PAGE_TRANSITION_RELOAD, 0, false)); } history_url.set_visit_count(visits->size()); @@ -178,10 +181,7 @@ class TestHistoryBackendDelegate : public HistoryBackend::Delegate { const RedirectList& redirects, base::Time visit_time) override {} void NotifyURLsModified(const URLRows& changed_urls) override {} - void NotifyURLsDeleted(const DeletionTimeRange& time_range, - bool expired, - const URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) override {} + void NotifyURLsDeleted(DeletionInfo deletion_info) override {} void NotifyKeywordSearchTermUpdated(const URLRow& row, KeywordID keyword_id, const base::string16& term) override {} @@ -305,14 +305,15 @@ class TypedURLSyncBridgeTest : public testing::Test { int typed_count, int64_t last_visit, bool hidden, + bool update_metadata, EntityChange::ChangeType change_type) { VisitVector visits; URLRow row = MakeTypedUrlRow(url, title, typed_count, last_visit, hidden, &visits); sync_pb::TypedUrlSpecifics typed_url_specifics; WriteToTypedUrlSpecifics(row, visits, &typed_url_specifics); - std::unique_ptr<MetadataChangeList> metadata_changes = - bridge()->CreateMetadataChangeList(); + std::string storage_key = GetStorageKey(typed_url_specifics.url()); + EntityChangeList entity_changes; switch (change_type) { case EntityChange::ACTION_ADD: @@ -320,15 +321,24 @@ class TypedURLSyncBridgeTest : public testing::Test { std::string(), SpecificsToEntity(typed_url_specifics))); break; case EntityChange::ACTION_UPDATE: - entity_changes.push_back( - EntityChange::CreateUpdate(GetStorageKey(typed_url_specifics.url()), - SpecificsToEntity(typed_url_specifics))); + entity_changes.push_back(EntityChange::CreateUpdate( + storage_key, SpecificsToEntity(typed_url_specifics))); break; case EntityChange::ACTION_DELETE: - entity_changes.push_back(EntityChange::CreateDelete( - GetStorageKey(typed_url_specifics.url()))); + entity_changes.push_back(EntityChange::CreateDelete(storage_key)); break; } + + std::unique_ptr<MetadataChangeList> metadata_changes = + bridge()->CreateMetadataChangeList(); + // There needs to be data present in the history DB before inserting + // metadata. + if (update_metadata) { + sync_pb::EntityMetadata metadata; + metadata.set_sequence_number(1); + metadata_changes->UpdateMetadata(storage_key, metadata); + } + bridge()->ApplySyncChanges(std::move(metadata_changes), entity_changes); return visits; } @@ -416,7 +426,8 @@ class TypedURLSyncBridgeTest : public testing::Test { } static VisitRow CreateVisit(ui::PageTransition type, int64_t timestamp) { - return VisitRow(0, base::Time::FromInternalValue(timestamp), 0, type, 0); + return VisitRow(0, base::Time::FromInternalValue(timestamp), 0, type, 0, + HistoryBackend::IsTypedIncrement(type)); } static TypedURLSyncBridge::MergeResult MergeUrls( @@ -998,6 +1009,65 @@ TEST_F(TypedURLSyncBridgeTest, DeleteLocalTypedUrl) { ASSERT_TRUE(deleted_storage_keys.empty()); } +// Expire several (but not all) local typed urls. This has only impact on local +// store (metadata in the db and in-memory maps), nothing gets synced up. +TEST_F(TypedURLSyncBridgeTest, ExpireLocalTypedUrl) { + StartSyncing(std::vector<TypedUrlSpecifics>()); + + URLRows url_rows; + std::vector<VisitVector> visit_vectors; + std::vector<std::string> urls; + urls.push_back("http://pie.com/"); + urls.push_back("http://cake.com/"); + urls.push_back("http://google.com/"); + urls.push_back("http://foo.com/"); + urls.push_back("http://bar.com/"); + + // Add the URLs into the history db and notify the bridge. + ASSERT_TRUE(BuildAndPushLocalChanges(5, 0, urls, &url_rows, &visit_vectors)); + ASSERT_EQ(5U, processor().put_multimap().size()); + int previous_put_size = processor().put_multimap().size(); + // Store the typed_urls incl. metadata into the bridge's database. + for (const std::string& url : urls) { + ApplyUrlAndVisitsChange(url, kTitle, /*typed_count=*/1, /*last_visit=*/3, + /*hidden=*/false, /*update_metadata=*/true, + EntityChange::ACTION_ADD); + } + + // Check all the metadata is here. + MetadataBatch metadata_batch; + metadata_store()->GetAllSyncMetadata(&metadata_batch); + ASSERT_EQ(5u, metadata_batch.TakeAllMetadata().size()); + + // Simulate expiration - delete some urls from the backend and create deleted + // row vector. + URLRows rows; + std::set<std::string> deleted_storage_keys; + for (size_t i = 0; i < 3u; ++i) { + std::string storage_key = GetStorageKey(url_rows[i].url().spec()); + deleted_storage_keys.insert(storage_key); + fake_history_backend_->DeleteURL(url_rows[i].url()); + rows.push_back(url_rows[i]); + } + + // Notify typed url sync service of these URLs getting expired. + bridge()->OnURLsDeleted(fake_history_backend_.get(), /*all_history=*/false, + /*expired=*/true, rows, std::set<GURL>()); + + // This does not propagate to the processor. + EXPECT_EQ(0U, processor().put_multimap().size() - previous_put_size); + EXPECT_EQ(0U, processor().delete_set().size()); + + // The urls are removed from the metadata store. + MetadataBatch smaller_metadata_batch; + metadata_store()->GetAllSyncMetadata(&smaller_metadata_batch); + EXPECT_EQ(2u, smaller_metadata_batch.GetAllMetadata().size()); + for (const auto& kv : smaller_metadata_batch.GetAllMetadata()) { + EXPECT_TRUE(deleted_storage_keys.find(kv.first) == + deleted_storage_keys.end()); + } +} + // Saturate the visits for a typed url with both TYPED and LINK navigations. // Check that no more than kMaxTypedURLVisits are synced, and that LINK visits // are dropped rather than TYPED ones. @@ -1105,8 +1175,10 @@ TEST_F(TypedURLSyncBridgeTest, ThrottleVisitLocalTypedUrl) { // has started. Check that local DB is received the new URL and visit. TEST_F(TypedURLSyncBridgeTest, AddUrlAndVisits) { StartSyncing(std::vector<TypedUrlSpecifics>()); - VisitVector visits = ApplyUrlAndVisitsChange(kURL, kTitle, 1, 3, false, - EntityChange::ACTION_ADD); + VisitVector visits = ApplyUrlAndVisitsChange( + kURL, kTitle, /*typed_count=*/1, /*last_visit=*/3, + /*hidden=*/false, + /*update_metadata=*/false, EntityChange::ACTION_ADD); ASSERT_EQ(0U, processor().put_multimap().size()); ASSERT_EQ(1U, processor().update_multimap().size()); @@ -1138,8 +1210,9 @@ TEST_F(TypedURLSyncBridgeTest, AddUrlAndVisits) { // visit. TEST_F(TypedURLSyncBridgeTest, AddExpiredUrlAndVisits) { StartSyncing(std::vector<TypedUrlSpecifics>()); - VisitVector visits = ApplyUrlAndVisitsChange(kURL, kTitle, 1, kExpiredVisit, - false, EntityChange::ACTION_ADD); + VisitVector visits = ApplyUrlAndVisitsChange( + kURL, kTitle, /*typed_count=*/1, /*last_visit=*/kExpiredVisit, + /*hidden=*/false, /*update_metadata=*/false, EntityChange::ACTION_ADD); ASSERT_EQ(0U, processor().put_multimap().size()); ASSERT_EQ(0U, processor().update_multimap().size()); @@ -1155,8 +1228,10 @@ TEST_F(TypedURLSyncBridgeTest, AddExpiredUrlAndVisits) { TEST_F(TypedURLSyncBridgeTest, UpdateUrlAndVisits) { StartSyncing(std::vector<TypedUrlSpecifics>()); - VisitVector visits = ApplyUrlAndVisitsChange(kURL, kTitle, 1, 3, false, - EntityChange::ACTION_ADD); + VisitVector visits = ApplyUrlAndVisitsChange( + kURL, kTitle, /*typed_count=*/1, /*last_visit=*/3, + /*hidden=*/false, + /*update_metadata=*/false, EntityChange::ACTION_ADD); base::Time visit_time = base::Time::FromInternalValue(3); VisitVector all_visits; URLRow url_row; @@ -1173,8 +1248,10 @@ TEST_F(TypedURLSyncBridgeTest, UpdateUrlAndVisits) { EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row)); EXPECT_EQ(kTitle, base::UTF16ToUTF8(url_row.title())); - VisitVector new_visits = ApplyUrlAndVisitsChange(kURL, kTitle2, 2, 6, false, - EntityChange::ACTION_UPDATE); + VisitVector new_visits = ApplyUrlAndVisitsChange( + kURL, kTitle2, /*typed_count=*/2, /*last_visit=*/6, + /*hidden=*/false, + /*update_metadata=*/false, EntityChange::ACTION_UPDATE); base::Time new_visit_time = base::Time::FromInternalValue(6); url_id = fake_history_backend_->GetIdByUrl(GURL(kURL)); @@ -1219,7 +1296,8 @@ TEST_F(TypedURLSyncBridgeTest, DeleteUrlAndVisits) { // changes back from fake_history_backend_. AddObserver(); - ApplyUrlAndVisitsChange(kURL, kTitle, 1, 3, false, + ApplyUrlAndVisitsChange(kURL, kTitle, /*typed_count=*/1, /*last_visit=*/3, + /*hidden=*/false, /*update_metadata=*/false, EntityChange::ACTION_DELETE); EXPECT_FALSE(fake_history_backend_->GetURL(GURL(kURL), &url_row)); @@ -1241,7 +1319,7 @@ TEST_F(TypedURLSyncBridgeTest, DiffVisitsSame) { for (int64_t visit : visits) { old_visits.push_back(VisitRow(0, base::Time::FromInternalValue(visit), 0, - ui::PAGE_TRANSITION_TYPED, 0)); + ui::PAGE_TRANSITION_TYPED, 0, true)); new_url.add_visits(visit); new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED); } @@ -1271,7 +1349,7 @@ TEST_F(TypedURLSyncBridgeTest, DiffVisitsRemove) { for (int64_t visit : visits_left) { old_visits.push_back(VisitRow(0, base::Time::FromInternalValue(visit), 0, - ui::PAGE_TRANSITION_TYPED, 0)); + ui::PAGE_TRANSITION_TYPED, 0, true)); } for (int64_t visit : visits_right) { @@ -1305,7 +1383,7 @@ TEST_F(TypedURLSyncBridgeTest, DiffVisitsAdd) { for (int64_t visit : visits_left) { old_visits.push_back(VisitRow(0, base::Time::FromInternalValue(visit), 0, - ui::PAGE_TRANSITION_TYPED, 0)); + ui::PAGE_TRANSITION_TYPED, 0, true)); } for (int64_t visit : visits_right) { @@ -1495,7 +1573,7 @@ TEST_F(TypedURLSyncBridgeTest, MergeUrlsAfterExpiration) { // First, create a history row that has two visits, with timestamps 2 and 3. VisitVector(history_visits); history_visits.push_back(VisitRow(0, base::Time::FromInternalValue(2), 0, - ui::PAGE_TRANSITION_TYPED, 0)); + ui::PAGE_TRANSITION_TYPED, 0, true)); URLRow history_url( MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &history_visits)); diff --git a/chromium/components/history/core/browser/visit_database.cc b/chromium/components/history/core/browser/visit_database.cc index 0ef84afb008..8dca02c1f2b 100644 --- a/chromium/components/history/core/browser/visit_database.cc +++ b/chromium/components/history/core/browser/visit_database.cc @@ -17,8 +17,11 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/time/time.h" +#include "components/google/core/browser/google_util.h" +#include "components/history/core/browser/history_backend.h" #include "components/history/core/browser/url_database.h" #include "sql/statement.h" +#include "sql/transaction.h" #include "ui/base/page_transition_types.h" #include "url/url_constants.h" @@ -42,7 +45,8 @@ bool VisitDatabase::InitVisitTable() { "segment_id INTEGER," // Some old DBs may have an "is_indexed" field here, but this is no // longer used and should NOT be read or written from any longer. - "visit_duration INTEGER DEFAULT 0 NOT NULL)")) + "visit_duration INTEGER DEFAULT 0 NOT NULL," + "incremented_omnibox_typed_score BOOLEAN DEFAULT FALSE NOT NULL)")) return false; } @@ -98,6 +102,7 @@ void VisitDatabase::FillVisitRow(const sql::Statement& statement, visit->segment_id = statement.ColumnInt64(5); visit->visit_duration = base::TimeDelta::FromInternalValue(statement.ColumnInt64(6)); + visit->incremented_omnibox_typed_score = statement.ColumnBool(7); } // static @@ -149,16 +154,19 @@ bool VisitDatabase::FillVisitVectorWithOptions(sql::Statement& statement, } VisitID VisitDatabase::AddVisit(VisitRow* visit, VisitSource source) { - sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, + sql::Statement statement(GetDB().GetCachedStatement( + SQL_FROM_HERE, "INSERT INTO visits " "(url, visit_time, from_visit, transition, segment_id, " - "visit_duration) VALUES (?,?,?,?,?,?)")); + "visit_duration, incremented_omnibox_typed_score) " + "VALUES (?,?,?,?,?,?,?)")); statement.BindInt64(0, visit->url_id); statement.BindInt64(1, visit->visit_time.ToInternalValue()); statement.BindInt64(2, visit->referring_visit); statement.BindInt64(3, visit->transition); statement.BindInt64(4, visit->segment_id); statement.BindInt64(5, visit->visit_duration.ToInternalValue()); + statement.BindBool(6, visit->incremented_omnibox_typed_score); if (!statement.Run()) { DVLOG(0) << "Failed to execute visit insert statement: " @@ -235,17 +243,19 @@ bool VisitDatabase::UpdateVisitRow(const VisitRow& visit) { if (visit.visit_id == visit.referring_visit) return false; - sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, + sql::Statement statement(GetDB().GetCachedStatement( + SQL_FROM_HERE, "UPDATE visits SET " "url=?,visit_time=?,from_visit=?,transition=?,segment_id=?," - "visit_duration=? WHERE id=?")); + "visit_duration=?,incremented_omnibox_typed_score=? WHERE id=?")); statement.BindInt64(0, visit.url_id); statement.BindInt64(1, visit.visit_time.ToInternalValue()); statement.BindInt64(2, visit.referring_visit); statement.BindInt64(3, visit.transition); statement.BindInt64(4, visit.segment_id); statement.BindInt64(5, visit.visit_duration.ToInternalValue()); - statement.BindInt64(6, visit.visit_id); + statement.BindBool(6, visit.incremented_omnibox_typed_score); + statement.BindInt64(7, visit.visit_id); return statement.Run(); } @@ -610,6 +620,46 @@ void VisitDatabase::GetVisitsSource(const VisitVector& visits, } } +std::vector<DomainVisit> +VisitDatabase::GetGoogleDomainVisitsFromSearchesInRange(base::Time begin_time, + base::Time end_time) { + sql::Statement statement(GetDB().GetCachedStatement( + SQL_FROM_HERE, + "SELECT " + " visit_time, " + " u.url " + "FROM " + " urls u JOIN visits v ON u.id = v.url " + "WHERE " + // Pre-filtering to limit the number of entries to process in + // C++. The url column is indexed so this makes the query more + // efficient. We then confirm in C++ that the domain of an entry + // is a valid Google domain before counting the visit. + " (u.url LIKE \"https://www.google.__/search%\" OR " + " u.url LIKE \"https://www.google.___/search%\" OR " + " u.url LIKE \"https://www.google.__.__/search%\" OR " + " u.url LIKE \"https://www.google.___.__/search%\") AND " + // Restrict to visits that are more recent than the specified start + // time. + " visit_time >= ? AND " + // Restrict to visits that are older than the specified end time. + " visit_time < ? ")); + statement.BindInt64(0, + begin_time.ToDeltaSinceWindowsEpoch().InMicroseconds()); + statement.BindInt64(1, end_time.ToDeltaSinceWindowsEpoch().InMicroseconds()); + std::vector<DomainVisit> domain_visits; + while (statement.Step()) { + const GURL url(statement.ColumnString(1)); + if (google_util::IsGoogleSearchUrl(url)) { + domain_visits.emplace_back( + url.host(), + base::Time::FromDeltaSinceWindowsEpoch( + base::TimeDelta::FromMicroseconds(statement.ColumnInt64(0)))); + } + } + return domain_visits; +} + bool VisitDatabase::MigrateVisitsWithoutDuration() { if (!GetDB().DoesTableExist("visits")) { NOTREACHED() << " Visits table should exist before migration"; @@ -626,4 +676,48 @@ bool VisitDatabase::MigrateVisitsWithoutDuration() { return true; } +bool VisitDatabase::MigrateVisitsWithoutIncrementedOmniboxTypedScore() { + if (!GetDB().DoesTableExist("visits")) { + NOTREACHED() << " Visits table should exist before migration"; + return false; + } + + if (!GetDB().DoesColumnExist("visits", "incremented_omnibox_typed_score")) { + // Wrap the creation and initialization of the new column in a transaction + // since the value must be computed outside of SQL and iteratively updated. + sql::Transaction committer(&GetDB()); + if (!committer.Begin()) + return false; + + // Old versions don't have the incremented_omnibox_typed_score column, we + // modify the table to add that field. We iterate through the table and + // compute the result for each row. + if (!GetDB().Execute("ALTER TABLE visits " + "ADD COLUMN incremented_omnibox_typed_score BOOLEAN " + "DEFAULT FALSE NOT NULL")) + return false; + + // Iterate through rows in the visits table and update each with the + // appropriate increment_omnibox_typed_score value. Because this column was + // newly added, the existing (default) value is not valid/correct. + sql::Statement read(GetDB().GetUniqueStatement( + "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits")); + while (read.is_valid() && read.Step()) { + VisitRow row; + FillVisitRow(read, &row); + // Check if the visit row is in an invalid state and if it is then + // leave the new field as the default value. + if (row.visit_id == row.referring_visit) + continue; + row.incremented_omnibox_typed_score = + HistoryBackend::IsTypedIncrement(row.transition); + if (!UpdateVisitRow(row)) + return false; + } + if (!read.Succeeded() || !committer.Commit()) + return false; + } + return true; +} + } // namespace history diff --git a/chromium/components/history/core/browser/visit_database.h b/chromium/components/history/core/browser/visit_database.h index ee950544f97..c4ec7ace7b1 100644 --- a/chromium/components/history/core/browser/visit_database.h +++ b/chromium/components/history/core/browser/visit_database.h @@ -188,6 +188,13 @@ class VisitDatabase { void GetVisitsSource(const VisitVector& visits, VisitSourceMap* sources); + // Returns the list of Google domain visits of the user based on the Google + // searches issued in the specified time interval. + // begin_time is inclusive, end_time is exclusive. + std::vector<DomainVisit> GetGoogleDomainVisitsFromSearchesInRange( + base::Time begin_time, + base::Time end_time); + protected: // Returns the database for the functions in this interface. virtual sql::Connection& GetDB() = 0; @@ -216,14 +223,19 @@ class VisitDatabase { // don't have visit_duration column yet. bool MigrateVisitsWithoutDuration(); + // Called by the derived classes to migrate the older visits table which + // don't have incremented_omnibox_typed_score column yet. + bool MigrateVisitsWithoutIncrementedOmniboxTypedScore(); + private: DISALLOW_COPY_AND_ASSIGN(VisitDatabase); }; -// Rows, in order, of the visit table. -#define HISTORY_VISIT_ROW_FIELDS \ - " id,url,visit_time,from_visit,transition,segment_id,visit_duration " +// Columns, in order, of the visit table. +#define HISTORY_VISIT_ROW_FIELDS \ + " id,url,visit_time,from_visit,transition,segment_id,visit_duration," \ + "incremented_omnibox_typed_score " } // namespace history diff --git a/chromium/components/history/core/browser/visit_database_unittest.cc b/chromium/components/history/core/browser/visit_database_unittest.cc index 77be2f6388d..ee7aad30b4e 100644 --- a/chromium/components/history/core/browser/visit_database_unittest.cc +++ b/chromium/components/history/core/browser/visit_database_unittest.cc @@ -14,11 +14,16 @@ #include "components/history/core/browser/url_database.h" #include "components/history/core/browser/visit_database.h" #include "sql/connection.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" using base::Time; using base::TimeDelta; +using testing::AllOf; +using testing::ElementsAre; +using testing::IsEmpty; +using testing::Property; namespace history { @@ -71,19 +76,18 @@ class VisitDatabaseTest : public PlatformTest, TEST_F(VisitDatabaseTest, Add) { // Add one visit. - VisitRow visit_info1(1, Time::Now(), 0, ui::PAGE_TRANSITION_LINK, 0); + VisitRow visit_info1(1, Time::Now(), 0, ui::PAGE_TRANSITION_LINK, 0, false); EXPECT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED)); // Add second visit for the same page. VisitRow visit_info2(visit_info1.url_id, - visit_info1.visit_time + TimeDelta::FromSeconds(1), 1, - ui::PAGE_TRANSITION_TYPED, 0); + visit_info1.visit_time + TimeDelta::FromSeconds(1), 1, + ui::PAGE_TRANSITION_TYPED, 0, true); EXPECT_TRUE(AddVisit(&visit_info2, SOURCE_BROWSED)); // Add third visit for a different page. - VisitRow visit_info3(2, - visit_info1.visit_time + TimeDelta::FromSeconds(2), 0, - ui::PAGE_TRANSITION_LINK, 0); + VisitRow visit_info3(2, visit_info1.visit_time + TimeDelta::FromSeconds(2), 0, + ui::PAGE_TRANSITION_LINK, 0, false); EXPECT_TRUE(AddVisit(&visit_info3, SOURCE_BROWSED)); // Query the first two. @@ -102,17 +106,17 @@ TEST_F(VisitDatabaseTest, Delete) { // should link them. static const int kTime1 = 1000; VisitRow visit_info1(1, Time::FromInternalValue(kTime1), 0, - ui::PAGE_TRANSITION_LINK, 0); + ui::PAGE_TRANSITION_LINK, 0, false); EXPECT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED)); static const int kTime2 = kTime1 + 1; - VisitRow visit_info2(1, Time::FromInternalValue(kTime2), - visit_info1.visit_id, ui::PAGE_TRANSITION_LINK, 0); + VisitRow visit_info2(1, Time::FromInternalValue(kTime2), visit_info1.visit_id, + ui::PAGE_TRANSITION_LINK, 0, false); EXPECT_TRUE(AddVisit(&visit_info2, SOURCE_BROWSED)); static const int kTime3 = kTime2 + 1; - VisitRow visit_info3(1, Time::FromInternalValue(kTime3), - visit_info2.visit_id, ui::PAGE_TRANSITION_LINK, 0); + VisitRow visit_info3(1, Time::FromInternalValue(kTime3), visit_info2.visit_id, + ui::PAGE_TRANSITION_LINK, 0, false); EXPECT_TRUE(AddVisit(&visit_info3, SOURCE_BROWSED)); // First make sure all the visits are there. @@ -138,7 +142,8 @@ TEST_F(VisitDatabaseTest, Delete) { TEST_F(VisitDatabaseTest, Update) { // Make something in the database. - VisitRow original(1, Time::Now(), 23, ui::PageTransitionFromInt(0), 19); + VisitRow original(1, Time::Now(), 23, ui::PageTransitionFromInt(0), 19, + false); AddVisit(&original, SOURCE_BROWSED); // Mutate that row. @@ -165,61 +170,58 @@ std::vector<VisitRow> GetTestVisitRows() { base::Time base_time = Time::UnixEpoch().LocalMidnight(); // Add one visit. - VisitRow visit_info1(1, base_time + TimeDelta::FromMinutes(1), 0, - ui::PageTransitionFromInt( - ui::PAGE_TRANSITION_LINK | - ui::PAGE_TRANSITION_CHAIN_START | - ui::PAGE_TRANSITION_CHAIN_END), - 0); + VisitRow visit_info1( + 1, base_time + TimeDelta::FromMinutes(1), 0, + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK | + ui::PAGE_TRANSITION_CHAIN_START | + ui::PAGE_TRANSITION_CHAIN_END), + 0, false); visit_info1.visit_id = 1; // Add second visit for the same page. - VisitRow visit_info2(visit_info1.url_id, - visit_info1.visit_time + TimeDelta::FromSeconds(1), 1, - ui::PageTransitionFromInt( - ui::PAGE_TRANSITION_TYPED | - ui::PAGE_TRANSITION_CHAIN_START | - ui::PAGE_TRANSITION_CHAIN_END), - 0); + VisitRow visit_info2( + visit_info1.url_id, visit_info1.visit_time + TimeDelta::FromSeconds(1), 1, + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED | + ui::PAGE_TRANSITION_CHAIN_START | + ui::PAGE_TRANSITION_CHAIN_END), + 0, true); visit_info2.visit_id = 2; // Add third visit for a different page. - VisitRow visit_info3(2, - visit_info1.visit_time + TimeDelta::FromSeconds(2), 0, - ui::PageTransitionFromInt( - ui::PAGE_TRANSITION_LINK | - ui::PAGE_TRANSITION_CHAIN_START), - 0); + VisitRow visit_info3( + 2, visit_info1.visit_time + TimeDelta::FromSeconds(2), 0, + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK | + ui::PAGE_TRANSITION_CHAIN_START), + 0, false); visit_info3.visit_id = 3; // Add a redirect visit from the last page. - VisitRow visit_info4(3, - visit_info1.visit_time + TimeDelta::FromSeconds(3), visit_info3.visit_id, - ui::PageTransitionFromInt( - ui::PAGE_TRANSITION_SERVER_REDIRECT | - ui::PAGE_TRANSITION_CHAIN_END), - 0); + VisitRow visit_info4( + 3, visit_info1.visit_time + TimeDelta::FromSeconds(3), + visit_info3.visit_id, + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_SERVER_REDIRECT | + ui::PAGE_TRANSITION_CHAIN_END), + 0, false); visit_info4.visit_id = 4; // Add a subframe visit. - VisitRow visit_info5(4, - visit_info1.visit_time + TimeDelta::FromSeconds(4), visit_info4.visit_id, - ui::PageTransitionFromInt( - ui::PAGE_TRANSITION_AUTO_SUBFRAME | - ui::PAGE_TRANSITION_CHAIN_START | - ui::PAGE_TRANSITION_CHAIN_END), - 0); + VisitRow visit_info5( + 4, visit_info1.visit_time + TimeDelta::FromSeconds(4), + visit_info4.visit_id, + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_AUTO_SUBFRAME | + ui::PAGE_TRANSITION_CHAIN_START | + ui::PAGE_TRANSITION_CHAIN_END), + 0, false); visit_info5.visit_id = 5; // Add third visit for the same URL as visit 1 and 2, but exactly a day // later than visit 2. - VisitRow visit_info6(visit_info1.url_id, - visit_info2.visit_time + TimeDelta::FromDays(1), 1, - ui::PageTransitionFromInt( - ui::PAGE_TRANSITION_TYPED | - ui::PAGE_TRANSITION_CHAIN_START | - ui::PAGE_TRANSITION_CHAIN_END), - 0); + VisitRow visit_info6( + visit_info1.url_id, visit_info2.visit_time + TimeDelta::FromDays(1), 1, + ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED | + ui::PAGE_TRANSITION_CHAIN_START | + ui::PAGE_TRANSITION_CHAIN_END), + 0, true); visit_info6.visit_id = 6; std::vector<VisitRow> test_visit_rows; @@ -367,13 +369,13 @@ TEST_F(VisitDatabaseTest, GetAllURLIDsForTransition) { TEST_F(VisitDatabaseTest, VisitSource) { // Add visits. - VisitRow visit_info1(111, Time::Now(), 0, ui::PAGE_TRANSITION_LINK, 0); + VisitRow visit_info1(111, Time::Now(), 0, ui::PAGE_TRANSITION_LINK, 0, false); ASSERT_TRUE(AddVisit(&visit_info1, SOURCE_BROWSED)); - VisitRow visit_info2(112, Time::Now(), 1, ui::PAGE_TRANSITION_TYPED, 0); + VisitRow visit_info2(112, Time::Now(), 1, ui::PAGE_TRANSITION_TYPED, 0, true); ASSERT_TRUE(AddVisit(&visit_info2, SOURCE_SYNCED)); - VisitRow visit_info3(113, Time::Now(), 0, ui::PAGE_TRANSITION_TYPED, 0); + VisitRow visit_info3(113, Time::Now(), 0, ui::PAGE_TRANSITION_TYPED, 0, true); ASSERT_TRUE(AddVisit(&visit_info3, SOURCE_EXTENSION)); // Query each visit. @@ -447,27 +449,27 @@ TEST_F(VisitDatabaseTest, GetHistoryCount) { // Add 5 visits (3 distinct URLs) for the day before yesterday. // Whether the URL was browsed on this machine or synced has no effect. - VisitRow first_day_1(1, now, 0, standard_transition, 0); + VisitRow first_day_1(1, now, 0, standard_transition, 0, true); first_day_1.visit_id = 1; AddVisit(&first_day_1, SOURCE_BROWSED); now += TimeDelta::FromHours(1); - VisitRow first_day_2(2, now, 0, standard_transition, 0); + VisitRow first_day_2(2, now, 0, standard_transition, 0, true); first_day_2.visit_id = 2; AddVisit(&first_day_2, SOURCE_BROWSED); now += TimeDelta::FromHours(1); - VisitRow first_day_3(1, now, 0, standard_transition, 0); + VisitRow first_day_3(1, now, 0, standard_transition, 0, true); first_day_3.visit_id = 3; AddVisit(&first_day_3, SOURCE_SYNCED); now += TimeDelta::FromHours(1); - VisitRow first_day_4(3, now, 0, standard_transition, 0); + VisitRow first_day_4(3, now, 0, standard_transition, 0, true); first_day_4.visit_id = 4; AddVisit(&first_day_4, SOURCE_SYNCED); now += TimeDelta::FromHours(1); - VisitRow first_day_5(2, now, 0, standard_transition, 0); + VisitRow first_day_5(2, now, 0, standard_transition, 0, true); first_day_5.visit_id = 5; AddVisit(&first_day_5, SOURCE_BROWSED); now += TimeDelta::FromHours(1); @@ -476,22 +478,22 @@ TEST_F(VisitDatabaseTest, GetHistoryCount) { // a user-visible navigation. Of the remaining 3, only 2 are unique. now = yesterday; - VisitRow second_day_1(1, now, 0, standard_transition, 0); + VisitRow second_day_1(1, now, 0, standard_transition, 0, true); second_day_1.visit_id = 6; AddVisit(&second_day_1, SOURCE_BROWSED); now += TimeDelta::FromHours(1); - VisitRow second_day_2(1, now, 0, standard_transition, 0); + VisitRow second_day_2(1, now, 0, standard_transition, 0, true); second_day_2.visit_id = 7; AddVisit(&second_day_2, SOURCE_BROWSED); now += TimeDelta::FromHours(1); - VisitRow second_day_3(2, now, 0, ui::PAGE_TRANSITION_AUTO_SUBFRAME, 0); + VisitRow second_day_3(2, now, 0, ui::PAGE_TRANSITION_AUTO_SUBFRAME, 0, false); second_day_3.visit_id = 8; AddVisit(&second_day_3, SOURCE_BROWSED); now += TimeDelta::FromHours(1); - VisitRow second_day_4(3, now, 0, standard_transition, 0); + VisitRow second_day_4(3, now, 0, standard_transition, 0, true); second_day_4.visit_id = 9; AddVisit(&second_day_4, SOURCE_BROWSED); now += TimeDelta::FromHours(1); @@ -568,12 +570,12 @@ TEST_F(VisitDatabaseTest, GetHistoryCount) { // 24 hours later. The count should be 1, not 2, because the day is longer // than 24 hours, and the two visits will be regarded as duplicate. if (!shift_backward.is_null()) { - VisitRow backward_1(1, shift_backward, 0, standard_transition, 0); + VisitRow backward_1(1, shift_backward, 0, standard_transition, 0, true); backward_1.visit_id = 10; AddVisit(&backward_1, SOURCE_BROWSED); - VisitRow backward_2(1, shift_backward + TimeDelta::FromHours(24), - 0, standard_transition, 0); + VisitRow backward_2(1, shift_backward + TimeDelta::FromHours(24), 0, + standard_transition, 0, true); backward_2.visit_id = 11; AddVisit(&backward_2, SOURCE_BROWSED); @@ -588,14 +590,15 @@ TEST_F(VisitDatabaseTest, GetHistoryCount) { // regarded as duplicate in a normal 24 hour day, but in this case the second // visit is already in the next day. if (!shift_forward.is_null()) { - VisitRow forward_1(1, shift_forward, 0, standard_transition, 0); + VisitRow forward_1(1, shift_forward, 0, standard_transition, 0, true); forward_1.visit_id = 12; AddVisit(&forward_1, SOURCE_BROWSED); Time almost_24_hours_later = shift_forward + TimeDelta::FromHours(24) - TimeDelta::FromMicroseconds(1); - VisitRow forward_2(1, almost_24_hours_later, 0, standard_transition, 0); + VisitRow forward_2(1, almost_24_hours_later, 0, standard_transition, 0, + true); forward_2.visit_id = 13; AddVisit(&forward_2, SOURCE_BROWSED); @@ -606,4 +609,89 @@ TEST_F(VisitDatabaseTest, GetHistoryCount) { } } +TEST_F(VisitDatabaseTest, GetGoogleDomainVisitsFromSearchesInRange_NoVisits) { + const auto begin_time = base::Time::Now(); + EXPECT_THAT(GetGoogleDomainVisitsFromSearchesInRange( + begin_time, begin_time + base::TimeDelta::FromDays(1)), + IsEmpty()); +} + +TEST_F(VisitDatabaseTest, + GetGoogleDomainVisitsFromSearchesInRange_TwoVistsInRange) { + const auto begin_time = base::Time::Now(); + // Out of range, one hour before begin time. + VisitRow row{AddURL(URLRow(GURL("https://www.google.fr/search?q=foo"))), + begin_time + base::TimeDelta::FromHours(-1), + 0, + ui::PageTransitionFromInt(0), + 0, + false}; + AddVisit(&row, SOURCE_BROWSED); + // In range, exactly begin time. + row = {AddURL(URLRow(GURL("https://www.google.com/search?q=foo"))), + begin_time, + 0, + ui::PageTransitionFromInt(0), + 0, + false}; + AddVisit(&row, SOURCE_BROWSED); + // In range, 23 hours after begin time. + row = {AddURL(URLRow(GURL("https://www.google.ch/search?q=foo"))), + begin_time + base::TimeDelta::FromHours(23), + 0, + ui::PageTransitionFromInt(0), + 0, + false}; + AddVisit(&row, SOURCE_BROWSED); + // Out of range, exactly a day after begin time. + row = {AddURL(URLRow(GURL("https://www.google.de/search?q=foo"))), + begin_time + base::TimeDelta::FromHours(24), + 0, + ui::PageTransitionFromInt(0), + 0, + false}; + AddVisit(&row, SOURCE_BROWSED); + + EXPECT_THAT( + GetGoogleDomainVisitsFromSearchesInRange( + begin_time, begin_time + base::TimeDelta::FromDays(1)), + ElementsAre( + AllOf(Property(&DomainVisit::domain, "www.google.com"), + Property(&DomainVisit::visit_time, begin_time)), + AllOf(Property(&DomainVisit::domain, "www.google.ch"), + Property(&DomainVisit::visit_time, + begin_time + base::TimeDelta::FromHours(23))))); +} + +TEST_F(VisitDatabaseTest, GetGoogleDomainVisitsFromSearchesInRange_NotSearch) { + const auto begin_time = base::Time::Now(); + VisitRow row{AddURL(URLRow(GURL("https://www.google.fr/searchin"))), + begin_time, + 0, + ui::PageTransitionFromInt(0), + 0, + false}; + AddVisit(&row, SOURCE_BROWSED); + + EXPECT_THAT(GetGoogleDomainVisitsFromSearchesInRange( + begin_time, begin_time + base::TimeDelta::FromDays(1)), + IsEmpty()); +} + +TEST_F(VisitDatabaseTest, + GetGoogleDomainVisitsFromSearchesInRange_InvalidGoogleDomain) { + const auto begin_time = base::Time::Now(); + VisitRow row{AddURL(URLRow(GURL("https://www.google.foo/search?q=foo"))), + begin_time, + 0, + ui::PageTransitionFromInt(0), + 0, + false}; + AddVisit(&row, SOURCE_BROWSED); + + EXPECT_THAT(GetGoogleDomainVisitsFromSearchesInRange( + begin_time, begin_time + base::TimeDelta::FromDays(1)), + IsEmpty()); +} + } // namespace history diff --git a/chromium/components/history/core/browser/web_history_service.cc b/chromium/components/history/core/browser/web_history_service.cc index 6a6ae16bbd1..325566e56eb 100644 --- a/chromium/components/history/core/browser/web_history_service.cc +++ b/chromium/components/history/core/browser/web_history_service.cc @@ -379,7 +379,7 @@ std::unique_ptr<base::DictionaryValue> WebHistoryService::ReadResponse( if (request->GetResponseCode() == net::HTTP_OK) { std::unique_ptr<base::Value> value = base::JSONReader::Read(request->GetResponseBody()); - if (value.get() && value.get()->is_dict()) + if (value && value->is_dict()) result.reset(static_cast<base::DictionaryValue*>(value.release())); else DLOG(WARNING) << "Non-JSON response received from history server."; |