Commit ddbbee86 authored by Jan Krcal's avatar Jan Krcal Committed by Commit Bot

[Remote suggestions] Set AVAILABLE_LOADING status when refreshing stale.

This CL uses the existing status AVAILABLE_LOADING to signal to the UI
that the currently available suggestions are stale and that new ones are
being loaded. When the fetch is over (or when it fails or when a timeout
fires), the category status is switched back to AVAILABLE.

Bug: 762932
Change-Id: If34d739ab316adec95f8e4e8f4b5319479fdacd5
Reviewed-on: https://chromium-review.googlesource.com/663729
Commit-Queue: Jan Krcal <jkrcal@chromium.org>
Reviewed-by: default avatarvitaliii <vitaliii@chromium.org>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Reviewed-by: default avatarTim Schumann <tschumann@chromium.org>
Cr-Commit-Position: refs/heads/master@{#506795}
parent 96c972ea
......@@ -393,7 +393,8 @@ void RegisterArticleProviderIfEnabled(ContentSuggestionsService* service,
base::MakeUnique<RemoteSuggestionsStatusServiceImpl>(
signin_manager, pref_service, additional_toggle_pref),
std::move(prefetched_pages_tracker),
std::move(breaking_news_raw_data_provider), debug_logger);
std::move(breaking_news_raw_data_provider), debug_logger,
std::make_unique<base::OneShotTimer>());
service->remote_suggestions_scheduler()->SetProvider(provider.get());
service->set_remote_suggestions_provider(provider.get());
......
......@@ -11,7 +11,10 @@ const char kEnableSnippets[] = "ntp_snippets.enable";
const char kRemoteSuggestionCategories[] = "ntp_snippets.remote_categories";
const char kSnippetLastFetchAttempt[] = "ntp_snippets.last_fetch_attempt";
const char kSnippetLastFetchAttemptTime[] = "ntp_snippets.last_fetch_attempt";
const char kSnippetLastSuccessfulFetchTime[] =
"ntp_snippets.last_successful_fetch_time";
// For backwards compatibility, we do not rename the "fetching_" prefs (should
// be "persistent_fetching_").
......
......@@ -17,7 +17,10 @@ extern const char kEnableSnippets[];
extern const char kRemoteSuggestionCategories[];
// The pref name for the last time when a background fetch was attempted.
extern const char kSnippetLastFetchAttempt[];
extern const char kSnippetLastFetchAttemptTime[];
// The pref name for the last time when a background fetch was successfull.
extern const char kSnippetLastSuccessfulFetchTime[];
// Pref names for minimal intervals between two successive background fetches.
//
......
......@@ -32,6 +32,12 @@ class RemoteSuggestionsProvider : public ContentSuggestionsProvider {
// of the fetch (unless nullptr).
virtual void RefetchInTheBackground(FetchStatusCallback callback) = 0;
// Refetches the suggestions in a state ready for display. Similar to
// |RefetchInTheBackground| above, but observers will be notified about the
// ongoing refetch and may be notified with old suggestions if the fetch fails
// or does not finish before timeout.
virtual void RefetchWhileDisplaying(FetchStatusCallback callback) = 0;
virtual const RemoteSuggestionsFetcher* suggestions_fetcher_for_debugging()
const = 0;
......
......@@ -21,6 +21,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/values.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/image_fetcher/core/image_fetcher.h"
......@@ -82,8 +83,7 @@ const int kDefaultMaxAdditionalPrefetchedSuggestions = 5;
const char kMaxAgeForAdditionalPrefetchedSuggestionParamName[] =
"max_age_for_additional_prefetched_suggestion_minutes";
const base::TimeDelta kDefaultMaxAgeForAdditionalPrefetchedSuggestion =
base::TimeDelta::FromHours(36);
const int kDefaultMaxAgeForAdditionalPrefetchedSuggestionMinutes = 36 * 60;
bool IsOrderingNewRemoteCategoriesBasedOnArticlesCategoryEnabled() {
// TODO(vitaliii): Use GetFieldTrialParamByFeature(As.*)? from
......@@ -138,7 +138,7 @@ base::TimeDelta GetMaxAgeForAdditionalPrefetchedSuggestion() {
return base::TimeDelta::FromMinutes(base::GetFieldTrialParamByFeatureAsInt(
kKeepPrefetchedContentSuggestions,
kMaxAgeForAdditionalPrefetchedSuggestionParamName,
kDefaultMaxAgeForAdditionalPrefetchedSuggestion.InMinutes()));
kDefaultMaxAgeForAdditionalPrefetchedSuggestionMinutes));
}
// Whether notifications for fetched suggestions are enabled. Note that this
......@@ -215,6 +215,22 @@ int GetFetchMoreSuggestionsCount() {
kFetchMoreSuggestionsCountDefault);
}
// Variation parameter for the timeout when refetching suggestions while
// displaying. If the fetch takes too long and the timeout is over, the category
// status is forced back to AVAILABLE and the existing (possibly stale)
// suggestions are notified.
const char kTimeoutForRefetchWhileDisplayingSecondsParamName[] =
"timeout_for_refetch_while_displaying_seconds";
const int kDefaultTimeoutForRefetchWhileDisplayingSeconds = 5;
base::TimeDelta GetTimeoutForRefetchWhileDisplaying() {
return base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
ntp_snippets::kArticleSuggestionsFeature,
kTimeoutForRefetchWhileDisplayingSecondsParamName,
kDefaultTimeoutForRefetchWhileDisplayingSeconds));
}
template <typename SuggestionPtrContainer>
std::unique_ptr<std::vector<std::string>> GetSuggestionIDVector(
const SuggestionPtrContainer& suggestions) {
......@@ -352,7 +368,8 @@ RemoteSuggestionsProviderImpl::RemoteSuggestionsProviderImpl(
std::unique_ptr<RemoteSuggestionsStatusService> status_service,
std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker,
std::unique_ptr<BreakingNewsListener> breaking_news_raw_data_provider,
Logger* debug_logger)
Logger* debug_logger,
std::unique_ptr<base::OneShotTimer> fetch_timeout_timer)
: RemoteSuggestionsProvider(observer),
state_(State::NOT_INITED),
pref_service_(pref_service),
......@@ -372,8 +389,10 @@ RemoteSuggestionsProviderImpl::RemoteSuggestionsProviderImpl(
prefetched_pages_tracker_(std::move(prefetched_pages_tracker)),
breaking_news_raw_data_provider_(
std::move(breaking_news_raw_data_provider)),
debug_logger_(debug_logger) {
debug_logger_(debug_logger),
fetch_timeout_timer_(std::move(fetch_timeout_timer)) {
DCHECK(debug_logger_);
DCHECK(fetch_timeout_timer_);
RestoreCategoriesFromPrefs();
// The articles category always exists. Add it if we didn't get it from prefs.
// TODO(treib): Rethink this.
......@@ -436,6 +455,56 @@ void RemoteSuggestionsProviderImpl::RefetchInTheBackground(
FetchSuggestions(/*interactive_request=*/false, std::move(callback));
}
void RemoteSuggestionsProviderImpl::RefetchWhileDisplaying(
FetchStatusCallback callback) {
debug_logger_->Log(FROM_HERE, /*message=*/std::string());
if (!AreArticlesAvailable()) {
// If the article section is not AVAILABLE, we cannot safely flip its status
// to AVAILABLE_LOADING and back. Instead, we fallback to the standard
// background refetch.
debug_logger_->Log(
FROM_HERE,
"fallback because the articles category is not displayed yet");
// TODO(jkrcal): If the provider is not ready() we fallback to
// RefetchInTheBackground which post-pones the FetchSuggestions() task until
// the provider gets ready. When the provider gets ready, the provider calls
// FetchSuggestions() directly, without flipping the category status as
// implemented in this function. We should postpone the
// RefetchWhileDisplaying task here, instead (or deal with postponing in the
// scheduler only).
RefetchInTheBackground(std::move(callback));
return;
}
NotifyRefetchWhileDisplayingStarted();
// |fetch_timeout_timer_| makes sure the UI stops waiting after a certain time
// period (in that case, it can fall-back to the old suggestions).
fetch_timeout_timer_->Start(
FROM_HERE, GetTimeoutForRefetchWhileDisplaying(),
base::Bind(&RemoteSuggestionsProviderImpl::
NotifyRefetchWhileDisplayingFailedOrTimeouted,
base::Unretained(this)));
FetchStatusCallback callback_wrapped = base::BindOnce(
&RemoteSuggestionsProviderImpl::OnRefetchWhileDisplayingFinished,
base::Unretained(this), std::move(callback));
FetchSuggestions(/*interactive_request=*/true, std::move(callback_wrapped));
}
void RemoteSuggestionsProviderImpl::OnRefetchWhileDisplayingFinished(
FetchStatusCallback callback,
Status status) {
fetch_timeout_timer_->Stop();
// If the fetch succeeds, it already notified new results.
if (!status.IsSuccess()) {
NotifyRefetchWhileDisplayingFailedOrTimeouted();
}
if (callback) {
std::move(callback).Run(status);
}
}
const RemoteSuggestionsFetcher*
RemoteSuggestionsProviderImpl::suggestions_fetcher_for_debugging() const {
return suggestions_fetcher_.get();
......@@ -559,6 +628,36 @@ void RemoteSuggestionsProviderImpl::MarkEmptyCategoriesAsLoading() {
}
}
bool RemoteSuggestionsProviderImpl::AreArticlesAvailable() {
if (!ready()) {
return false;
}
auto articles_it = category_contents_.find(articles_category_);
DCHECK(articles_it != category_contents_.end());
CategoryContent& content = articles_it->second;
return content.status == CategoryStatus::AVAILABLE;
}
void RemoteSuggestionsProviderImpl::NotifyRefetchWhileDisplayingStarted() {
auto articles_it = category_contents_.find(articles_category_);
DCHECK(articles_it != category_contents_.end());
CategoryContent& content = articles_it->second;
DCHECK_EQ(content.status, CategoryStatus::AVAILABLE);
UpdateCategoryStatus(articles_it->first, CategoryStatus::AVAILABLE_LOADING);
}
void RemoteSuggestionsProviderImpl::
NotifyRefetchWhileDisplayingFailedOrTimeouted() {
auto articles_it = category_contents_.find(articles_category_);
DCHECK(articles_it != category_contents_.end());
CategoryContent& content = articles_it->second;
DCHECK_EQ(content.status, CategoryStatus::AVAILABLE_LOADING);
UpdateCategoryStatus(articles_it->first, CategoryStatus::AVAILABLE);
// TODO(jkrcal): Technically, we have no new suggestions; we should not
// notify. This is a work-around before crbug.com/768410 gets fixed.
NotifyNewSuggestions(articles_category_, content.suggestions);
}
CategoryStatus RemoteSuggestionsProviderImpl::GetCategoryStatus(
Category category) {
auto content_it = category_contents_.find(category);
......
......@@ -20,6 +20,7 @@
#include "base/optional.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/ntp_snippets/category.h"
#include "components/ntp_snippets/category_status.h"
#include "components/ntp_snippets/content_suggestion.h"
......@@ -72,7 +73,8 @@ class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
std::unique_ptr<RemoteSuggestionsStatusService> status_service,
std::unique_ptr<PrefetchedPagesTracker> prefetched_pages_tracker,
std::unique_ptr<BreakingNewsListener> breaking_news_raw_data_provider,
Logger* debug_logger);
Logger* debug_logger,
std::unique_ptr<base::OneShotTimer> fetch_timeout_timer);
~RemoteSuggestionsProviderImpl() override;
......@@ -89,6 +91,7 @@ class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
// RemoteSuggestionsProvider implementation.
void RefetchInTheBackground(FetchStatusCallback callback) override;
void RefetchWhileDisplaying(FetchStatusCallback callback) override;
// TODO(fhorschig): Remove this getter when there is an interface for the
// fetcher that allows better mocks.
......@@ -393,6 +396,12 @@ class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
void MarkEmptyCategoriesAsLoading();
bool AreArticlesAvailable();
void NotifyRefetchWhileDisplayingStarted();
void NotifyRefetchWhileDisplayingFailedOrTimeouted();
void OnRefetchWhileDisplayingFinished(FetchStatusCallback callback,
Status status);
State state_;
PrefService* pref_service_;
......@@ -456,6 +465,9 @@ class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
// Additional logging, accesible through snippets-internals.
Logger* debug_logger_;
// A Timer for canceling too long fetches.
std::unique_ptr<base::OneShotTimer> fetch_timeout_timer_;
DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProviderImpl);
};
......
......@@ -143,6 +143,10 @@ const char* const kTriggerTypesParamValueForEmptyList = "-";
const int kBlockBackgroundFetchesMinutesAfterClearingHistory = 30;
// Variation parameter for minimal age of a fetch to be considered "stale".
const char kMinAgeForStaleFetchHoursParamName[] =
"min_age_for_stale_fetch_hours";
// Returns the time interval to use for scheduling remote suggestion fetches for
// the given interval and user_class.
base::TimeDelta GetDesiredFetchingInterval(
......@@ -412,6 +416,16 @@ bool RemoteSuggestionsSchedulerImpl::FetchingSchedule::is_empty() const {
interval_shown_fallback.is_zero();
}
base::TimeDelta
RemoteSuggestionsSchedulerImpl::FetchingSchedule::GetStalenessInterval() const {
// The default value for staleness is |interval_startup_wifi| which is not
// constant. It depends on user class and is configurable by field trial
// params as well.
return base::TimeDelta::FromHours(base::GetFieldTrialParamByFeatureAsInt(
ntp_snippets::kArticleSuggestionsFeature,
kMinAgeForStaleFetchHoursParamName, interval_startup_wifi.InHours()));
}
// The TriggerType enum specifies values for the events that can trigger
// fetching remote suggestions. These values are written to logs. New enum
// values can be added, but existing enums must never be renumbered or deleted
......@@ -478,7 +492,8 @@ void RemoteSuggestionsSchedulerImpl::RegisterProfilePrefs(
0);
registry->RegisterInt64Pref(prefs::kSnippetShownFetchingIntervalWifi, 0);
registry->RegisterInt64Pref(prefs::kSnippetShownFetchingIntervalFallback, 0);
registry->RegisterInt64Pref(prefs::kSnippetLastFetchAttempt, 0);
registry->RegisterInt64Pref(prefs::kSnippetLastFetchAttemptTime, 0);
registry->RegisterInt64Pref(prefs::kSnippetLastSuccessfulFetchTime, 0);
}
void RemoteSuggestionsSchedulerImpl::SetProvider(
......@@ -498,7 +513,7 @@ void RemoteSuggestionsSchedulerImpl::RunQueuedTriggersIfReady() {
std::set<TriggerType> queued_triggers_copy;
queued_triggers_copy.swap(queued_triggers_);
for (const TriggerType trigger : queued_triggers_copy) {
RefetchInTheBackgroundIfAppropriate(trigger);
RefetchIfAppropriate(trigger);
}
}
}
......@@ -545,29 +560,28 @@ void RemoteSuggestionsSchedulerImpl::OnInteractiveFetchFinished(
void RemoteSuggestionsSchedulerImpl::OnPersistentSchedulerWakeUp() {
debug_logger_->Log(FROM_HERE, /*message=*/std::string());
RefetchInTheBackgroundIfAppropriate(
TriggerType::PERSISTENT_SCHEDULER_WAKE_UP);
RefetchIfAppropriate(TriggerType::PERSISTENT_SCHEDULER_WAKE_UP);
}
void RemoteSuggestionsSchedulerImpl::OnBrowserForegrounded() {
debug_logger_->Log(FROM_HERE, /*message=*/std::string());
// TODO(jkrcal): Consider that this is called whenever we open or return to an
// Activity. Therefore, keep work light for fast start up calls.
RefetchInTheBackgroundIfAppropriate(TriggerType::BROWSER_FOREGROUNDED);
RefetchIfAppropriate(TriggerType::BROWSER_FOREGROUNDED);
}
void RemoteSuggestionsSchedulerImpl::OnBrowserColdStart() {
debug_logger_->Log(FROM_HERE, /*message=*/std::string());
// TODO(jkrcal): Consider that work here must be kept light for fast
// cold start ups.
RefetchInTheBackgroundIfAppropriate(TriggerType::BROWSER_COLD_START);
RefetchIfAppropriate(TriggerType::BROWSER_COLD_START);
}
void RemoteSuggestionsSchedulerImpl::OnSuggestionsSurfaceOpened() {
debug_logger_->Log(FROM_HERE, /*message=*/std::string());
// TODO(jkrcal): Consider that this is called whenever we open an NTP.
// Therefore, keep work light for fast start up calls.
RefetchInTheBackgroundIfAppropriate(TriggerType::SURFACE_OPENED);
RefetchIfAppropriate(TriggerType::SURFACE_OPENED);
}
void RemoteSuggestionsSchedulerImpl::StartScheduling() {
......@@ -662,8 +676,21 @@ void RemoteSuggestionsSchedulerImpl::StoreFetchingSchedule() {
SerializeTimeDelta(schedule_.interval_shown_fallback));
}
void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate(
TriggerType trigger) {
bool RemoteSuggestionsSchedulerImpl::IsLastSuccessfulFetchStale() const {
// Avoid claiming staleness on the first fetch ever (after installing /
// upgrading Chrome to a version that writes this pref). We really do not
// know when was the last fetch.
if (!profile_prefs_->HasPrefPath(prefs::kSnippetLastSuccessfulFetchTime)) {
return false;
}
const base::Time last_successful_fetch_time = DeserializeTime(
profile_prefs_->GetInt64(prefs::kSnippetLastSuccessfulFetchTime));
return clock_->Now() - last_successful_fetch_time >
schedule_.GetStalenessInterval();
}
void RemoteSuggestionsSchedulerImpl::RefetchIfAppropriate(TriggerType trigger) {
debug_logger_->Log(FROM_HERE, /*message=*/std::string());
if (background_fetch_in_progress_) {
......@@ -691,7 +718,7 @@ void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate(
}
const base::Time last_fetch_attempt_time = base::Time::FromInternalValue(
profile_prefs_->GetInt64(prefs::kSnippetLastFetchAttempt));
profile_prefs_->GetInt64(prefs::kSnippetLastFetchAttemptTime));
if (trigger == TriggerType::SURFACE_OPENED &&
!time_until_first_shown_trigger_reported_) {
......@@ -709,7 +736,7 @@ void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate(
}
if (trigger != TriggerType::PERSISTENT_SCHEDULER_WAKE_UP &&
!ShouldRefetchInTheBackgroundNow(last_fetch_attempt_time, trigger)) {
!ShouldRefetchNow(last_fetch_attempt_time, trigger)) {
debug_logger_->Log(FROM_HERE, "stop, because too soon");
return;
}
......@@ -741,12 +768,23 @@ void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate(
debug_logger_->Log(FROM_HERE, "issuing a fetch");
background_fetch_in_progress_ = true;
provider_->RefetchInTheBackground(base::Bind(
&RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundFinished,
base::Unretained(this)));
if ((trigger == TriggerType::BROWSER_COLD_START ||
trigger == TriggerType::BROWSER_FOREGROUNDED ||
trigger == TriggerType::SURFACE_OPENED) &&
IsLastSuccessfulFetchStale()) {
provider_->RefetchWhileDisplaying(
base::Bind(&RemoteSuggestionsSchedulerImpl::RefetchFinished,
base::Unretained(this)));
return;
}
provider_->RefetchInTheBackground(
base::Bind(&RemoteSuggestionsSchedulerImpl::RefetchFinished,
base::Unretained(this)));
}
bool RemoteSuggestionsSchedulerImpl::ShouldRefetchInTheBackgroundNow(
bool RemoteSuggestionsSchedulerImpl::ShouldRefetchNow(
base::Time last_fetch_attempt_time,
TriggerType trigger) {
// If we have no persistent scheduler to ask, err on the side of caution.
......@@ -820,14 +858,13 @@ bool RemoteSuggestionsSchedulerImpl::AcquireQuota(bool interactive_request) {
return false;
}
void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundFinished(
Status fetch_status) {
void RemoteSuggestionsSchedulerImpl::RefetchFinished(Status fetch_status) {
background_fetch_in_progress_ = false;
OnFetchCompleted(fetch_status);
}
void RemoteSuggestionsSchedulerImpl::OnFetchCompleted(Status fetch_status) {
profile_prefs_->SetInt64(prefs::kSnippetLastFetchAttempt,
profile_prefs_->SetInt64(prefs::kSnippetLastFetchAttemptTime,
SerializeTime(clock_->Now()));
time_until_first_shown_trigger_reported_ = false;
time_until_first_startup_trigger_reported_ = false;
......@@ -839,11 +876,15 @@ void RemoteSuggestionsSchedulerImpl::OnFetchCompleted(Status fetch_status) {
if (fetch_status.code != StatusCode::SUCCESS) {
return;
}
profile_prefs_->SetInt64(prefs::kSnippetLastSuccessfulFetchTime,
SerializeTime(clock_->Now()));
ApplyPersistentFetchingSchedule();
}
void RemoteSuggestionsSchedulerImpl::ClearLastFetchAttemptTime() {
profile_prefs_->ClearPref(prefs::kSnippetLastFetchAttempt);
profile_prefs_->ClearPref(prefs::kSnippetLastFetchAttemptTime);
}
std::set<RemoteSuggestionsSchedulerImpl::TriggerType>
......
......@@ -71,6 +71,12 @@ class RemoteSuggestionsSchedulerImpl : public RemoteSuggestionsScheduler {
bool operator!=(const FetchingSchedule& other) const;
bool is_empty() const;
// Interval since the last successful fetch after which to consider the
// current content stale.
base::TimeDelta GetStalenessInterval() const;
// Intervals since the last fetch attempt after which to fetch again
// (depending on the trigger and connectivity).
base::TimeDelta interval_persistent_wifi;
base::TimeDelta interval_persistent_fallback;
base::TimeDelta interval_startup_wifi;
......@@ -91,14 +97,16 @@ class RemoteSuggestionsSchedulerImpl : public RemoteSuggestionsScheduler {
// schedule.
void StopScheduling();
bool IsLastSuccessfulFetchStale() const;
// Trigger a background refetch for the given |trigger| if enabled and if the
// timing is appropriate for another fetch.
void RefetchInTheBackgroundIfAppropriate(TriggerType trigger);
void RefetchIfAppropriate(TriggerType trigger);
// Checks whether it is time to perform a soft background fetch for |trigger|,
// according to |schedule|.
bool ShouldRefetchInTheBackgroundNow(base::Time last_fetch_attempt_time,
TriggerType trigger);
bool ShouldRefetchNow(base::Time last_fetch_attempt_time,
TriggerType trigger);
// Returns whether all components are ready for background fetches.
bool IsReadyForBackgroundFetches() const;
......@@ -108,8 +116,8 @@ class RemoteSuggestionsSchedulerImpl : public RemoteSuggestionsScheduler {
// Returns true if quota is available for another request.
bool AcquireQuota(bool interactive_request);
// Callback after RefetchInTheBackground is completed.
void RefetchInTheBackgroundFinished(Status fetch_status);
// Callback after Refetch is completed.
void RefetchFinished(Status fetch_status);
// Common function to call after a fetch of any type is finished.
void OnFetchCompleted(Status fetch_status);
......
......@@ -196,7 +196,8 @@ void RegisterRemoteSuggestionsProvider(ContentSuggestionsService* service,
base::MakeUnique<RemoteSuggestionsStatusServiceImpl>(signin_manager,
prefs, pref_name),
/*prefetched_pages_tracker=*/nullptr,
/*breaking_news_raw_data_provider*/ nullptr, service->debug_logger());
/*breaking_news_raw_data_provider*/ nullptr, service->debug_logger(),
std::make_unique<base::OneShotTimer>());
service->remote_suggestions_scheduler()->SetProvider(provider.get());
service->set_remote_suggestions_provider(provider.get());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment