0

[NTP][RQ] Removes the NTP Most Visited Repeatable Queries code

We are no longer pursuing this feature in its current form. See:
http://shortn/_19XwyxO97k

Keeps ntp_features::kNtpRepeatableQueries feature around for use in a
follow-up experiment to show organic repeatable queries in NTP MV tiles.

Fixed: 1177139, 1170500, 1163629
Change-Id: I3916df9a1296096ecfb7874700ab60275d828ce7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2910964
Commit-Queue: Moe Ahmadi <mahmadi@chromium.org>
Reviewed-by: Mark Pearson <mpearson@chromium.org>
Reviewed-by: Nicolas Ouellet-Payeur <nicolaso@chromium.org>
Reviewed-by: Tibor Goldschwendt <tiborg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#886317}
This commit is contained in:
Moe Ahmadi
2021-05-25 16:32:48 +00:00
committed by Chromium LUCI CQ
parent cacb35c4da
commit bf8038bb47
27 changed files with 20 additions and 1863 deletions

@ -3974,8 +3974,6 @@ static_library("browser") {
"search/promos/promo_service_factory.cc",
"search/promos/promo_service_factory.h",
"search/promos/promo_service_observer.h",
"search/repeatable_queries/repeatable_queries_service_factory.cc",
"search/repeatable_queries/repeatable_queries_service_factory.h",
"search/search_engine_base_url_tracker.cc",
"search/search_engine_base_url_tracker.h",
"search/search_suggest/search_suggest_data.cc",

@ -1512,19 +1512,6 @@ const FeatureEntry::FeatureVariation kNtpDriveModuleVariations[] = {
{"- Managed Users Only", kNtpDriveModuleManagedUsersOnly,
base::size(kNtpDriveModuleManagedUsersOnly), nullptr},
};
const FeatureEntry::FeatureParam kNtpRepeatableQueriesInsertPositionStart[] = {
{ntp_features::kNtpRepeatableQueriesInsertPositionParam, "start"}};
const FeatureEntry::FeatureParam kNtpRepeatableQueriesInsertPositionEnd[] = {
{ntp_features::kNtpRepeatableQueriesInsertPositionParam, "end"}};
const FeatureEntry::FeatureVariation kNtpRepeatableQueriesVariations[] = {
{"- Start", kNtpRepeatableQueriesInsertPositionStart,
base::size(kNtpRepeatableQueriesInsertPositionStart),
"t3317864" /* variation_id */},
{"- End", kNtpRepeatableQueriesInsertPositionEnd,
base::size(kNtpRepeatableQueriesInsertPositionEnd),
"t3317864" /* variation_id */},
};
#endif // !defined(OS_ANDROID)
#if !defined(OS_ANDROID)
@ -4618,9 +4605,7 @@ const FeatureEntry kFeatureEntries[] = {
{"ntp-repeatable-queries", flag_descriptions::kNtpRepeatableQueriesName,
flag_descriptions::kNtpRepeatableQueriesDescription, kOsDesktop,
FEATURE_WITH_PARAMS_VALUE_TYPE(ntp_features::kNtpRepeatableQueries,
kNtpRepeatableQueriesVariations,
"NtpRepeatableQueries")},
FEATURE_VALUE_TYPE(ntp_features::kNtpRepeatableQueries)},
{"ntp-modules", flag_descriptions::kNtpModulesName,
flag_descriptions::kNtpModulesDescription, kOsDesktop,

@ -18,7 +18,6 @@
#include "chrome/browser/ntp_tiles/chrome_custom_links_manager_factory.h"
#include "chrome/browser/ntp_tiles/chrome_popular_sites_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/repeatable_queries/repeatable_queries_service_factory.h"
#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
#include "chrome/common/buildflags.h"
#include "components/history/core/browser/top_sites.h"
@ -120,11 +119,6 @@ ChromeMostVisitedSitesFactory::NewForProfile(Profile* profile) {
auto most_visited_sites = std::make_unique<ntp_tiles::MostVisitedSites>(
profile->GetPrefs(), TopSitesFactory::GetForProfile(profile),
#if defined(OS_ANDROID)
nullptr,
#else
RepeatableQueriesServiceFactory::GetForProfile(profile),
#endif
SuggestionsServiceFactory::GetForProfile(profile),
#if defined(OS_ANDROID)
ChromePopularSitesFactory::NewForProfile(profile),

@ -10,74 +10,6 @@
namespace ntp_features {
TEST(NTPFeaturesTest, LocalHistoryRepeatableQueriesAgeThresholdDays) {
base::test::ScopedFeatureList scoped_feature_list_;
// The default value can be overridden.
scoped_feature_list_.InitWithFeaturesAndParameters(
{{kNtpRepeatableQueries,
{{kNtpRepeatableQueriesAgeThresholdDaysParam, "7"}}}},
{});
base::Time age_threshold = GetLocalHistoryRepeatableQueriesAgeThreshold();
EXPECT_EQ(7, base::TimeDelta(base::Time::Now() - age_threshold).InDays());
// If the age threshold is not parsable to an unsigned integer, the default
// value is used.
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeaturesAndParameters(
{{kNtpRepeatableQueries,
{{kNtpRepeatableQueriesAgeThresholdDaysParam, "j"}}}},
{});
age_threshold = GetLocalHistoryRepeatableQueriesAgeThreshold();
EXPECT_EQ(180, base::TimeDelta(base::Time::Now() - age_threshold).InDays());
}
TEST(NTPFeaturesTest, LocalHistoryRepeatableQueriesRecencyDecayUnit) {
base::test::ScopedFeatureList scoped_feature_list_;
// The default value can be overridden.
scoped_feature_list_.InitWithFeaturesAndParameters(
{{kNtpRepeatableQueries,
{{kNtpRepeatableQueriesRecencyHalfLifeSecondsParam,
"86400" /* One day */}}}},
{});
int recency_decay = GetLocalHistoryRepeatableQueriesRecencyHalfLifeSeconds();
EXPECT_EQ(86400, recency_decay);
// If the recency decay unit is not parsable to an unsigned integer, the
// default value is used.
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeaturesAndParameters(
{{kNtpRepeatableQueries,
{{kNtpRepeatableQueriesRecencyHalfLifeSecondsParam, "j"}}}},
{});
recency_decay = GetLocalHistoryRepeatableQueriesRecencyHalfLifeSeconds();
EXPECT_EQ(604800 /* One week */, recency_decay);
}
TEST(NTPFeaturesTest, LocalHistoryRepeatableQueriesFrequencyExponent) {
base::test::ScopedFeatureList scoped_feature_list_;
// The default value can be overridden.
scoped_feature_list_.InitWithFeaturesAndParameters(
{{kNtpRepeatableQueries,
{{kNtpRepeatableQueriesFrequencyExponentParam, "1.5"}}}},
{});
double frequency_exponent =
GetLocalHistoryRepeatableQueriesFrequencyExponent();
EXPECT_EQ(1.5, frequency_exponent);
// If the recency decay unit is not parsable to an unsigned integer, the
// default value is used.
scoped_feature_list_.Reset();
scoped_feature_list_.InitWithFeaturesAndParameters(
{{kNtpRepeatableQueries,
{{kNtpRepeatableQueriesFrequencyExponentParam, "j"}}}},
{});
frequency_exponent = GetLocalHistoryRepeatableQueriesFrequencyExponent();
EXPECT_EQ(2, frequency_exponent);
}
TEST(NTPFeaturesTest, ModulesLoadTimeout) {
base::test::ScopedFeatureList scoped_feature_list_;

@ -1,62 +0,0 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/search/repeatable_queries/repeatable_queries_service_factory.h"
#include "base/feature_list.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/common/webui_url_constants.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/search/ntp_features.h"
#include "components/search/repeatable_queries/repeatable_queries_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
// static
RepeatableQueriesService* RepeatableQueriesServiceFactory::GetForProfile(
Profile* profile) {
return static_cast<RepeatableQueriesService*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
}
// static
RepeatableQueriesServiceFactory*
RepeatableQueriesServiceFactory::GetInstance() {
return base::Singleton<RepeatableQueriesServiceFactory>::get();
}
RepeatableQueriesServiceFactory::RepeatableQueriesServiceFactory()
: BrowserContextKeyedServiceFactory(
"RepeatableQueriesService",
BrowserContextDependencyManager::GetInstance()) {
DependsOn(CookieSettingsFactory::GetInstance());
DependsOn(IdentityManagerFactory::GetInstance());
}
RepeatableQueriesServiceFactory::~RepeatableQueriesServiceFactory() = default;
KeyedService* RepeatableQueriesServiceFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
if (!base::FeatureList::IsEnabled(ntp_features::kNtpRepeatableQueries))
return nullptr;
Profile* profile = Profile::FromBrowserContext(context);
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
history::HistoryService* history_service =
HistoryServiceFactory::GetForProfile(profile,
ServiceAccessType::EXPLICIT_ACCESS);
TemplateURLService* template_url_service =
TemplateURLServiceFactory::GetForProfile(profile);
auto url_loader_factory = context->GetDefaultStoragePartition()
->GetURLLoaderFactoryForBrowserProcess();
return new RepeatableQueriesService(identity_manager, history_service,
template_url_service, url_loader_factory,
GURL(chrome::kChromeUINewTabURL));
}

@ -1,36 +0,0 @@
// Copyright 2020 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 CHROME_BROWSER_SEARCH_REPEATABLE_QUERIES_REPEATABLE_QUERIES_SERVICE_FACTORY_H_
#define CHROME_BROWSER_SEARCH_REPEATABLE_QUERIES_REPEATABLE_QUERIES_SERVICE_FACTORY_H_
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
class RepeatableQueriesService;
class Profile;
class RepeatableQueriesServiceFactory
: public BrowserContextKeyedServiceFactory {
public:
// Returns the RepeatableQueriesService for |profile|.
static RepeatableQueriesService* GetForProfile(Profile* profile);
static RepeatableQueriesServiceFactory* GetInstance();
private:
friend struct base::DefaultSingletonTraits<RepeatableQueriesServiceFactory>;
RepeatableQueriesServiceFactory();
~RepeatableQueriesServiceFactory() override;
// Overridden from BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* profile) const override;
DISALLOW_COPY_AND_ASSIGN(RepeatableQueriesServiceFactory);
};
#endif // CHROME_BROWSER_SEARCH_REPEATABLE_QUERIES_REPEATABLE_QUERIES_SERVICE_FACTORY_H_

@ -92,7 +92,6 @@ bool ChromeNTPTilesInternalsMessageHandlerClient::DoesSourceExist(
return false;
#endif
case ntp_tiles::TileSource::CUSTOM_LINKS:
case ntp_tiles::TileSource::REPEATABLE_QUERIES_SERVICE:
#if defined(OS_ANDROID)
return false;
#else

@ -29,7 +29,6 @@ const char kHistogramAllowlistName[] = "allowlist";
const char kHistogramHomepageName[] = "homepage";
const char kHistogramCustomLinksName[] = "custom_links";
const char kHistogramExploreName[] = "explore";
const char kHistogramRepeatableQueryName[] = "repeatable_query";
// Suffixes for the various icon types.
const char kTileTypeSuffixIconColor[] = "IconsColor";
@ -60,8 +59,6 @@ std::string GetSourceHistogramName(TileSource source) {
return kHistogramCustomLinksName;
case TileSource::EXPLORE:
return kHistogramExploreName;
case TileSource::REPEATABLE_QUERIES_SERVICE:
return kHistogramRepeatableQueryName;
}
NOTREACHED();
return std::string();

@ -120,7 +120,6 @@ std::u16string GenerateShortTitle(const std::u16string& title) {
MostVisitedSites::MostVisitedSites(
PrefService* prefs,
scoped_refptr<history::TopSites> top_sites,
RepeatableQueriesService* repeatable_queries,
SuggestionsService* suggestions,
std::unique_ptr<PopularSites> popular_sites,
std::unique_ptr<CustomLinksManager> custom_links,
@ -128,7 +127,6 @@ MostVisitedSites::MostVisitedSites(
std::unique_ptr<MostVisitedSitesSupervisor> supervisor)
: prefs_(prefs),
top_sites_(top_sites),
repeatable_queries_(repeatable_queries),
suggestions_service_(suggestions),
popular_sites_(std::move(popular_sites)),
custom_links_(std::move(custom_links)),
@ -181,8 +179,6 @@ bool MostVisitedSites::DoesSourceExist(TileSource source) const {
return custom_links_ != nullptr;
case TileSource::EXPLORE:
return explore_sites_client_ != nullptr;
case TileSource::REPEATABLE_QUERIES_SERVICE:
return false;
}
NOTREACHED();
return false;
@ -220,10 +216,6 @@ void MostVisitedSites::SetMostVisitedURLsObserver(Observer* observer,
top_sites_observation_.Observe(top_sites_.get());
}
if (repeatable_queries_) {
repeatable_queries_observation_.Observe(repeatable_queries_);
}
if (custom_links_) {
custom_links_subscription_ =
custom_links_->RegisterCallbackForOnChanged(base::BindRepeating(
@ -250,10 +242,6 @@ void MostVisitedSites::Refresh() {
top_sites_->SyncWithHistory();
}
if (repeatable_queries_) {
repeatable_queries_->Refresh();
}
suggestions_service_->FetchSuggestionsData();
}
@ -403,12 +391,6 @@ void MostVisitedSites::AddOrRemoveBlockedUrl(const GURL& url, bool add_url) {
base::UserMetricsAction("Suggestions.Site.RemovalUndone"));
}
if (repeatable_queries_) {
// Restoring repeatable queries is not supported as deletion is permanent.
if (add_url)
repeatable_queries_->DeleteQueryWithDestinationURL(url);
}
if (top_sites_) {
if (add_url)
top_sites_->AddBlockedUrl(url);
@ -501,37 +483,7 @@ void MostVisitedSites::OnMostVisitedURLsAvailable(
}
mv_source_ = TileSource::TOP_SITES;
InitiateNotificationForNewTiles(InsertRepeatableQueryTiles(std::move(tiles)));
}
NTPTilesVector MostVisitedSites::InsertRepeatableQueryTiles(
NTPTilesVector tiles) {
if (!repeatable_queries_)
return tiles;
const std::vector<RepeatableQuery>& repeatable_queries =
repeatable_queries_->repeatable_queries();
// Make room for the repeatable query tiles, if necessary.
int num_overflow_tiles =
tiles.size() + repeatable_queries.size() - GetMaxNumSites();
if (num_overflow_tiles > 0)
tiles.resize(tiles.size() - num_overflow_tiles);
auto insert_position = (ntp_features::GetRepeatableQueriesInsertPosition() ==
ntp_features::RepeatableQueriesInsertPosition::kStart)
? tiles.begin()
: tiles.end();
for (const auto& repeatable_query : repeatable_queries) {
NTPTile tile;
tile.title = repeatable_query.query;
tile.url = repeatable_query.destination_url;
tile.source = TileSource::REPEATABLE_QUERIES_SERVICE;
auto inserted_position = tiles.insert(insert_position, tile);
insert_position = inserted_position + 1;
}
return tiles;
InitiateNotificationForNewTiles(std::move(tiles));
}
void MostVisitedSites::OnSuggestionsProfileChanged(
@ -938,21 +890,6 @@ void MostVisitedSites::TopSitesChanged(TopSites* top_sites,
}
}
void MostVisitedSites::OnRepeatableQueriesUpdated() {
// Repeatable Queries are shown along with the most visited URLs only.
// Simulate a change to the most visited urls. This will result in
// MostVisitedSites::OnMostVisitedURLsAvailable to be called synchronously or
// asynchronously depending on whether the most visited URLs are cached.
if (top_sites_) {
TopSitesChanged(top_sites_.get(), ChangeReason::MOST_VISITED);
}
}
void MostVisitedSites::OnRepeatableQueriesServiceShuttingDown() {
DCHECK(repeatable_queries_observation_.IsObserving());
repeatable_queries_observation_.Reset();
}
bool MostVisitedSites::ShouldAddHomeTile() const {
return GetMaxNumSites() > 0u &&
homepage_client_ && // No platform-specific implementation - no tile.

@ -29,8 +29,6 @@
#include "components/ntp_tiles/popular_sites.h"
#include "components/ntp_tiles/section_type.h"
#include "components/ntp_tiles/tile_source.h"
#include "components/search/repeatable_queries/repeatable_queries_service.h"
#include "components/search/repeatable_queries/repeatable_queries_service_observer.h"
#include "components/suggestions/proto/suggestions.pb.h"
#include "components/suggestions/suggestions_service.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@ -84,8 +82,7 @@ class MostVisitedSitesSupervisor {
// Tracks the list of most visited sites.
class MostVisitedSites : public history::TopSitesObserver,
public MostVisitedSitesSupervisor::Observer,
public RepeatableQueriesServiceObserver {
public MostVisitedSitesSupervisor::Observer {
public:
// The observer to be notified when the list of most visited sites changes.
class Observer {
@ -126,7 +123,6 @@ class MostVisitedSites : public history::TopSitesObserver,
// optional and if null, the associated features will be disabled.
MostVisitedSites(PrefService* prefs,
scoped_refptr<history::TopSites> top_sites,
RepeatableQueriesService* repeatable_queries,
suggestions::SuggestionsService* suggestions,
std::unique_ptr<PopularSites> popular_sites,
std::unique_ptr<CustomLinksManager> custom_links,
@ -266,8 +262,6 @@ class MostVisitedSites : public history::TopSitesObserver,
void OnMostVisitedURLsAvailable(
const history::MostVisitedURLList& visited_list);
NTPTilesVector InsertRepeatableQueryTiles(NTPTilesVector tiles);
// Callback for when an update is reported by the SuggestionsService.
void OnSuggestionsProfileChanged(
const suggestions::SuggestionsProfile& suggestions_profile);
@ -349,13 +343,8 @@ class MostVisitedSites : public history::TopSitesObserver,
void TopSitesChanged(history::TopSites* top_sites,
ChangeReason change_reason) override;
// RepeatableQueriesServiceObserver implementation.
void OnRepeatableQueriesUpdated() override;
void OnRepeatableQueriesServiceShuttingDown() override;
PrefService* prefs_;
scoped_refptr<history::TopSites> top_sites_;
RepeatableQueriesService* repeatable_queries_;
suggestions::SuggestionsService* suggestions_service_;
std::unique_ptr<PopularSites> const popular_sites_;
std::unique_ptr<CustomLinksManager> const custom_links_;
@ -382,10 +371,6 @@ class MostVisitedSites : public history::TopSitesObserver,
base::ScopedObservation<history::TopSites, history::TopSitesObserver>
top_sites_observation_{this};
base::ScopedObservation<RepeatableQueriesService,
RepeatableQueriesServiceObserver>
repeatable_queries_observation_{this};
base::CallbackListSubscription custom_links_subscription_;
// The main source of personal tiles - either TOP_SITES or SUGGESTIONS_SEVICE.

@ -472,9 +472,9 @@ class MostVisitedSitesTest
EXPECT_CALL(*icon_cacher, StartFetchMostLikely(_, _)).Times(AtLeast(0));
most_visited_sites_ = std::make_unique<MostVisitedSites>(
&pref_service_, mock_top_sites_, /*repeatable_queries=*/nullptr,
&mock_suggestions_service_, popular_sites_factory_.New(),
std::move(mock_custom_links), std::move(icon_cacher),
&pref_service_, mock_top_sites_, &mock_suggestions_service_,
popular_sites_factory_.New(), std::move(mock_custom_links),
std::move(icon_cacher),
/*supervisor=*/nullptr);
}

@ -28,10 +28,8 @@ enum class TileSource {
HOMEPAGE,
// Tile comes from explore sites list.
EXPLORE,
// Tile comes from the repeatable queries service, based on search history.
REPEATABLE_QUERIES_SERVICE,
LAST = REPEATABLE_QUERIES_SERVICE
LAST = EXPLORE
};
} // namespace ntp_tiles

@ -152,7 +152,6 @@ class InMemoryURLIndex : public KeyedService,
friend class history::HQPPerfTestOnePopularURL;
friend class InMemoryURLIndexTest;
friend class InMemoryURLIndexCacheTest;
friend class RepeatableQueriesServiceTest;
FRIEND_TEST_ALL_PREFIXES(InMemoryURLIndexTest, ExpireRow);
FRIEND_TEST_ALL_PREFIXES(LimitedInMemoryURLIndexTest, Initialization);

@ -6,9 +6,6 @@ static_library("search") {
sources = [
"ntp_features.cc",
"ntp_features.h",
"repeatable_queries/repeatable_queries_service.cc",
"repeatable_queries/repeatable_queries_service.h",
"repeatable_queries/repeatable_queries_service_observer.h",
"search.cc",
"search.h",
"search_provider_observer.cc",
@ -34,10 +31,7 @@ static_library("search") {
source_set("unit_tests") {
testonly = true
sources = [
"repeatable_queries/repeatable_queries_service_unittest.cc",
"search_unittest.cc",
]
sources = [ "search_unittest.cc" ]
if (is_android) {
sources += [ "search_android_unittest.cc" ]
}

@ -95,15 +95,6 @@ const base::Feature kNtpChromeCartModule{"NtpChromeCartModule",
const base::Feature kNtpDriveModule{"NtpDriveModule",
base::FEATURE_DISABLED_BY_DEFAULT};
const char kNtpRepeatableQueriesAgeThresholdDaysParam[] =
"NtpRepeatableQueriesAgeThresholdDays";
const char kNtpRepeatableQueriesRecencyHalfLifeSecondsParam[] =
"NtpRepeatableQueriesRecencyHalfLifeSeconds";
const char kNtpRepeatableQueriesFrequencyExponentParam[] =
"NtpRepeatableQueriesFrequencyExponent";
const char kNtpRepeatableQueriesInsertPositionParam[] =
"NtpRepeatableQueriesInsertPosition";
const char kNtpModulesLoadTimeoutMillisecondsParam[] =
"NtpModulesLoadTimeoutMillisecondsParam";
const char kNtpStatefulTasksModuleDataParam[] =
@ -117,60 +108,6 @@ const char kNtpDriveModuleDataParam[] = "NtpDriveModuleDataParam";
const char kNtpDriveModuleManagedUsersOnlyParam[] =
"NtpDriveModuleManagedUsersOnlyParam";
base::Time GetLocalHistoryRepeatableQueriesAgeThreshold() {
const base::TimeDelta kLocalHistoryRepeatableQueriesAgeThreshold =
base::TimeDelta::FromDays(180); // Six months.
std::string param_value = base::GetFieldTrialParamValueByFeature(
kNtpRepeatableQueries, kNtpRepeatableQueriesAgeThresholdDaysParam);
// If the field trial param is not found or cannot be parsed to an unsigned
// integer, return the default value.
unsigned int param_value_as_int = 0;
if (!base::StringToUint(param_value, &param_value_as_int)) {
return base::Time::Now() - kLocalHistoryRepeatableQueriesAgeThreshold;
}
return (base::Time::Now() - base::TimeDelta::FromDays(param_value_as_int));
}
int GetLocalHistoryRepeatableQueriesRecencyHalfLifeSeconds() {
const base::TimeDelta kLocalHistoryRepeatableQueriesRecencyHalfLife =
base::TimeDelta::FromDays(7); // One week.
std::string param_value = base::GetFieldTrialParamValueByFeature(
kNtpRepeatableQueries, kNtpRepeatableQueriesRecencyHalfLifeSecondsParam);
// If the field trial param is not found or cannot be parsed to an unsigned
// integer, return the default value.
unsigned int param_value_as_int = 0;
if (!base::StringToUint(param_value, &param_value_as_int)) {
return kLocalHistoryRepeatableQueriesRecencyHalfLife.InSeconds();
}
return param_value_as_int;
}
double GetLocalHistoryRepeatableQueriesFrequencyExponent() {
const double kLocalHistoryRepeatableQueriesFrequencyExponent = 2.0;
std::string param_value = base::GetFieldTrialParamValueByFeature(
kNtpRepeatableQueries, kNtpRepeatableQueriesFrequencyExponentParam);
// If the field trial param is not found or cannot be parsed to an unsigned
// integer, return the default value.
double param_value_as_double = 0;
if (!base::StringToDouble(param_value, &param_value_as_double)) {
return kLocalHistoryRepeatableQueriesFrequencyExponent;
}
return param_value_as_double;
}
RepeatableQueriesInsertPosition GetRepeatableQueriesInsertPosition() {
std::string param_value = base::GetFieldTrialParamValueByFeature(
kNtpRepeatableQueries, kNtpRepeatableQueriesInsertPositionParam);
return param_value == "end" ? RepeatableQueriesInsertPosition::kEnd
: RepeatableQueriesInsertPosition::kStart;
}
base::TimeDelta GetModulesLoadTimeout() {
std::string param_value = base::GetFieldTrialParamValueByFeature(
kModules, kNtpModulesLoadTimeoutMillisecondsParam);

@ -8,7 +8,7 @@
#include "base/feature_list.h"
namespace base {
class Time;
class TimeDelta;
} // namespace base
namespace ntp_features {
@ -38,28 +38,6 @@ extern const base::Feature kDisableSearchSuggestChips;
extern const base::Feature kNtpHandleMostVisitedNavigationExplicitly;
// Parameter name determining the age threshold in days for local history
// repeatable queries.
// The value of this parameter should be parsable as an unsigned integer.
extern const char kNtpRepeatableQueriesAgeThresholdDaysParam[];
// Parameter name determining the number of seconds until the recency component
// of the frecency score for local history repeatable queries decays to half.
// The value of this parameter should be parsable as an unsigned integer.
extern const char kNtpRepeatableQueriesRecencyHalfLifeSecondsParam[];
// Parameter name determining the factor by which the frequency component of the
// frecency score for local history repeatable queries is exponentiated.
// The value of this parameter should be parsable as a double.
extern const char kNtpRepeatableQueriesFrequencyExponentParam[];
// Parameter name determining the position, with respect to the MV tiles, in
// which the repeatable queries should be inserted.
extern const char kNtpRepeatableQueriesInsertPositionParam[];
// The available positions, with respect to the MV tiles, in which the
// repeatable queries can be inserted.
enum class RepeatableQueriesInsertPosition {
kStart = 0, // At the start of MV tiles.
kEnd, // At the end of MV tiles.
};
// Parameter determining the module load timeout.
extern const char kNtpModulesLoadTimeoutMillisecondsParam[];
// Parameter determining the type of stateful data to request.
@ -75,18 +53,6 @@ extern const char kNtpDriveModuleDataParam[];
// Parameter for enabling the Drive module for managed users only.
extern const char kNtpDriveModuleManagedUsersOnlyParam[];
// Returns the age threshold for local history repeatable queries.
base::Time GetLocalHistoryRepeatableQueriesAgeThreshold();
// Returns the number of seconds until the recency component of the frecency
// score for local history repeatable queries decays to half.
int GetLocalHistoryRepeatableQueriesRecencyHalfLifeSeconds();
// Returns the factor by which the frequency component of the frecency score for
// local history repeatable queries is exponentiated.
double GetLocalHistoryRepeatableQueriesFrequencyExponent();
// Returns the position, with respect to the MV tiles, in which the repeatable
// queries should be inserted.
RepeatableQueriesInsertPosition GetRepeatableQueriesInsertPosition();
// Returns the timeout after which the load of a module should be aborted.
base::TimeDelta GetModulesLoadTimeout();
} // namespace ntp_features

@ -1,529 +0,0 @@
// Copyright 2020 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/search/repeatable_queries/repeatable_queries_service.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/json/json_writer.h"
#include "base/metrics/histogram_functions.h"
#include "base/scoped_observation.h"
#include "base/stl_util.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/keyword_search_term.h"
#include "components/history/core/browser/url_database.h"
#include "components/search/ntp_features.h"
#include "components/search/search_provider_observer.h"
#include "components/search_engines/template_url_service.h"
#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/variations/net/variations_http_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
namespace {
const char kXSSIResponsePreamble[] = ")]}'";
const size_t kMaxQueries = 2;
bool JsonToRepeatableQueriesData(const base::Value& root_value,
std::vector<RepeatableQuery>* data) {
// 1st element is the query. 2nd element is the list of results.
std::u16string query;
const base::ListValue* root_list = nullptr;
const base::ListValue* results_list = nullptr;
if (!root_value.GetAsList(&root_list) || !root_list->GetString(0, &query) ||
!query.empty() || !root_list->GetList(1, &results_list))
return false;
// Ignore the 3rd and 4th elements. 5th element is the key-value pairs from
// the Suggest server containing the deletion URLs.
const base::DictionaryValue* extras = nullptr;
const base::ListValue* suggestion_details = nullptr;
if (!root_list->GetDictionary(4, &extras) ||
!extras->GetList("google:suggestdetail", &suggestion_details) ||
suggestion_details->GetSize() != results_list->GetSize()) {
return false;
}
std::u16string suggestion;
for (size_t index = 0; results_list->GetString(index, &suggestion); ++index) {
RepeatableQuery result;
result.query = base::CollapseWhitespace(suggestion, false);
if (result.query.empty())
continue;
const base::DictionaryValue* suggestion_detail = nullptr;
if (suggestion_details->GetDictionary(index, &suggestion_detail)) {
suggestion_detail->GetString("du", &result.deletion_url);
}
data->push_back(result);
}
return !data->empty();
}
} // namespace
// static
const char RepeatableQueriesService::kExtractedCountHistogram[] =
"NewTabPage.RepeatableQueries.ExtractedCount";
const char RepeatableQueriesService::kExtractionDurationHistogram[] =
"NewTabPage.RepeatableQueries.ExtractionDuration";
class RepeatableQueriesService::SigninObserver
: public signin::IdentityManager::Observer {
public:
SigninObserver(signin::IdentityManager* identity_manager,
base::RepeatingClosure callback)
: identity_manager_(identity_manager), callback_(std::move(callback)) {
if (identity_manager_) {
identity_manager_observation_.Observe(identity_manager_);
}
}
~SigninObserver() override = default;
bool IsSignedIn() {
return identity_manager_ ? !identity_manager_->GetAccountsInCookieJar()
.signed_in_accounts.empty()
: false;
}
private:
// IdentityManager::Observer implementation.
void OnAccountsInCookieUpdated(
const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
const GoogleServiceAuthError& error) override {
callback_.Run();
}
base::ScopedObservation<signin::IdentityManager,
signin::IdentityManager::Observer>
identity_manager_observation_{this};
// May be nullptr in tests.
signin::IdentityManager* const identity_manager_;
base::RepeatingClosure callback_;
};
RepeatableQueriesService::RepeatableQueriesService(
signin::IdentityManager* identity_manager,
history::HistoryService* history_service,
TemplateURLService* template_url_service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const GURL& request_initiator_url)
: history_service_(history_service),
template_url_service_(template_url_service),
url_loader_factory_(url_loader_factory),
request_initiator_url_(request_initiator_url),
signin_observer_(std::make_unique<SigninObserver>(
identity_manager,
base::BindRepeating(&RepeatableQueriesService::SigninStatusChanged,
base::Unretained(this)))),
search_provider_observer_(std::make_unique<SearchProviderObserver>(
template_url_service,
base::BindRepeating(&RepeatableQueriesService::SearchProviderChanged,
base::Unretained(this)))),
deletion_task_runner_(base::ThreadPool::CreateSequencedTaskRunner({})) {
DCHECK(history_service_);
DCHECK(template_url_service_);
DCHECK(url_loader_factory_);
}
RepeatableQueriesService::~RepeatableQueriesService() = default;
void RepeatableQueriesService::Shutdown() {
for (auto& observer : observers_) {
observer.OnRepeatableQueriesServiceShuttingDown();
}
}
const std::vector<RepeatableQuery>&
RepeatableQueriesService::repeatable_queries() const {
return repeatable_queries_;
}
void RepeatableQueriesService::Refresh() {
if (!search_provider_observer()->is_google()) {
NotifyObservers();
return;
}
if (signin_observer()->IsSignedIn()) {
GetRepeatableQueriesFromServer();
} else {
GetRepeatableQueriesFromURLDatabase();
}
}
void RepeatableQueriesService::DeleteQueryWithDestinationURL(const GURL& url) {
auto it = std::find_if(repeatable_queries_.begin(), repeatable_queries_.end(),
[&url](const auto& repeatable_query) {
return repeatable_query.destination_url == url;
});
// Return if no repeatable query with a matching destination URL exists.
if (it == repeatable_queries_.end()) {
// Still notify observers of the deletion attempt.
NotifyObservers();
return;
}
if (it->deletion_url.empty()) {
DeleteRepeatableQueryFromURLDatabase(it->query);
} else {
DeleteRepeatableQueryFromServer(it->deletion_url);
}
// Delete all the Google search URLs for the given query from history.
const TemplateURL* default_provider =
template_url_service_->GetDefaultSearchProvider();
if (default_provider) {
history_service_->DeleteMatchingURLsForKeyword(default_provider->id(),
it->query);
}
// Make sure the query is not suggested again.
MarkQueryAsDeleted(it->query);
// Update the repeatable queries and notify the observers.
repeatable_queries_.erase(it);
NotifyObservers();
}
void RepeatableQueriesService::AddObserver(
RepeatableQueriesServiceObserver* observer) {
observers_.AddObserver(observer);
}
void RepeatableQueriesService::RemoveObserver(
RepeatableQueriesServiceObserver* observer) {
observers_.RemoveObserver(observer);
}
RepeatableQueriesService::SigninObserver*
RepeatableQueriesService::signin_observer() {
return signin_observer_.get();
}
SearchProviderObserver* RepeatableQueriesService::search_provider_observer() {
return search_provider_observer_.get();
}
void RepeatableQueriesService::SearchProviderChanged() {
// If we have cached data, clear it.
repeatable_queries_.clear();
Refresh();
}
void RepeatableQueriesService::SigninStatusChanged() {
// If we have cached data, clear it.
repeatable_queries_.clear();
Refresh();
}
GURL RepeatableQueriesService::GetQueryDestinationURL(
const std::u16string& query,
const TemplateURL* search_provider) {
DCHECK(search_provider);
TemplateURLRef::SearchTermsArgs search_terms_args(query);
const TemplateURLRef& search_url_ref = search_provider->url_ref();
const SearchTermsData& search_terms_data =
template_url_service_->search_terms_data();
DCHECK(search_url_ref.SupportsReplacement(search_terms_data));
return GURL(
search_url_ref.ReplaceSearchTerms(search_terms_args, search_terms_data));
}
GURL RepeatableQueriesService::GetQueryDeletionURL(
const std::string& deletion_url) {
const auto* default_provider =
template_url_service_->GetDefaultSearchProvider();
if (!default_provider)
return GURL();
const SearchTermsData& search_terms_data =
template_url_service_->search_terms_data();
GURL request_url = default_provider->GenerateSearchURL(search_terms_data);
return request_url.GetOrigin().Resolve(deletion_url);
}
GURL RepeatableQueriesService::GetRequestURL() {
TemplateURLRef::SearchTermsArgs search_terms_args;
search_terms_args.request_source = TemplateURLRef::NON_SEARCHBOX_NTP;
const TemplateURLRef& suggestion_url_ref =
template_url_service_->GetDefaultSearchProvider()->suggestions_url_ref();
const SearchTermsData& search_terms_data =
template_url_service_->search_terms_data();
DCHECK(suggestion_url_ref.SupportsReplacement(search_terms_data));
return GURL(suggestion_url_ref.ReplaceSearchTerms(search_terms_args,
search_terms_data));
}
void RepeatableQueriesService::FlushForTesting(base::OnceClosure flushed) {
deletion_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(),
std::move(flushed));
}
void RepeatableQueriesService::GetRepeatableQueriesFromServer() {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("repeatable_queries_service", R"(
semantics {
sender: "Repeatable Queries Service"
description:
"Downloads search queries to be shown on the Most Visited "
"section of New Tab Page to signed-in users based on their "
"previous search history."
trigger:
"Displaying the new tab page, if Google is the "
"configured search provider, and the user is signed in."
data: "Google credentials if user is signed in."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting:
"Users can control this feature by selecting a non-Google default "
"search engine in Chrome settings under 'Search Engine', or by "
"signing out of the browser on the New Tab Page. Users can opt "
"out of this feature by switching to custom shortcuts."
chrome_policy {
DefaultSearchProviderEnabled {
policy_options {mode: MANDATORY}
DefaultSearchProviderEnabled: false
}
BrowserSignin {
policy_options {mode: MANDATORY}
BrowserSignin: 0
}
}
})");
auto resource_request = std::make_unique<network::ResourceRequest>();
const GURL& request_url = GetRequestURL();
variations::AppendVariationsHeaderUnknownSignedIn(
request_url, variations::InIncognito::kNo, resource_request.get());
resource_request->url = request_url;
resource_request->request_initiator =
url::Origin::Create(request_initiator_url_);
loaders_.push_back(network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation));
loaders_.back()->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&RepeatableQueriesService::RepeatableQueriesResponseLoaded,
weak_ptr_factory_.GetWeakPtr(), loaders_.back().get()),
network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
}
void RepeatableQueriesService::RepeatableQueriesResponseLoaded(
network::SimpleURLLoader* loader,
std::unique_ptr<std::string> response) {
auto net_error = loader->NetError();
base::EraseIf(loaders_, [loader](const auto& loader_ptr) {
return loader == loader_ptr.get();
});
if (net_error != net::OK || !response) {
// In the case of network errors, keep the cached data, if any, but still
// notify observers of the finished load attempt.
NotifyObservers();
return;
}
if (base::StartsWith(*response, kXSSIResponsePreamble,
base::CompareCase::SENSITIVE)) {
*response = response->substr(strlen(kXSSIResponsePreamble));
}
data_decoder::DataDecoder::ParseJsonIsolated(
*response,
base::BindOnce(&RepeatableQueriesService::RepeatableQueriesParsed,
weak_ptr_factory_.GetWeakPtr()));
}
void RepeatableQueriesService::RepeatableQueriesParsed(
data_decoder::DataDecoder::ValueOrError result) {
const TemplateURL* default_provider =
template_url_service_->GetDefaultSearchProvider();
if (!default_provider)
return;
repeatable_queries_.clear();
std::vector<RepeatableQuery> queries;
if (result.value && JsonToRepeatableQueriesData(*result.value, &queries)) {
for (auto& query : queries) {
if (IsQueryDeleted(query.query))
continue;
query.destination_url =
GetQueryDestinationURL(query.query, default_provider);
repeatable_queries_.push_back(query);
if (repeatable_queries_.size() >= kMaxQueries)
break;
}
}
NotifyObservers();
}
void RepeatableQueriesService::GetRepeatableQueriesFromURLDatabase() {
const TemplateURL* default_provider =
template_url_service_->GetDefaultSearchProvider();
if (!default_provider)
return;
repeatable_queries_.clear();
// Fail if the in-memory URLDatabase is not available.
history::URLDatabase* url_db = history_service_->InMemoryDatabase();
if (!url_db)
return;
const base::TimeTicks db_query_time = base::TimeTicks::Now();
auto results = url_db->GetMostRecentNormalizedKeywordSearchTerms(
template_url_service_->GetDefaultSearchProvider()->id(),
ntp_features::GetLocalHistoryRepeatableQueriesAgeThreshold());
const base::Time now = base::Time::Now();
const int kRecencyDecayUnitSec =
ntp_features::GetLocalHistoryRepeatableQueriesRecencyHalfLifeSeconds();
const double kFrequencyExponent =
ntp_features::GetLocalHistoryRepeatableQueriesFrequencyExponent();
auto CompareByFrecency = [&](const auto& a, const auto& b) {
return a.GetFrecency(now, kRecencyDecayUnitSec, kFrequencyExponent) >
b.GetFrecency(now, kRecencyDecayUnitSec, kFrequencyExponent);
};
std::sort(results.begin(), results.end(), CompareByFrecency);
for (const auto& result : results) {
RepeatableQuery repeatable_query;
repeatable_query.query = result.normalized_term;
if (IsQueryDeleted(repeatable_query.query))
continue;
repeatable_query.destination_url =
GetQueryDestinationURL(repeatable_query.query, default_provider);
repeatable_queries_.push_back(repeatable_query);
if (repeatable_queries_.size() >= kMaxQueries)
break;
}
base::UmaHistogramTimes(kExtractionDurationHistogram,
base::TimeTicks::Now() - db_query_time);
base::UmaHistogramCounts10000(kExtractedCountHistogram, results.size());
NotifyObservers();
}
void RepeatableQueriesService::DeleteRepeatableQueryFromServer(
const std::string& deletion_url) {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("repeatable_queries_deletion", R"(
semantics {
sender: "Repeatable Queries Service"
description:
"When users attempt to delete a server-provided repeatable search "
"query from the Most Visited section of New Tab Page, Chrome sends "
"a request to the server requesting deletion of that suggestion."
trigger:
"User attempts to delete a server-provided repeatable search "
"query for which the server provided a custom deletion URL from "
"the Most Visited section of New Tab Page, if Google is the "
"configured search provider, and the user is signed in."
data: "Google credentials if user is signed in."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting:
"Users can control this feature by selecting a non-Google default "
"search engine in Chrome settings under 'Search Engine', or by "
"signing out of the browser on the New Tab Page. Users can opt "
"out of this feature by switching to custom shortcuts."
chrome_policy {
DefaultSearchProviderEnabled {
policy_options {mode: MANDATORY}
DefaultSearchProviderEnabled: false
}
BrowserSignin {
policy_options {mode: MANDATORY}
BrowserSignin: 0
}
}
})");
GURL request_url = GetQueryDeletionURL(deletion_url);
if (!request_url.is_valid())
return;
auto deletion_request = std::make_unique<network::ResourceRequest>();
variations::AppendVariationsHeaderUnknownSignedIn(
request_url, variations::InIncognito::kNo, deletion_request.get());
deletion_request->url = request_url;
deletion_request->request_initiator =
url::Origin::Create(request_initiator_url_);
loaders_.push_back(network::SimpleURLLoader::Create(
std::move(deletion_request), traffic_annotation));
loaders_.back()->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&RepeatableQueriesService::DeletionResponseLoaded,
weak_ptr_factory_.GetWeakPtr(), loaders_.back().get()),
network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
}
void RepeatableQueriesService::DeletionResponseLoaded(
network::SimpleURLLoader* loader,
std::unique_ptr<std::string> response) {
base::EraseIf(loaders_, [loader](const auto& loader_ptr) {
return loader == loader_ptr.get();
});
}
void RepeatableQueriesService::DeleteRepeatableQueryFromURLDatabase(
const std::u16string& query) {
deletion_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&RepeatableQueriesService::DeleteRepeatableQueryFromURLDatabaseTask,
weak_ptr_factory_.GetWeakPtr(), query,
history_service_->InMemoryDatabase()));
}
void RepeatableQueriesService::DeleteRepeatableQueryFromURLDatabaseTask(
const std::u16string& query,
history::URLDatabase* url_db) {
// Fail if the in-memory URLDatabase is not available.
if (!url_db)
return;
// Delete all the search terms matching the repeatable query suggestion from
// the in-memory URLDatabase.
url_db->DeleteKeywordSearchTermForNormalizedTerm(
template_url_service_->GetDefaultSearchProvider()->id(), query);
}
void RepeatableQueriesService::NotifyObservers() {
for (auto& observer : observers_) {
observer.OnRepeatableQueriesUpdated();
}
}
bool RepeatableQueriesService::IsQueryDeleted(const std::u16string& query) {
return base::Contains(deleted_repeatable_queries_, query);
}
void RepeatableQueriesService::MarkQueryAsDeleted(const std::u16string& query) {
deleted_repeatable_queries_.insert(query);
}

@ -1,191 +0,0 @@
// Copyright 2020 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_SEARCH_REPEATABLE_QUERIES_REPEATABLE_QUERIES_SERVICE_H_
#define COMPONENTS_SEARCH_REPEATABLE_QUERIES_REPEATABLE_QUERIES_SERVICE_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/search/repeatable_queries/repeatable_queries_service_observer.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "url/gurl.h"
class SearchProviderObserver;
class TemplateURLService;
class TemplateURL;
namespace base {
class SequencedTaskRunner;
} // namespace base
namespace history {
class HistoryService;
class URLDatabase;
} // namespace history
namespace network {
class SimpleURLLoader;
class SharedURLLoaderFactory;
} // namespace network
namespace signin {
class IdentityManager;
} // namespace signin
// Represents a repeatable query suggestion.
class RepeatableQuery {
public:
RepeatableQuery() = default;
~RepeatableQuery() = default;
bool operator==(const RepeatableQuery& other) const {
return query == other.query && destination_url == other.destination_url &&
deletion_url == other.deletion_url;
}
bool operator!=(const RepeatableQuery& other) const {
return !(this == &other);
}
// Repeatable query suggestion.
std::u16string query;
// The URL to navigate to when the suggestion is selected.
GURL destination_url;
// The relative endpoint used for deleting the query suggestion on the server.
// Populated for server provided queries only.
std::string deletion_url;
};
// Provides repeatable query suggestions to be shown in the NTP Most Visited
// tiles when Google is the default search provider. The repeatable queries are
// requested from the server for signed-in users and extracted from the
// in-memory URLDatabase for unauthenticated users.
class RepeatableQueriesService : public KeyedService {
public:
RepeatableQueriesService(
signin::IdentityManager* identity_manager,
history::HistoryService* history_service,
TemplateURLService* template_url_service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const GURL& request_initiator_url);
~RepeatableQueriesService() override;
RepeatableQueriesService(const RepeatableQueriesService&) = delete;
RepeatableQueriesService& operator=(const RepeatableQueriesService&) = delete;
// Histograms recorded by this class.
static const char kExtractedCountHistogram[];
static const char kExtractionDurationHistogram[];
// KeyedService:
void Shutdown() override;
// Returns the currently cached repeatable query suggestions, if any.
const std::vector<RepeatableQuery>& repeatable_queries() const;
// If Google is the default search provider, asynchronously requests
// repeatable query suggestions from the server for signed-in users and
// synchronously extracts them from the in-memory URLDatabase for
// unauthenticated users. Regardless of success, observers are notified via
// RepeatableQueriesServiceObserver::OnRepeatableQueriesUpdated.
void Refresh();
// Deletes the records of the repeatable query suggestion with the given
// destination URL on the server as well as on the device, whichever is
// applicable. Prevents the suggestion from being offered again by
// blocklisting it. Updates the current set of suggestions and notifies the
// observers.
void DeleteQueryWithDestinationURL(const GURL& url);
// Add/remove observers.
void AddObserver(RepeatableQueriesServiceObserver* observer);
void RemoveObserver(RepeatableQueriesServiceObserver* observer);
protected:
class SigninObserver;
virtual SigninObserver* signin_observer();
virtual SearchProviderObserver* search_provider_observer();
// Called when the default search provider changes.
void SearchProviderChanged();
// Called when the signin status changes.
void SigninStatusChanged();
// Returns the server destination URL for |query| with |search_provider|.
// |search_provider| may not be nullptr.
GURL GetQueryDestinationURL(const std::u16string& query,
const TemplateURL* search_provider);
// Returns the resolved deletion URL for the given relative deletion URL.
GURL GetQueryDeletionURL(const std::string& deletion_url);
// Returns the server request URL.
GURL GetRequestURL();
void FlushForTesting(base::OnceClosure flushed);
private:
// Requests repeatable queries from the server. Called for signed-in users.
void GetRepeatableQueriesFromServer();
void RepeatableQueriesResponseLoaded(network::SimpleURLLoader* loader,
std::unique_ptr<std::string> response);
void RepeatableQueriesParsed(data_decoder::DataDecoder::ValueOrError result);
// Queries the in-memory URLDatabase for the repeatable queries submitted
// to the default search provider. Called for unauthenticated users.
void GetRepeatableQueriesFromURLDatabase();
// Deletes |query| from the in-memory URLDatabase.
void DeleteRepeatableQueryFromURLDatabase(const std::u16string& query);
void DeleteRepeatableQueryFromURLDatabaseTask(const std::u16string& query,
history::URLDatabase* url_db);
// Deletes the query with |deletion_url| from the server.
void DeleteRepeatableQueryFromServer(const std::string& deletion_url);
void DeletionResponseLoaded(network::SimpleURLLoader* loader,
std::unique_ptr<std::string> response);
void NotifyObservers();
bool IsQueryDeleted(const std::u16string& query);
void MarkQueryAsDeleted(const std::u16string& query);
history::HistoryService* history_service_;
TemplateURLService* template_url_service_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
const GURL request_initiator_url_;
std::unique_ptr<SigninObserver> signin_observer_;
std::unique_ptr<SearchProviderObserver> search_provider_observer_;
base::ObserverList<RepeatableQueriesServiceObserver, true> observers_;
std::vector<RepeatableQuery> repeatable_queries_;
// Used to ensure the deleted repeatable queries won't be suggested again.
// This does not need to be persisted across sessions as the queries do get
// deleted on the server as well as on the device, whichever is applicable.
std::set<std::u16string> deleted_repeatable_queries_;
std::vector<std::unique_ptr<network::SimpleURLLoader>> loaders_;
// The TaskRunner to which in-memory URLDatabase deletion tasks are posted.
scoped_refptr<base::SequencedTaskRunner> deletion_task_runner_;
base::WeakPtrFactory<RepeatableQueriesService> weak_ptr_factory_{this};
};
#endif // COMPONENTS_SEARCH_REPEATABLE_QUERIES_REPEATABLE_QUERIES_SERVICE_H_

@ -1,23 +0,0 @@
// Copyright 2020 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_SEARCH_REPEATABLE_QUERIES_REPEATABLE_QUERIES_SERVICE_OBSERVER_H_
#define COMPONENTS_SEARCH_REPEATABLE_QUERIES_REPEATABLE_QUERIES_SERVICE_OBSERVER_H_
// Observer class for the RepeatableQueriesService.
class RepeatableQueriesServiceObserver : public base::CheckedObserver {
public:
// Called after a Refresh() call on the service, either directly or as a
// result of default search provider or signin status change. Note that this
// is called after each Refresh(), even if the network request failed, or if
// it didn't result in an actual change to the cached data. Observers can get
// the repeatable queries via RepeatableQueriesService::repeatable_queries().
virtual void OnRepeatableQueriesUpdated() = 0;
// Called when the service is shutting down allowing the observers to
// unregister themselves and clear references to the service.
virtual void OnRepeatableQueriesServiceShuttingDown() {}
};
#endif // COMPONENTS_SEARCH_REPEATABLE_QUERIES_REPEATABLE_QUERIES_SERVICE_OBSERVER_H_

@ -1,702 +0,0 @@
// Copyright 2020 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/search/repeatable_queries/repeatable_queries_service.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/callback_helpers.h"
#include "base/cancelable_callback.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/test/test_bookmark_client.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/url_database.h"
#include "components/history/core/test/history_service_test_util.h"
#include "components/omnibox/browser/in_memory_url_index.h"
#include "components/omnibox/browser/in_memory_url_index_test_util.h"
#include "components/search/search.h"
#include "components/search/search_provider_observer.h"
#include "components/search_engines/search_engines_test_util.h"
#include "components/search_engines/template_url_service.h"
#include "components/signin/public/base/test_signin_client.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
using base::Time;
using base::TimeDelta;
namespace {
std::string GoodServerResponse() {
return R"()]}'
[
"",
[
"server query 1",
"server query 2",
"server query 3"
],
[],
[],
{
"google:suggestdetail":[
{
"du":"/delete?server+query+1"
},
{
"du":"/delete?server+query+2"
},
{
"du":"/delete?server+query+3"
}
]
}
])";
}
std::string BadServerResponse1() {
return R"()]}'
[
"",
[
"server query 1",
"server query 2",
"server query 3"
],
[],
[],
{
}
])";
}
std::string BadServerResponse2() {
return R"()]}'
[
"",
[
"server query 1",
"server query 2",
"server query 3"
],
[],
[],
{
"google:suggestdetail":[
{
"du":"/delete?server+query+1"
},
{
"du":"/delete?server+query+2"
},
]
}
])";
}
// Used to populate the URLDatabase.
struct TestURLData {
const TemplateURL* search_provider;
std::string search_terms;
int age_in_seconds;
int visit_count = 1;
std::string title = "";
int typed_count = 1;
bool hidden = false;
};
} // namespace
class MockSearchProviderObserver : public SearchProviderObserver {
public:
MockSearchProviderObserver()
: SearchProviderObserver(/*template_url_service=*/nullptr,
base::DoNothing::Repeatedly()) {}
~MockSearchProviderObserver() override = default;
MOCK_METHOD0(is_google, bool());
};
class TestRepeatableQueriesService : public RepeatableQueriesService {
public:
TestRepeatableQueriesService(
signin::IdentityManager* identity_manager,
history::HistoryService* history_service,
TemplateURLService* template_url_service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const GURL& request_initiator_url)
: RepeatableQueriesService(identity_manager,
history_service,
template_url_service,
std::move(url_loader_factory),
request_initiator_url) {}
~TestRepeatableQueriesService() override = default;
MockSearchProviderObserver* search_provider_observer() override {
return &search_provider_observer_;
}
void SearchProviderChanged() {
RepeatableQueriesService::SearchProviderChanged();
}
void SigninStatusChanged() {
RepeatableQueriesService::SigninStatusChanged();
}
void FlushForTesting(base::OnceClosure flushed) {
RepeatableQueriesService::FlushForTesting(std::move(flushed));
}
GURL GetQueryDestinationURL(const std::u16string& query,
const TemplateURL* search_provider) {
return RepeatableQueriesService::GetQueryDestinationURL(query,
search_provider);
}
GURL GetQueryDeletionURL(const std::string& deletion_url) {
return RepeatableQueriesService::GetQueryDeletionURL(deletion_url);
}
GURL GetRequestURL() { return RepeatableQueriesService::GetRequestURL(); }
testing::NiceMock<MockSearchProviderObserver> search_provider_observer_;
};
class RepeatableQueriesServiceTest : public ::testing::Test,
public RepeatableQueriesServiceObserver {
public:
RepeatableQueriesServiceTest() = default;
~RepeatableQueriesServiceTest() override = default;
void SetUp() override {
bookmark_model_ = bookmarks::TestBookmarkClient::CreateModel();
CHECK(history_dir_.CreateUniqueTempDir());
history_service_ = history::CreateHistoryService(
history_dir_.GetPath(), /*create_history_db=*/true);
in_memory_url_index_ = std::make_unique<InMemoryURLIndex>(
bookmark_model_.get(), history_service_.get(), nullptr,
history_dir_.GetPath(), SchemeSet());
in_memory_url_index_->Init();
template_url_service_ = std::make_unique<TemplateURLService>(nullptr, 0);
// Add the fallback default search provider to the TemplateURLService so
// that it gets a valid unique identifier. Make the newly added provider the
// user selected default search provider.
TemplateURL* default_provider = template_url_service_->Add(
std::make_unique<TemplateURL>(default_search_provider()->data()));
template_url_service_->SetUserSelectedDefaultSearchProvider(
default_provider);
// Verify that Google is the default search provider.
EXPECT_TRUE(
search::DefaultSearchProviderIsGoogle(template_url_service_.get()));
identity_env_ = std::make_unique<signin::IdentityTestEnvironment>(
&test_url_loader_factory_);
identity_env_->MakePrimaryAccountAvailable("example@gmail.com");
identity_env_->SetAutomaticIssueOfAccessTokens(true);
service_ = std::make_unique<TestRepeatableQueriesService>(
identity_env_->identity_manager(), history_service_.get(),
template_url_service_.get(),
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_),
GURL());
EXPECT_TRUE(service_->repeatable_queries().empty());
service_->AddObserver(this);
}
void TearDown() override {
// RepeatableQueriesService must be explicitly shut down so that its
// observers can unregister.
service_->Shutdown();
// InMemoryURLIndex must be explicitly shut down or it will DCHECK() in
// its destructor.
in_memory_url_index_->Shutdown();
WaitForRepeatableQueriesService();
WaitForHistoryService();
WaitForInMemoryURLIndex();
}
const TemplateURL* default_search_provider() {
return template_url_service_->GetDefaultSearchProvider();
}
network::TestURLLoaderFactory* test_url_loader_factory() {
return &test_url_loader_factory_;
}
TestRepeatableQueriesService* service() { return service_.get(); }
void set_service_is_done(bool is_done) { service_is_done_ = is_done; }
void SignIn() {
AccountInfo account_info =
identity_env_->MakeAccountAvailable("test@email.com");
identity_env_->SetCookieAccounts({{account_info.email, account_info.gaia}});
}
void SignOut() { identity_env_->SetCookieAccounts({}); }
GURL GetQueryDestinationURL(const std::string& query) {
return service_->GetQueryDestinationURL(base::ASCIIToUTF16(query),
default_search_provider());
}
void RefreshAndMaybeWaitForService() {
service_is_done_ = false;
service_->Refresh();
MaybeWaitForService();
}
void MaybeWaitForService() {
if (!service_is_done_) {
service_run_loop_ = std::make_unique<base::RunLoop>();
// Quits in OnRepeatableQueriesUpdated when the service is done.
service_run_loop_->Run();
}
}
// Fills the URLDatabase with search URLs created using the provided data.
void FillURLDatabase(const std::vector<TestURLData>& url_data_list) {
const Time now = Time::Now();
for (const auto& entry : url_data_list) {
TemplateURLRef::SearchTermsArgs search_terms_args(
base::UTF8ToUTF16(entry.search_terms));
const auto& search_terms_data =
template_url_service_->search_terms_data();
std::string search_url =
entry.search_provider->url_ref().ReplaceSearchTerms(
search_terms_args, search_terms_data);
history_service_->AddPageWithDetails(
GURL(search_url), base::UTF8ToUTF16(entry.title), entry.visit_count,
entry.typed_count, now - TimeDelta::FromSeconds(entry.age_in_seconds),
entry.hidden, history::SOURCE_BROWSED);
history_service_->SetKeywordSearchTermsForURL(
GURL(search_url), entry.search_provider->id(),
base::UTF8ToUTF16(entry.search_terms));
WaitForHistoryService();
}
}
// Waits for RepeatableQueriesService's async operations.
void WaitForRepeatableQueriesService() {
base::RunLoop run_loop;
service_->FlushForTesting(run_loop.QuitClosure());
run_loop.Run();
}
// Waits for history::HistoryService's async operations.
void WaitForHistoryService() {
history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
}
// Waits for InMemoryURLIndex's async operations.
void WaitForInMemoryURLIndex() {
BlockUntilInMemoryURLIndexIsRefreshed(in_memory_url_index_.get());
}
private:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<base::RunLoop> service_run_loop_;
std::unique_ptr<bookmarks::BookmarkModel> bookmark_model_;
std::unique_ptr<history::HistoryService> history_service_;
std::unique_ptr<InMemoryURLIndex> in_memory_url_index_;
base::ScopedTempDir history_dir_;
std::unique_ptr<TemplateURLService> template_url_service_;
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<signin::IdentityTestEnvironment> identity_env_;
std::unique_ptr<TestRepeatableQueriesService> service_;
bool service_is_done_ = false;
// RepeatableQueriesServiceObserver
void OnRepeatableQueriesUpdated() override;
void OnRepeatableQueriesServiceShuttingDown() override;
};
void RepeatableQueriesServiceTest::OnRepeatableQueriesUpdated() {
service_is_done_ = true;
if (service_run_loop_) {
service_run_loop_->Quit();
}
}
void RepeatableQueriesServiceTest::OnRepeatableQueriesServiceShuttingDown() {
service_->RemoveObserver(this);
}
// TODO(crbug.com/1151909) Test fails on iOS
// TODO(crbug.com/1177139) Re-enable test
TEST_F(RepeatableQueriesServiceTest, DISABLED_SignedIn) {
SignIn();
test_url_loader_factory()->AddResponse(service()->GetRequestURL().spec(),
GoodServerResponse());
EXPECT_CALL(*service()->search_provider_observer(), is_google())
.WillOnce(testing::Return(true));
// Request a refresh.
RefreshAndMaybeWaitForService();
// The first two server suggestions are kept as repeatable queries.
std::vector<RepeatableQuery> expected_server_queries{
{u"server query 1", GetQueryDestinationURL("server query 1"),
"/delete?server+query+1"},
{u"server query 2", GetQueryDestinationURL("server query 2"),
"/delete?server+query+2"}};
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
}
// TODO(crbug.com/1151909) Test fails on iOS
// TODO(crbug.com/1170500) Test fails also on other platforms
TEST_F(RepeatableQueriesServiceTest, DISABLED_SignedIn_BadResponse) {
SignIn();
test_url_loader_factory()->AddResponse(service()->GetRequestURL().spec(),
GoodServerResponse());
EXPECT_CALL(*service()->search_provider_observer(), is_google())
.WillRepeatedly(testing::Return(true));
// Request a refresh.
RefreshAndMaybeWaitForService();
std::vector<RepeatableQuery> expected_server_queries{
{u"server query 1", GetQueryDestinationURL("server query 1"),
"/delete?server+query+1"},
{u"server query 2", GetQueryDestinationURL("server query 2"),
"/delete?server+query+2"}};
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
test_url_loader_factory()->AddResponse(service()->GetRequestURL().spec(),
BadServerResponse1());
// Request a refresh.
RefreshAndMaybeWaitForService();
// Cached data is cleared.
EXPECT_TRUE(service()->repeatable_queries().empty());
test_url_loader_factory()->AddResponse(service()->GetRequestURL().spec(),
BadServerResponse2());
// Request a refresh.
RefreshAndMaybeWaitForService();
// Cached data is still empty.
EXPECT_TRUE(service()->repeatable_queries().empty());
}
// TODO(crbug.com/1151909) Test fails on iOS
// TODO(crbug.com/1170500) Test fails also on other platforms
TEST_F(RepeatableQueriesServiceTest, DISABLED_SignedIn_ErrorResponse) {
SignIn();
test_url_loader_factory()->AddResponse(service()->GetRequestURL().spec(),
GoodServerResponse());
EXPECT_CALL(*service()->search_provider_observer(), is_google())
.WillRepeatedly(testing::Return(true));
// Request a refresh.
RefreshAndMaybeWaitForService();
std::vector<RepeatableQuery> expected_server_queries{
{u"server query 1", GetQueryDestinationURL("server query 1"),
"/delete?server+query+1"},
{u"server query 2", GetQueryDestinationURL("server query 2"),
"/delete?server+query+2"}};
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
test_url_loader_factory()->AddResponse(
service()->GetRequestURL(), network::mojom::URLResponseHead::New(),
std::string(), network::URLLoaderCompletionStatus(net::HTTP_NOT_FOUND));
// Request a refresh.
RefreshAndMaybeWaitForService();
// Cached data is kept.
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
}
// TODO(crbug.com/1151909) Test fails on iOS
// TODO(crbug.com/1158533): This test is disabled because it has been failing
// intermittently on all platforms.
TEST_F(RepeatableQueriesServiceTest,
DISABLED_SignedIn_DefaultSearchProviderChanged) {
SignIn();
test_url_loader_factory()->AddResponse(service()->GetRequestURL().spec(),
GoodServerResponse());
EXPECT_CALL(*service()->search_provider_observer(), is_google())
.WillOnce(testing::Return(true))
.WillOnce(testing::Return(false));
// Request a refresh.
RefreshAndMaybeWaitForService();
std::vector<RepeatableQuery> expected_server_queries{
{u"server query 1", GetQueryDestinationURL("server query 1"),
"/delete?server+query+1"},
{u"server query 2", GetQueryDestinationURL("server query 2"),
"/delete?server+query+2"}};
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
set_service_is_done(false);
// Simulate DSP change. Requests a refresh.
service()->SearchProviderChanged();
MaybeWaitForService();
// Cached data is cleared.
EXPECT_TRUE(service()->repeatable_queries().empty());
}
// TODO(crbug.com/1151909) Test fails on iOS
// TODO(crbug.com/1170500) Test fails also on other platforms
TEST_F(RepeatableQueriesServiceTest, DISABLED_SignedIn_SigninStatusChanged) {
base::HistogramTester histogram_tester;
SignIn();
test_url_loader_factory()->AddResponse(service()->GetRequestURL().spec(),
GoodServerResponse());
EXPECT_CALL(*service()->search_provider_observer(), is_google())
.WillRepeatedly(testing::Return(true));
// Request a refresh.
RefreshAndMaybeWaitForService();
std::vector<RepeatableQuery> expected_server_queries{
{u"server query 1", GetQueryDestinationURL("server query 1"),
"/delete?server+query+1"},
{u"server query 2", GetQueryDestinationURL("server query 2"),
"/delete?server+query+2"}};
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
int original_query_age =
history::kAutocompleteDuplicateVisitIntervalThreshold.InSeconds() + 3;
FillURLDatabase({
// Issued far enough from the original query; won't be ignored:
{default_search_provider(), "more recent local query",
/*age_in_seconds=*/0},
// Issued far enough from the original query; won't be ignored:
{default_search_provider(), "less recent local query",
/*age_in_seconds=*/1},
{default_search_provider(), "less recent local query",
/*age_in_seconds=*/original_query_age},
{default_search_provider(), "more recent local query",
/*age_in_seconds=*/original_query_age},
});
set_service_is_done(false);
SignOut(); // Requests a refresh.
MaybeWaitForService();
// Cached data is updated to local results.
std::vector<RepeatableQuery> expected_local_queries{
{u"more recent local query",
GetQueryDestinationURL("more recent local query"), ""},
{u"less recent local query",
GetQueryDestinationURL("less recent local query"), ""}};
EXPECT_EQ(expected_local_queries, service()->repeatable_queries());
histogram_tester.ExpectTotalCount(
RepeatableQueriesService::kExtractionDurationHistogram, 1);
histogram_tester.ExpectTotalCount(
RepeatableQueriesService::kExtractedCountHistogram, 1);
histogram_tester.ExpectUniqueSample(
RepeatableQueriesService::kExtractedCountHistogram, 2, 1);
}
// TODO(crbug.com/1151909) Test fails on iOS
// TODO(crbug.com/1170500) Test fails also on other platforms
TEST_F(RepeatableQueriesServiceTest, DISABLED_SignedIn_Deletion) {
SignIn();
test_url_loader_factory()->AddResponse(service()->GetRequestURL().spec(),
GoodServerResponse());
EXPECT_CALL(*service()->search_provider_observer(), is_google())
.WillRepeatedly(testing::Return(true));
// Request a refresh.
RefreshAndMaybeWaitForService();
std::vector<RepeatableQuery> expected_server_queries{
{u"server query 1", GetQueryDestinationURL("server query 1"),
"/delete?server+query+1"},
{u"server query 2", GetQueryDestinationURL("server query 2"),
"/delete?server+query+2"}};
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
// Try to delete a query suggestion not provided by the service.
set_service_is_done(false);
service()->DeleteQueryWithDestinationURL(GetQueryDestinationURL("blah"));
// No request to delete the suggestion was sent.
EXPECT_TRUE(test_url_loader_factory()->pending_requests()->empty());
MaybeWaitForService();
// Suggestions should not change.
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
// Delete the query suggestion provided by the service.
set_service_is_done(false);
service()->DeleteQueryWithDestinationURL(
GetQueryDestinationURL("server query 1"));
// A request to delete the suggestion was sent.
EXPECT_EQ(1u, test_url_loader_factory()->pending_requests()->size());
EXPECT_EQ(test_url_loader_factory()->GetPendingRequest(0)->request.url,
service()->GetQueryDeletionURL("/delete?server+query+1"));
MaybeWaitForService();
expected_server_queries = {{u"server query 2",
GetQueryDestinationURL("server query 2"),
"/delete?server+query+2"}};
// The deleted suggestion is not offered anymore.
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
expected_server_queries = {
{u"server query 2", GetQueryDestinationURL("server query 2"),
"/delete?server+query+2"},
{u"server query 3", GetQueryDestinationURL("server query 3"),
"/delete?server+query+3"}};
// Request a refresh.
RefreshAndMaybeWaitForService();
// The deleted suggestion will not be offered again.
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
}
// TODO(crbug.com/1151909) Test fails on iOS simulators
// TODO(crbug.com/1170500) Test fails also on other platforms
TEST_F(RepeatableQueriesServiceTest,
DISABLED_SignedOut_DefaultSearchProviderChanged) {
int original_query_age =
history::kAutocompleteDuplicateVisitIntervalThreshold.InSeconds() + 3;
FillURLDatabase({
// Issued far enough from the original query; won't be ignored:
{default_search_provider(), "more recent local query",
/*age_in_seconds=*/0},
// Issued far enough from the original query; won't be ignored:
{default_search_provider(), "less recent local query",
/*age_in_seconds=*/1},
{default_search_provider(), "less recent local query",
/*age_in_seconds=*/original_query_age},
{default_search_provider(), "more recent local query",
/*age_in_seconds=*/original_query_age},
});
EXPECT_CALL(*service()->search_provider_observer(), is_google())
.WillOnce(testing::Return(true))
.WillOnce(testing::Return(false));
// Request a refresh.
RefreshAndMaybeWaitForService();
std::vector<RepeatableQuery> expected_local_queries{
{u"more recent local query",
GetQueryDestinationURL("more recent local query"), ""},
{u"less recent local query",
GetQueryDestinationURL("less recent local query"), ""}};
EXPECT_EQ(expected_local_queries, service()->repeatable_queries());
set_service_is_done(false);
// Simulate DSP change. Requests a refresh.
service()->SearchProviderChanged();
MaybeWaitForService();
// Cached data is cleared.
EXPECT_TRUE(service()->repeatable_queries().empty());
}
// TODO(crbug.com/1151909) Test fails on iOS
// TODO(crbug.com/1170500) Test fails also on other platforms
TEST_F(RepeatableQueriesServiceTest, DISABLED_SignedOut_SigninStatusChanged) {
int original_query_age =
history::kAutocompleteDuplicateVisitIntervalThreshold.InSeconds() + 3;
FillURLDatabase({
// Issued far enough from the original query; won't be ignored:
{default_search_provider(), "more recent local query",
/*age_in_seconds=*/0},
// Issued far enough from the original query; won't be ignored:
{default_search_provider(), "less recent local query",
/*age_in_seconds=*/1},
{default_search_provider(), "less recent local query",
/*age_in_seconds=*/original_query_age},
{default_search_provider(), "more recent local query",
/*age_in_seconds=*/original_query_age},
});
EXPECT_CALL(*service()->search_provider_observer(), is_google())
.WillRepeatedly(testing::Return(true));
// Request a refresh.
RefreshAndMaybeWaitForService();
std::vector<RepeatableQuery> expected_local_queries{
{u"more recent local query",
GetQueryDestinationURL("more recent local query"), ""},
{u"less recent local query",
GetQueryDestinationURL("less recent local query"), ""}};
EXPECT_EQ(expected_local_queries, service()->repeatable_queries());
test_url_loader_factory()->AddResponse(service()->GetRequestURL().spec(),
GoodServerResponse());
set_service_is_done(false);
SignIn(); // Requests a refresh.
MaybeWaitForService();
// Cached data is updated to server results.
std::vector<RepeatableQuery> expected_server_queries{
{u"server query 1", GetQueryDestinationURL("server query 1"),
"/delete?server+query+1"},
{u"server query 2", GetQueryDestinationURL("server query 2"),
"/delete?server+query+2"}};
EXPECT_EQ(expected_server_queries, service()->repeatable_queries());
}
// TODO(crbug.com/1151909) Test fails on iOS
// TODO(crbug.com/1170500) Test fails also on other platforms
TEST_F(RepeatableQueriesServiceTest, DISABLED_SignedOut_Deletion) {
FillURLDatabase({{default_search_provider(), "local query 1",
/*age_in_seconds=*/1},
{default_search_provider(), "local query 2",
/*age_in_seconds=*/2},
{default_search_provider(), "local query 3",
/*age_in_seconds=*/3}});
EXPECT_CALL(*service()->search_provider_observer(), is_google())
.WillRepeatedly(testing::Return(true));
// Request a refresh.
RefreshAndMaybeWaitForService();
std::vector<RepeatableQuery> expected_local_queries{
{u"local query 1", GetQueryDestinationURL("local query 1"), ""},
{u"local query 2", GetQueryDestinationURL("local query 2"), ""}};
EXPECT_EQ(expected_local_queries, service()->repeatable_queries());
// Try to delete a query suggestion not provided by the service.
set_service_is_done(false);
service()->DeleteQueryWithDestinationURL(GetQueryDestinationURL("blah"));
MaybeWaitForService();
// Suggestions should not change.
EXPECT_EQ(expected_local_queries, service()->repeatable_queries());
// Delete the query suggestion provided by the service.
set_service_is_done(false);
service()->DeleteQueryWithDestinationURL(
GetQueryDestinationURL("local query 1"));
MaybeWaitForService();
expected_local_queries = {
{u"local query 2", GetQueryDestinationURL("local query 2"), ""}};
// The deleted suggestion is not offered anymore.
EXPECT_EQ(expected_local_queries, service()->repeatable_queries());
// Make sure there is no pending deletion task.
WaitForRepeatableQueriesService();
// Request a refresh.
RefreshAndMaybeWaitForService();
expected_local_queries = {
{u"local query 2", GetQueryDestinationURL("local query 2"), ""},
{u"local query 3", GetQueryDestinationURL("local query 3"), ""}};
// The deleted suggestion will not be offered again.
EXPECT_EQ(expected_local_queries, service()->repeatable_queries());
}

@ -25,7 +25,6 @@ IOSMostVisitedSitesFactory::NewForBrowserState(
return std::make_unique<ntp_tiles::MostVisitedSites>(
browser_state->GetPrefs(),
ios::TopSitesFactory::GetForBrowserState(browser_state),
/*repeatable_queries=*/nullptr,
suggestions::SuggestionsServiceFactory::GetForBrowserState(browser_state),
IOSPopularSitesFactory::NewForBrowserState(browser_state),
/*custom_links=*/nullptr,

@ -76,7 +76,6 @@ bool IOSNTPTilesInternalsMessageHandlerBridge::DoesSourceExist(
case ntp_tiles::TileSource::CUSTOM_LINKS:
case ntp_tiles::TileSource::ALLOWLIST:
case ntp_tiles::TileSource::EXPLORE:
case ntp_tiles::TileSource::REPEATABLE_QUERIES_SERVICE:
return false;
}
NOTREACHED();

@ -5040,31 +5040,6 @@
]
}
],
"NtpRepeatableQueriesDesktop": [
{
"platforms": [
"chromeos",
"chromeos_lacros",
"linux",
"mac",
"windows"
],
"experiments": [
{
"name": "Enabled",
"params": {
"NtpRepeatableQueriesAgeThresholdDays": "180",
"NtpRepeatableQueriesFrequencyExponent": "2",
"NtpRepeatableQueriesInsertPosition": "start",
"NtpRepeatableQueriesRecencyHalfLifeSeconds": "604800"
},
"enable_features": [
"NtpRepeatableQueries"
]
}
]
}
],
"NtpWebUIDesktop": [
{
"platforms": [

@ -11620,7 +11620,11 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
<suffix name="popular_baked_in" label="Baked-in, popular suggestions."/>
<suffix name="popular_fetched"
label="Non-personalized, popular suggestions (fetched from the server)."/>
<suffix name="repeatable_query" label="Repeatable Query suggestion."/>
<suffix name="repeatable_query" label="Repeatable Query suggestion.">
<obsolete>
Removed in 05 2021.
</obsolete>
</suffix>
<suffix name="search_page" label="Google Search page">
<obsolete>
Used for experiment but was never launched, as of 2019-07.

@ -1325,6 +1325,9 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
<histogram name="NewTabPage.RepeatableQueries.ExtractedCount" units="count"
expires_after="2021-10-25">
<obsolete>
Removed in 05 2021.
</obsolete>
<owner>mahmadi@chromium.org</owner>
<owner>chrome-desktop-ntp@google.com</owner>
<summary>
@ -1337,6 +1340,9 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
<histogram name="NewTabPage.RepeatableQueries.ExtractionDuration" units="ms"
expires_after="2021-10-25">
<obsolete>
Removed in 05 2021.
</obsolete>
<owner>mahmadi@chromium.org</owner>
<owner>chrome-desktop-ntp@google.com</owner>
<summary>

@ -285,8 +285,6 @@ Refer to README.md for content description and update process.
<item id="remoting_telemetry_log_writer" added_in_milestone="86" hash_code="107268760" type="0" content_hash_code="81741595" os_list="linux,windows" file_path="remoting/base/telemetry_log_writer.cc"/>
<item id="render_view_context_menu" added_in_milestone="62" hash_code="25844439" type="0" content_hash_code="69471170" os_list="linux,windows" file_path="chrome/browser/renderer_context_menu/render_view_context_menu.cc"/>
<item id="renderer_initiated_download" added_in_milestone="62" hash_code="116443055" type="0" content_hash_code="37846436" os_list="linux,windows" file_path="content/browser/renderer_host/render_frame_host_impl.cc"/>
<item id="repeatable_queries_deletion" added_in_milestone="88" hash_code="59980744" type="0" content_hash_code="9022058" os_list="linux,windows" file_path="components/search/repeatable_queries/repeatable_queries_service.cc"/>
<item id="repeatable_queries_service" added_in_milestone="88" hash_code="5394442" type="0" content_hash_code="115294794" os_list="linux,windows" file_path="components/search/repeatable_queries/repeatable_queries_service.cc"/>
<item id="reporting" added_in_milestone="62" hash_code="109891200" type="0" content_hash_code="125758928" os_list="linux,windows" file_path="net/reporting/reporting_uploader.cc"/>
<item id="resource_dispatcher_host" added_in_milestone="62" hash_code="81157007" type="0" deprecated="2019-07-30" content_hash_code="35725167" file_path=""/>
<item id="resource_prefetch" added_in_milestone="62" hash_code="110815970" type="0" deprecated="2018-02-28" content_hash_code="39251261" file_path=""/>

@ -409,8 +409,6 @@ hidden="true" so that these annotations don't show up in the document.
<traffic_annotation unique_id="popular_sites_fetch"/>
<traffic_annotation unique_id="search_suggest_service"/>
<traffic_annotation unique_id="remote_suggestions_provider"/>
<traffic_annotation unique_id="repeatable_queries_deletion"/>
<traffic_annotation unique_id="repeatable_queries_service"/>
<traffic_annotation unique_id="promo_service"/>
<traffic_annotation unique_id="task_module_service"/>
</sender>