diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-17 13:57:45 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-19 13:44:40 +0000 |
commit | 6ec7b8da05d21a3878bd21c691b41e675d74bb1c (patch) | |
tree | b87f250bc19413750b9bb9cdbf2da20ef5014820 /chromium/components/ntp_snippets | |
parent | ec02ee4181c49b61fce1c8fb99292dbb8139cc90 (diff) | |
download | qtwebengine-chromium-6ec7b8da05d21a3878bd21c691b41e675d74bb1c.tar.gz |
BASELINE: Update Chromium to 60.0.3112.70
Change-Id: I9911c2280a014d4632f254857876a395d4baed2d
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components/ntp_snippets')
36 files changed, 703 insertions, 367 deletions
diff --git a/chromium/components/ntp_snippets/BUILD.gn b/chromium/components/ntp_snippets/BUILD.gn index 0cf1874adc8..54a66cf3d0e 100644 --- a/chromium/components/ntp_snippets/BUILD.gn +++ b/chromium/components/ntp_snippets/BUILD.gn @@ -47,8 +47,6 @@ static_library("ntp_snippets") { "pref_names.h", "pref_util.cc", "pref_util.h", - "reading_list/reading_list_distillation_state_util.cc", - "reading_list/reading_list_distillation_state_util.h", "reading_list/reading_list_suggestions_provider.cc", "reading_list/reading_list_suggestions_provider.h", "remote/json_request.cc", diff --git a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc index 436e8bcee05..9b0e2df80d3 100644 --- a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc +++ b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc @@ -18,9 +18,6 @@ #include "components/ntp_snippets/category.h" #include "components/ntp_snippets/content_suggestion.h" #include "components/ntp_snippets/features.h" -#include "components/ntp_snippets/pref_names.h" -#include "components/prefs/pref_registry_simple.h" -#include "components/prefs/pref_service.h" #include "components/strings/grit/components_strings.h" #include "components/variations/variations_associated_data.h" #include "ui/base/l10n/l10n_util.h" @@ -34,17 +31,13 @@ namespace ntp_snippets { namespace { const int kMaxBookmarks = 10; -const int kMaxBookmarkAgeInDays = 42; +const int kMaxBookmarkAgeInDays = 7; const char* kMaxBookmarksParamName = "bookmarks_max_count"; const char* kMaxBookmarkAgeInDaysParamName = "bookmarks_max_age_in_days"; const char* kConsiderDesktopVisitsParamName = "bookmarks_consider_desktop_visits"; -// TODO(treib,jkrcal): Remove this after M57. -const char kDeprecatedBookmarksFirstM54StartPref[] = - "ntp_suggestions.bookmarks.first_M54_start"; - // Any bookmark created or visited after this time will be considered recent. // Note that bookmarks can be shown that do not meet this threshold. base::Time GetThresholdTime() { @@ -64,15 +57,14 @@ int GetMaxCount() { bool AreDesktopVisitsConsidered() { return variations::GetVariationParamByFeatureAsBool( ntp_snippets::kBookmarkSuggestionsFeature, - kConsiderDesktopVisitsParamName, false); + kConsiderDesktopVisitsParamName, true); } } // namespace BookmarkSuggestionsProvider::BookmarkSuggestionsProvider( ContentSuggestionsProvider::Observer* observer, - bookmarks::BookmarkModel* bookmark_model, - PrefService* pref_service) + bookmarks::BookmarkModel* bookmark_model) : ContentSuggestionsProvider(observer), category_status_(CategoryStatus::AVAILABLE_LOADING), provided_category_( @@ -82,7 +74,6 @@ BookmarkSuggestionsProvider::BookmarkSuggestionsProvider( end_of_list_last_visit_date_(GetThresholdTime()), consider_bookmark_visits_from_desktop_(AreDesktopVisitsConsidered()) { observer->OnCategoryStatusChanged(this, provided_category_, category_status_); - pref_service->ClearPref(kDeprecatedBookmarksFirstM54StartPref); bookmark_model_->AddObserver(this); FetchBookmarks(); } @@ -91,12 +82,6 @@ BookmarkSuggestionsProvider::~BookmarkSuggestionsProvider() { bookmark_model_->RemoveObserver(this); } -// static -void BookmarkSuggestionsProvider::RegisterProfilePrefs( - PrefRegistrySimple* registry) { - registry->RegisterInt64Pref(kDeprecatedBookmarksFirstM54StartPref, 0); -} - //////////////////////////////////////////////////////////////////////////////// // Private methods diff --git a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h index 64cd6e25d1a..404c8d6ada2 100644 --- a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h +++ b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h @@ -15,9 +15,6 @@ #include "components/ntp_snippets/category_status.h" #include "components/ntp_snippets/content_suggestions_provider.h" -class PrefRegistrySimple; -class PrefService; - namespace ntp_snippets { // Provides content suggestions from the bookmarks model. @@ -25,12 +22,9 @@ class BookmarkSuggestionsProvider : public ContentSuggestionsProvider, public bookmarks::BookmarkModelObserver { public: BookmarkSuggestionsProvider(ContentSuggestionsProvider::Observer* observer, - bookmarks::BookmarkModel* bookmark_model, - PrefService* pref_service); + bookmarks::BookmarkModel* bookmark_model); ~BookmarkSuggestionsProvider() override; - static void RegisterProfilePrefs(PrefRegistrySimple* registry); - private: // ContentSuggestionsProvider implementation. CategoryStatus GetCategoryStatus(Category category) override; diff --git a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider_unittest.cc b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider_unittest.cc index 92682d579b7..fda7b18c941 100644 --- a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider_unittest.cc +++ b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider_unittest.cc @@ -18,7 +18,6 @@ #include "components/ntp_snippets/bookmarks/bookmark_last_visit_utils.h" #include "components/ntp_snippets/category.h" #include "components/ntp_snippets/mock_content_suggestions_provider_observer.h" -#include "components/prefs/testing_pref_service.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -53,15 +52,13 @@ class BookmarkSuggestionsProviderTest : public ::testing::Test { _, Category::FromKnownCategory(KnownCategories::BOOKMARKS), CategoryStatus::AVAILABLE)) .RetiresOnSaturation(); - BookmarkSuggestionsProvider::RegisterProfilePrefs(test_prefs_.registry()); - provider_ = base::MakeUnique<BookmarkSuggestionsProvider>( - &observer_, model_.get(), &test_prefs_); + provider_ = + base::MakeUnique<BookmarkSuggestionsProvider>(&observer_, model_.get()); } protected: std::unique_ptr<bookmarks::BookmarkModel> model_; StrictMock<MockContentSuggestionsProviderObserver> observer_; - TestingPrefServiceSimple test_prefs_; std::unique_ptr<BookmarkSuggestionsProvider> provider_; }; diff --git a/chromium/components/ntp_snippets/category_rankers/category_ranker.h b/chromium/components/ntp_snippets/category_rankers/category_ranker.h index aa19c57d668..89b01246f64 100644 --- a/chromium/components/ntp_snippets/category_rankers/category_ranker.h +++ b/chromium/components/ntp_snippets/category_rankers/category_ranker.h @@ -5,6 +5,9 @@ #ifndef COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CATEGORY_RANKER_H_ #define COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CATEGORY_RANKER_H_ +#include <string> +#include <vector> + #include "base/time/time.h" #include "components/ntp_snippets/category.h" @@ -39,6 +42,17 @@ class CategoryRanker { virtual void InsertCategoryAfterIfNecessary(Category category_to_insert, Category anchor) = 0; + struct DebugDataItem { + std::string label; + std::string content; + DebugDataItem(const std::string& label, const std::string& content) + : label(label), content(content) {} + }; + + // Returns DebugData in form of pairs of strings (label; content), + // e.g. describing internal state or parameter values. + virtual std::vector<DebugDataItem> GetDebugData() = 0; + // Feedback data from the user to update the ranking. // Called whenever a suggestion is opened by the user. diff --git a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc index 62e2ad711b5..1b060e63eb6 100644 --- a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc +++ b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc @@ -10,6 +10,7 @@ #include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/values.h" #include "components/ntp_snippets/category_rankers/constant_category_ranker.h" #include "components/ntp_snippets/content_suggestions_metrics.h" @@ -131,6 +132,14 @@ base::Optional<Category> GetPromotedCategoryFromVariations() { return Category::FromIDValue(category_id); } +std::string GetOptionalCategoryAsString( + const base::Optional<Category>& optional_category) { + if (optional_category.has_value()) { + return base::IntToString(optional_category->id()); + } + return "None"; +} + } // namespace ClickBasedCategoryRanker::ClickBasedCategoryRanker( @@ -264,6 +273,46 @@ void ClickBasedCategoryRanker::InsertCategoryAfterIfNecessary( /*after=*/true); } +std::vector<CategoryRanker::DebugDataItem> +ClickBasedCategoryRanker::GetDebugData() { + std::vector<CategoryRanker::DebugDataItem> result; + result.push_back( + CategoryRanker::DebugDataItem("Type", "ClickBasedCategoryRanker")); + + std::string initial_order_type; + CategoryOrderChoice choice = GetSelectedCategoryOrder(); + if (choice == CategoryOrderChoice::GENERAL) { + initial_order_type = "GENERAL"; + } + if (choice == CategoryOrderChoice::EMERGING_MARKETS_ORIENTED) { + initial_order_type = "EMERGING_MARKETS_ORIENTED;"; + } + result.push_back( + CategoryRanker::DebugDataItem("Initial order type", initial_order_type)); + + std::vector<std::string> category_strings; + for (const auto& ranked_category : ordered_categories_) { + category_strings.push_back(base::ReplaceStringPlaceholders( + "($1; $2)", + {base::IntToString(ranked_category.category.id()), + base::IntToString(ranked_category.clicks)}, + /*offsets=*/nullptr)); + } + result.push_back( + CategoryRanker::DebugDataItem("Current order (with click counts)", + base::JoinString(category_strings, ", "))); + + result.push_back(CategoryRanker::DebugDataItem( + "Actual promoted category", + GetOptionalCategoryAsString(promoted_category_))); + + result.push_back(CategoryRanker::DebugDataItem( + "Raw promoted category from variations", + GetOptionalCategoryAsString(GetPromotedCategoryFromVariations()))); + + return result; +} + void ClickBasedCategoryRanker::OnSuggestionOpened(Category category) { if (!ContainsCategory(category)) { LOG(DFATAL) << "The category with ID " << category.id() diff --git a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.h b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.h index 06bd846107f..1c69ec913d7 100644 --- a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.h +++ b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.h @@ -6,6 +6,7 @@ #define COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_ #include <memory> +#include <string> #include <vector> #include "base/optional.h" @@ -40,6 +41,7 @@ class ClickBasedCategoryRanker : public CategoryRanker { Category anchor) override; void InsertCategoryAfterIfNecessary(Category category_to_insert, Category anchor) override; + std::vector<CategoryRanker::DebugDataItem> GetDebugData() override; void OnSuggestionOpened(Category category) override; void OnCategoryDismissed(Category category) override; diff --git a/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.cc b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.cc index 828cd317e5d..d68fe9104d4 100644 --- a/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.cc +++ b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.cc @@ -5,6 +5,8 @@ #include "components/ntp_snippets/category_rankers/constant_category_ranker.h" #include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "components/ntp_snippets/features.h" namespace ntp_snippets { @@ -75,6 +77,33 @@ void ConstantCategoryRanker::InsertCategoryAfterIfNecessary( AppendCategoryIfNecessary(category_to_insert); } +std::vector<CategoryRanker::DebugDataItem> +ConstantCategoryRanker::GetDebugData() { + std::vector<CategoryRanker::DebugDataItem> result; + result.push_back( + CategoryRanker::DebugDataItem("Type", "ConstantCategoryRanker")); + + std::string initial_order_type; + CategoryOrderChoice choice = GetSelectedCategoryOrder(); + if (choice == CategoryOrderChoice::GENERAL) { + initial_order_type = "GENERAL"; + } + if (choice == CategoryOrderChoice::EMERGING_MARKETS_ORIENTED) { + initial_order_type = "EMERGING_MARKETS_ORIENTED;"; + } + result.push_back( + CategoryRanker::DebugDataItem("Initial order type", initial_order_type)); + + std::vector<std::string> category_strings; + for (Category category : ordered_categories_) { + category_strings.push_back(base::IntToString(category.id())); + } + result.push_back(CategoryRanker::DebugDataItem( + "Current order", base::JoinString(category_strings, ", "))); + + return result; +} + void ConstantCategoryRanker::OnSuggestionOpened(Category category) { // Ignored. The order is constant. } diff --git a/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.h b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.h index 8e4417e859a..b3a129ec4e7 100644 --- a/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.h +++ b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.h @@ -5,6 +5,7 @@ #ifndef COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CONSTANT_SECTION_RANKER_H_ #define COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CONSTANT_SECTION_RANKER_H_ +#include <string> #include <vector> #include "base/time/time.h" @@ -31,6 +32,7 @@ class ConstantCategoryRanker : public CategoryRanker { Category anchor) override; void InsertCategoryAfterIfNecessary(Category category_to_insert, Category anchor) override; + std::vector<CategoryRanker::DebugDataItem> GetDebugData() override; void OnSuggestionOpened(Category category) override; void OnCategoryDismissed(Category category) override; diff --git a/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.cc b/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.cc index 94739a7b525..42ce8bc9dab 100644 --- a/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.cc +++ b/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.cc @@ -42,6 +42,11 @@ void FakeCategoryRanker::InsertCategoryAfterIfNecessary( // Ignored. } +std::vector<CategoryRanker::DebugDataItem> FakeCategoryRanker::GetDebugData() { + // Ignored. + return std::vector<CategoryRanker::DebugDataItem>(); +} + void FakeCategoryRanker::OnSuggestionOpened(Category category) { // Ignored. } diff --git a/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.h b/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.h index 9b5e37659ff..dedba15bf66 100644 --- a/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.h +++ b/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.h @@ -31,6 +31,7 @@ class FakeCategoryRanker : public CategoryRanker { Category anchor) override; void InsertCategoryAfterIfNecessary(Category category_to_insert, Category anchor) override; + std::vector<CategoryRanker::DebugDataItem> GetDebugData() override; void OnSuggestionOpened(Category category) override; void OnCategoryDismissed(Category category) override; diff --git a/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.h b/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.h index 2238aeea7e7..6dbed913c8e 100644 --- a/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.h +++ b/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.h @@ -24,6 +24,7 @@ class MockCategoryRanker : public CategoryRanker { void(Category category_to_insert, Category anchor)); MOCK_METHOD2(InsertCategoryAfterIfNecessary, void(Category category_to_insert, Category anchor)); + MOCK_METHOD0(GetDebugData, std::vector<CategoryRanker::DebugDataItem>()); MOCK_METHOD1(OnSuggestionOpened, void(Category category)); MOCK_METHOD1(OnCategoryDismissed, void(Category Category)); }; diff --git a/chromium/components/ntp_snippets/content_suggestion.h b/chromium/components/ntp_snippets/content_suggestion.h index 5bc647e4cda..1b70c38fbd1 100644 --- a/chromium/components/ntp_snippets/content_suggestion.h +++ b/chromium/components/ntp_snippets/content_suggestion.h @@ -49,14 +49,8 @@ struct RecentTabSuggestionExtra { // ReadingListSuggestionExtra contains additional data which is only available // for Reading List suggestions. struct ReadingListSuggestionExtra { - // State of the distillation a suggestion. This is the meaningful extract of - // ReadingListEntry::DistillationState for the suggestions. It is duplicated - // here to avoid a dependence on ReadingList. - enum class ReadingListSuggestionDistilledState { PENDING, SUCCESS, FAILURE }; - // State of the distillation of the suggestion. - ReadingListSuggestionDistilledState distilled_state = - ReadingListSuggestionDistilledState::PENDING; + bool distilled = false; // URL of the page whose favicon should be displayed for this suggestion. GURL favicon_page_url; }; diff --git a/chromium/components/ntp_snippets/content_suggestions_metrics.cc b/chromium/components/ntp_snippets/content_suggestions_metrics.cc index 5d2d4afe9af..348a555694e 100644 --- a/chromium/components/ntp_snippets/content_suggestions_metrics.cc +++ b/chromium/components/ntp_snippets/content_suggestions_metrics.cc @@ -25,6 +25,8 @@ const int kMaxCategories = 10; const char kHistogramCountOnNtpOpened[] = "NewTabPage.ContentSuggestions.CountOnNtpOpened"; +const char kHistogramSectionCountOnNtpOpened[] = + "NewTabPage.ContentSuggestions.SectionCountOnNtpOpened"; const char kHistogramShown[] = "NewTabPage.ContentSuggestions.Shown"; const char kHistogramShownAge[] = "NewTabPage.ContentSuggestions.ShownAge"; const char kHistogramShownScore[] = @@ -212,7 +214,8 @@ void RecordContentSuggestionsUsage() { } // namespace void OnPageShown( - const std::vector<std::pair<Category, int>>& suggestions_per_category) { + const std::vector<std::pair<Category, int>>& suggestions_per_category, + int visible_categories_count) { int suggestions_total = 0; for (const std::pair<Category, int>& item : suggestions_per_category) { LogCategoryHistogramPosition(kHistogramCountOnNtpOpened, item.first, @@ -221,6 +224,8 @@ void OnPageShown( } UMA_HISTOGRAM_EXACT_LINEAR(kHistogramCountOnNtpOpened, suggestions_total, kMaxSuggestionsTotal); + UMA_HISTOGRAM_EXACT_LINEAR(kHistogramSectionCountOnNtpOpened, + visible_categories_count, kMaxCategories); } void OnSuggestionShown(int global_position, @@ -292,6 +297,8 @@ void OnSuggestionOpened(int global_position, if (category.IsKnownCategory(KnownCategories::ARTICLES)) { RecordContentSuggestionsUsage(); } + + base::RecordAction(base::UserMetricsAction("Suggestions.Content.Opened")); } void OnSuggestionMenuOpened(int global_position, @@ -366,5 +373,17 @@ void RecordRemoteSuggestionsProviderState(bool enabled) { "NewTabPage.ContentSuggestions.Preferences.RemoteSuggestions", enabled); } +void RecordContentSuggestionDismissed() { + base::RecordAction(base::UserMetricsAction("Suggestions.Content.Dismissed")); +} + +void RecordCategoryDismissed() { + base::RecordAction(base::UserMetricsAction("Suggestions.Category.Dismissed")); +} + +void RecordFetchAction() { + base::RecordAction(base::UserMetricsAction("Suggestions.Category.Fetch")); +} + } // namespace metrics } // namespace ntp_snippets diff --git a/chromium/components/ntp_snippets/content_suggestions_metrics.h b/chromium/components/ntp_snippets/content_suggestions_metrics.h index a2f04e5668d..e31b7713633 100644 --- a/chromium/components/ntp_snippets/content_suggestions_metrics.h +++ b/chromium/components/ntp_snippets/content_suggestions_metrics.h @@ -16,7 +16,8 @@ namespace ntp_snippets { namespace metrics { void OnPageShown( - const std::vector<std::pair<Category, int>>& suggestions_per_category); + const std::vector<std::pair<Category, int>>& suggestions_per_category, + int visible_categories_count); // Should only be called once per NTP for each suggestion. void OnSuggestionShown(int global_position, @@ -60,6 +61,12 @@ void OnCategoryDismissed(Category category); void RecordRemoteSuggestionsProviderState(bool enabled); +void RecordContentSuggestionDismissed(); + +void RecordCategoryDismissed(); + +void RecordFetchAction(); + } // namespace metrics } // namespace ntp_snippets diff --git a/chromium/components/ntp_snippets/content_suggestions_provider.h b/chromium/components/ntp_snippets/content_suggestions_provider.h index 8c485dfd5a0..3fc37b01525 100644 --- a/chromium/components/ntp_snippets/content_suggestions_provider.h +++ b/chromium/components/ntp_snippets/content_suggestions_provider.h @@ -97,9 +97,11 @@ class ContentSuggestionsProvider { // Fetches more suggestions for the given category. The new suggestions // will not include any suggestion of the |known_suggestion_ids| sets. - // The given |callback| is called with these suggestions, along with all - // existing suggestions. It has to be invoked exactly once as the front-end - // might wait for its completion. + // As a result of this call, the provider: + // - should call the |callback| with these additional suggestions (exactly + // once as the front-end might wait for its completion); + // - should *not* notify its Observer by OnNewSuggestions() with these + // additional suggestions. virtual void Fetch(const Category& category, const std::set<std::string>& known_suggestion_ids, const FetchDoneCallback& callback) = 0; diff --git a/chromium/components/ntp_snippets/content_suggestions_service.cc b/chromium/components/ntp_snippets/content_suggestions_service.cc index de7ab3ff14f..71f323594d2 100644 --- a/chromium/components/ntp_snippets/content_suggestions_service.cc +++ b/chromium/components/ntp_snippets/content_suggestions_service.cc @@ -20,6 +20,7 @@ #include "components/favicon/core/large_icon_service.h" #include "components/favicon_base/fallback_icon_style.h" #include "components/favicon_base/favicon_types.h" +#include "components/ntp_snippets/content_suggestions_metrics.h" #include "components/ntp_snippets/pref_names.h" #include "components/ntp_snippets/remote/remote_suggestions_provider.h" #include "components/prefs/pref_registry_simple.h" @@ -169,16 +170,9 @@ void ContentSuggestionsService::FetchSuggestionFavicon( return; } - // TODO(jkrcal): Create a general wrapper function in LargeIconService that - // does handle the get-from-cache-and-fallback-to-google-server functionality - // in one shot (for all clients that do not need to react in between). - large_icon_service_->GetLargeIconImageOrFallbackStyle( - domain_with_favicon, minimum_size_in_pixel, desired_size_in_pixel, - base::Bind(&ContentSuggestionsService::OnGetFaviconFromCacheFinished, - base::Unretained(this), domain_with_favicon, - minimum_size_in_pixel, desired_size_in_pixel, callback, - /*continue_to_google_server=*/true), - &favicons_task_tracker_); + GetFaviconFromCache(domain_with_favicon, minimum_size_in_pixel, + desired_size_in_pixel, callback, + /*continue_to_google_server=*/true); } GURL ContentSuggestionsService::GetFaviconDomain( @@ -204,6 +198,26 @@ GURL ContentSuggestionsService::GetFaviconDomain( return GURL(); } +void ContentSuggestionsService::GetFaviconFromCache( + const GURL& publisher_url, + int minimum_size_in_pixel, + int desired_size_in_pixel, + const ImageFetchedCallback& callback, + bool continue_to_google_server) { + // TODO(jkrcal): Create a general wrapper function in LargeIconService that + // does handle the get-from-cache-and-fallback-to-google-server functionality + // in one shot (for all clients that do not need to react in between). + + // Use desired_size = 0 for getting the icon from the cache (so that the icon + // is not poorly rescaled by LargeIconService). + large_icon_service_->GetLargeIconImageOrFallbackStyle( + publisher_url, minimum_size_in_pixel, /*desired_size_in_pixel=*/0, + base::Bind(&ContentSuggestionsService::OnGetFaviconFromCacheFinished, + base::Unretained(this), publisher_url, minimum_size_in_pixel, + desired_size_in_pixel, callback, continue_to_google_server), + &favicons_task_tracker_); +} + void ContentSuggestionsService::OnGetFaviconFromCacheFinished( const GURL& publisher_url, int minimum_size_in_pixel, @@ -233,9 +247,13 @@ void ContentSuggestionsService::OnGetFaviconFromCacheFinished( } // Try to fetch the favicon from a Google favicon server. + // TODO(jkrcal): Currently used only for Articles for you which have public + // URLs. Let the provider decide whether |publisher_url| may be private or + // not. large_icon_service_ ->GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( - publisher_url, minimum_size_in_pixel, + publisher_url, minimum_size_in_pixel, desired_size_in_pixel, + /*may_page_url_be_private=*/false, base::Bind( &ContentSuggestionsService::OnGetFaviconFromGoogleServerFinished, base::Unretained(this), publisher_url, minimum_size_in_pixel, @@ -254,14 +272,9 @@ void ContentSuggestionsService::OnGetFaviconFromGoogleServerFinished( return; } - // Get the freshly downloaded icon from the cache. - large_icon_service_->GetLargeIconImageOrFallbackStyle( - publisher_url, minimum_size_in_pixel, desired_size_in_pixel, - base::Bind(&ContentSuggestionsService::OnGetFaviconFromCacheFinished, - base::Unretained(this), publisher_url, minimum_size_in_pixel, - desired_size_in_pixel, callback, - /*continue_to_google_server=*/false), - &favicons_task_tracker_); + GetFaviconFromCache(publisher_url, minimum_size_in_pixel, + desired_size_in_pixel, callback, + /*continue_to_google_server=*/false); } void ContentSuggestionsService::ClearHistory( @@ -324,6 +337,9 @@ void ContentSuggestionsService::DismissSuggestion( << " for unavailable category " << suggestion_id.category(); return; } + + metrics::RecordContentSuggestionDismissed(); + providers_by_category_[suggestion_id.category()]->DismissSuggestion( suggestion_id); @@ -339,6 +355,8 @@ void ContentSuggestionsService::DismissCategory(Category category) { return; } + metrics::RecordCategoryDismissed(); + ContentSuggestionsProvider* provider = providers_it->second; UnregisterCategory(category, provider); @@ -382,6 +400,8 @@ void ContentSuggestionsService::Fetch( return; } + metrics::RecordFetchAction(); + providers_it->second->Fetch(category, known_suggestion_ids, callback); } diff --git a/chromium/components/ntp_snippets/content_suggestions_service.h b/chromium/components/ntp_snippets/content_suggestions_service.h index 308e4dbe69d..40c442bc6bb 100644 --- a/chromium/components/ntp_snippets/content_suggestions_service.h +++ b/chromium/components/ntp_snippets/content_suggestions_service.h @@ -15,6 +15,7 @@ #include "base/observer_list.h" #include "base/optional.h" #include "base/scoped_observer.h" +#include "base/supports_user_data.h" #include "base/task/cancelable_task_tracker.h" #include "base/time/time.h" #include "components/history/core/browser/history_service.h" @@ -47,6 +48,7 @@ class RemoteSuggestionsProvider; // Retrieves suggestions from a number of ContentSuggestionsProviders and serves // them grouped into categories. There can be at most one provider per category. class ContentSuggestionsService : public KeyedService, + public base::SupportsUserData, public ContentSuggestionsProvider::Observer, public SigninManagerBase::Observer, public history::HistoryServiceObserver { @@ -330,6 +332,14 @@ class ContentSuggestionsService : public KeyedService, // Get the domain of the suggestion suitable for fetching the favicon. GURL GetFaviconDomain(const ContentSuggestion::ID& suggestion_id); + + // Initiate the fetch of a favicon from the local cache. + void GetFaviconFromCache(const GURL& publisher_url, + int minimum_size_in_pixel, + int desired_size_in_pixel, + const ImageFetchedCallback& callback, + bool continue_to_google_server); + // Callbacks for fetching favicons. void OnGetFaviconFromCacheFinished( const GURL& publisher_url, diff --git a/chromium/components/ntp_snippets/features.cc b/chromium/components/ntp_snippets/features.cc index 0b90a4271f1..6910f402873 100644 --- a/chromium/components/ntp_snippets/features.cc +++ b/chromium/components/ntp_snippets/features.cc @@ -13,6 +13,20 @@ namespace ntp_snippets { +// Keep sorted, and keep nullptr at the end. +const base::Feature*(kAllFeatures[]) = {&kArticleSuggestionsFeature, + &kBookmarkSuggestionsFeature, + &kCategoryOrder, + &kCategoryRanker, + &kForeignSessionsSuggestionsFeature, + &kIncreasedVisibility, + &kNotificationsFeature, + &kPhysicalWebPageSuggestionsFeature, + &kPreferAmpUrlsFeature, + &kPublisherFaviconsFromNewServerFeature, + &kRecentOfflineTabSuggestionsFeature, + nullptr}; + const base::Feature kArticleSuggestionsFeature{ "NTPArticleSuggestions", base::FEATURE_ENABLED_BY_DEFAULT}; @@ -113,4 +127,18 @@ CategoryOrderChoice GetSelectedCategoryOrder() { return CategoryOrderChoice::GENERAL; } +const base::Feature kNotificationsFeature = {"ContentSuggestionsNotifications", + base::FEATURE_DISABLED_BY_DEFAULT}; + +const char kNotificationsPriorityParam[] = "priority"; +const char kNotificationsTextParam[] = "text"; +const char kNotificationsTextValuePublisher[] = "publisher"; +const char kNotificationsTextValueSnippet[] = "snippet"; +const char kNotificationsTextValueAndMore[] = "and_more"; +const char kNotificationsKeepWhenFrontmostParam[] = + "keep_notification_when_frontmost"; +const char kNotificationsOpenToNTPParam[] = "open_to_ntp"; +const char kNotificationsDailyLimit[] = "daily_limit"; +const char kNotificationsIgnoredLimitParam[] = "ignored_limit"; + } // namespace ntp_snippets diff --git a/chromium/components/ntp_snippets/features.h b/chromium/components/ntp_snippets/features.h index 8832aed5502..9946e7c2d0a 100644 --- a/chromium/components/ntp_snippets/features.h +++ b/chromium/components/ntp_snippets/features.h @@ -18,6 +18,14 @@ class Clock; namespace ntp_snippets { +// +// Null-terminated list of all features related to content suggestions. +// +// If you add a base::Feature below, you must add it to this list. It is used in +// internal pages to list relevant parameters and settings. +// +extern const base::Feature*(kAllFeatures[]); + // Features to turn individual providers/categories on/off. // TODO(jkrcal): Rename to kRemoteSuggestionsFeature. extern const base::Feature kArticleSuggestionsFeature; @@ -72,6 +80,39 @@ enum class CategoryOrderChoice { // Returns which category order to use according to kCategoryOrder feature. CategoryOrderChoice GetSelectedCategoryOrder(); +// Enables and configures notifications for content suggestions on Android. +extern const base::Feature kNotificationsFeature; + +// An integer. The priority of the notification, ranging from -2 (PRIORITY_MIN) +// to 2 (PRIORITY_MAX). Vibrates and makes sound if >= 0. +extern const char kNotificationsPriorityParam[]; +constexpr int kNotificationsDefaultPriority = -1; + +// "publisher": use article's publisher as notification's text (default). +// "snippet": use article's snippet as notification's text. +// "and_more": use "From $1. Read this article and $2 more." as text. +extern const char kNotificationsTextParam[]; +extern const char kNotificationsTextValuePublisher[]; +extern const char kNotificationsTextValueSnippet[]; +extern const char kNotificationsTextValueAndMore[]; + +// "true": when Chrome becomes frontmost, leave notifications open. +// "false": automatically dismiss notification when Chrome becomes frontmost. +extern const char kNotificationsKeepWhenFrontmostParam[]; + +// "true": notifications link to chrome://newtab, with appropriate text. +// "false": notifications link to URL of notifying article. +extern const char kNotificationsOpenToNTPParam[]; + +// An integer. The maximum number of notifications that will be shown in 1 day. +extern const char kNotificationsDailyLimit[]; +constexpr int kNotificationsDefaultDailyLimit = 1; + +// An integer. The number of notifications that can be ignored. If the user +// ignores this many notifications or more, we stop sending them. +extern const char kNotificationsIgnoredLimitParam[]; +constexpr int kNotificationsIgnoredDefaultLimit = 3; + } // namespace ntp_snippets #endif // COMPONENTS_NTP_SNIPPETS_FEATURES_H_ diff --git a/chromium/components/ntp_snippets/pref_names.cc b/chromium/components/ntp_snippets/pref_names.cc index fb5582a6725..bea67c36392 100644 --- a/chromium/components/ntp_snippets/pref_names.cc +++ b/chromium/components/ntp_snippets/pref_names.cc @@ -13,18 +13,25 @@ const char kRemoteSuggestionCategories[] = "ntp_snippets.remote_categories"; const char kSnippetLastFetchAttempt[] = "ntp_snippets.last_fetch_attempt"; -const char kSnippetSoftFetchingIntervalWifi[] = - "ntp_snippets.soft_fetching_interval_wifi"; - -const char kSnippetSoftFetchingIntervalFallback[] = - "ntp_snippets.soft_fetching_interval_fallback"; - +// For backwards compatibility, we do not rename the "fetching_" prefs (should +// be "persistent_fetching_"). const char kSnippetPersistentFetchingIntervalWifi[] = "ntp_snippets.fetching_interval_wifi"; - const char kSnippetPersistentFetchingIntervalFallback[] = "ntp_snippets.fetching_interval_fallback"; +const char kSnippetStartupFetchingIntervalWifi[] = + "ntp_snippets.startup_fetching_interval_wifi"; +const char kSnippetStartupFetchingIntervalFallback[] = + "ntp_snippets.startup_fetching_interval_fallback"; + +// For backwards compatibility, we do not rename the "soft_fetching_" prefs +// (should be "shown_fetching_"). +const char kSnippetShownFetchingIntervalWifi[] = + "ntp_snippets.soft_fetching_interval_wifi"; +const char kSnippetShownFetchingIntervalFallback[] = + "ntp_snippets.soft_fetching_interval_fallback"; + const char kSnippetFetcherRequestCount[] = "ntp.request_throttler.suggestion_fetcher.count"; const char kSnippetFetcherInteractiveRequestCount[] = diff --git a/chromium/components/ntp_snippets/pref_names.h b/chromium/components/ntp_snippets/pref_names.h index 9826eb9e9ca..fe3e198c83e 100644 --- a/chromium/components/ntp_snippets/pref_names.h +++ b/chromium/components/ntp_snippets/pref_names.h @@ -18,21 +18,25 @@ extern const char kRemoteSuggestionCategories[]; // The pref name for the last time when a background fetch was attempted. extern const char kSnippetLastFetchAttempt[]; -// The pref name for the currently applied minimal interval between two -// successive soft background fetches that react to user activity (such as -// opening Chrome) when there is a WiFi connectivity. -extern const char kSnippetSoftFetchingIntervalWifi[]; -// The pref name for the currently applied minimal interval between two -// successive soft background fetches that react to user activity (such as -// opening Chrome) when there is no WiFi connectivity. -extern const char kSnippetSoftFetchingIntervalFallback[]; - -// The pref name for the currently-scheduled background fetching interval when -// there is WiFi connectivity. + +// Pref names for minimal intervals between two successive background fetches. +// +// The prefs store *currently applied* minimal intervals. For each trigger type +// there are two intervals stored in prefs: +// - "Wifi" for situations with Wifi / unmetered network connectivity, and +// - "Fallback" for situations with only non-Wifi / metered network. +// We check "meteredness" of the current network only on platforms that support +// that, notably Android; we use WiFi as the proxy of being unmetered elsewhere. +// +// Intervals for trigger type 1: wake-up of the persistent scheduler. extern const char kSnippetPersistentFetchingIntervalWifi[]; -// The pref name for the currently-scheduled background fetching interval when -// there is no WiFi connectivity. extern const char kSnippetPersistentFetchingIntervalFallback[]; +// Intervals for trigger type 2: browser started-up (both cold and warm start). +extern const char kSnippetStartupFetchingIntervalWifi[]; +extern const char kSnippetStartupFetchingIntervalFallback[]; +// Intervals for trigger type 3: suggestions shown to the user. +extern const char kSnippetShownFetchingIntervalWifi[]; +extern const char kSnippetShownFetchingIntervalFallback[]; // The pref name for today's count of RemoteSuggestionsFetcher requests, so far. extern const char kSnippetFetcherRequestCount[]; diff --git a/chromium/components/ntp_snippets/reading_list/reading_list_distillation_state_util.cc b/chromium/components/ntp_snippets/reading_list/reading_list_distillation_state_util.cc deleted file mode 100644 index e7361ad1300..00000000000 --- a/chromium/components/ntp_snippets/reading_list/reading_list_distillation_state_util.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2017 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/ntp_snippets/reading_list/reading_list_distillation_state_util.h" - -#include "base/logging.h" - -namespace ntp_snippets { - -ReadingListEntry::DistillationState ReadingListStateFromSuggestionState( - ReadingListSuggestionExtra::ReadingListSuggestionDistilledState - distilled_state) { - switch (distilled_state) { - case ReadingListSuggestionExtra::ReadingListSuggestionDistilledState:: - PENDING: - return ReadingListEntry::WAITING; - case ReadingListSuggestionExtra::ReadingListSuggestionDistilledState:: - SUCCESS: - return ReadingListEntry::PROCESSED; - case ReadingListSuggestionExtra::ReadingListSuggestionDistilledState:: - FAILURE: - return ReadingListEntry::DISTILLATION_ERROR; - } - NOTREACHED(); - return ReadingListEntry::PROCESSING; -} - -ReadingListSuggestionExtra::ReadingListSuggestionDistilledState -SuggestionStateFromReadingListState( - ReadingListEntry::DistillationState distilled_state) { - switch (distilled_state) { - case ReadingListEntry::WILL_RETRY: - case ReadingListEntry::PROCESSING: - case ReadingListEntry::WAITING: - return ReadingListSuggestionExtra::ReadingListSuggestionDistilledState:: - PENDING; - case ReadingListEntry::PROCESSED: - return ReadingListSuggestionExtra::ReadingListSuggestionDistilledState:: - SUCCESS; - case ReadingListEntry::DISTILLATION_ERROR: - return ReadingListSuggestionExtra::ReadingListSuggestionDistilledState:: - FAILURE; - } - NOTREACHED(); - return ReadingListSuggestionExtra::ReadingListSuggestionDistilledState:: - PENDING; -} - -} // namespace ntp_snippets diff --git a/chromium/components/ntp_snippets/reading_list/reading_list_distillation_state_util.h b/chromium/components/ntp_snippets/reading_list/reading_list_distillation_state_util.h deleted file mode 100644 index a00553fc98a..00000000000 --- a/chromium/components/ntp_snippets/reading_list/reading_list_distillation_state_util.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 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_NTP_SNIPPETS_READING_LIST_READING_LIST_DISTILLATION_STATE_UTIL_H_ -#define COMPONENTS_NTP_SNIPPETS_READING_LIST_READING_LIST_DISTILLATION_STATE_UTIL_H_ - -#include "components/ntp_snippets/content_suggestion.h" -#include "components/reading_list/core/reading_list_entry.h" - -namespace ntp_snippets { - -ReadingListEntry::DistillationState ReadingListStateFromSuggestionState( - ReadingListSuggestionExtra::ReadingListSuggestionDistilledState - distilled_state); - -ReadingListSuggestionExtra::ReadingListSuggestionDistilledState -SuggestionStateFromReadingListState( - ReadingListEntry::DistillationState distilled_state); - -} // namespace ntp_snippets - -#endif // COMPONENTS_NTP_SNIPPETS_READING_LIST_READING_LIST_DISTILLATION_STATE_UTIL_H_ diff --git a/chromium/components/ntp_snippets/reading_list/reading_list_suggestions_provider.cc b/chromium/components/ntp_snippets/reading_list/reading_list_suggestions_provider.cc index ed11ed83fda..9bf791c4857 100644 --- a/chromium/components/ntp_snippets/reading_list/reading_list_suggestions_provider.cc +++ b/chromium/components/ntp_snippets/reading_list/reading_list_suggestions_provider.cc @@ -11,8 +11,8 @@ #include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" #include "components/ntp_snippets/category.h" -#include "components/ntp_snippets/reading_list/reading_list_distillation_state_util.h" #include "components/reading_list/core/reading_list_entry.h" #include "components/reading_list/core/reading_list_model.h" #include "components/strings/grit/components_strings.h" @@ -218,10 +218,16 @@ ContentSuggestion ReadingListSuggestionsProvider::ConvertEntry( } suggestion.set_publisher_name( url_formatter::FormatUrl(entry->URL().GetOrigin())); + int64_t entry_time = entry->DistillationTime(); + if (entry_time == 0) { + entry_time = entry->CreationTime(); + } + + suggestion.set_publish_date( + base::Time::FromDoubleT(entry_time / base::Time::kMicrosecondsPerSecond)); auto extra = base::MakeUnique<ReadingListSuggestionExtra>(); - extra->distilled_state = - SuggestionStateFromReadingListState(entry->DistilledState()); + extra->distilled = entry->DistilledState() == ReadingListEntry::PROCESSED; extra->favicon_page_url = entry->DistilledURL().is_valid() ? entry->DistilledURL() : entry->URL(); suggestion.set_reading_list_suggestion_extra(std::move(extra)); diff --git a/chromium/components/ntp_snippets/remote/fetch.py b/chromium/components/ntp_snippets/remote/fetch.py index 48f448a3ec7..aa753f3e7e4 100755 --- a/chromium/components/ntp_snippets/remote/fetch.py +++ b/chromium/components/ntp_snippets/remote/fetch.py @@ -195,9 +195,9 @@ def Authenticate(args, headers, client_id, client_secret): creds.apply(headers) -def PrintShortResponse(r): +def PrintShortResponse(j): now = datetime.datetime.now() - for category in r.json()["categories"]: + for category in j["categories"]: print("%s: " % category["localizedTitle"]) for suggestion in category["suggestions"]: attribution = suggestion["attribution"] diff --git a/chromium/components/ntp_snippets/remote/json_request.cc b/chromium/components/ntp_snippets/remote/json_request.cc index 3f7af6e8b2b..2ea9f4f7d57 100644 --- a/chromium/components/ntp_snippets/remote/json_request.cc +++ b/chromium/components/ntp_snippets/remote/json_request.cc @@ -52,8 +52,6 @@ namespace { // Variation parameter for disabling the retry. const char kBackground5xxRetriesName[] = "background_5xx_retries_count"; -const int kMaxExcludedIds = 100; - // Variation parameter for sending LanguageModel info to the server. const char kSendTopLanguagesName[] = "send_top_languages"; @@ -321,9 +319,6 @@ std::string JsonRequest::Builder::BuildBody() const { auto excluded = base::MakeUnique<base::ListValue>(); for (const auto& id : params_.excluded_ids) { excluded->AppendString(id); - if (excluded->GetSize() >= kMaxExcludedIds) { - break; - } } request->Set("excludedSuggestionIds", std::move(excluded)); diff --git a/chromium/components/ntp_snippets/remote/json_request_unittest.cc b/chromium/components/ntp_snippets/remote/json_request_unittest.cc index d19f2c8b812..3791ccd5059 100644 --- a/chromium/components/ntp_snippets/remote/json_request_unittest.cc +++ b/chromium/components/ntp_snippets/remote/json_request_unittest.cc @@ -148,7 +148,7 @@ TEST_F(JsonRequestTest, BuildRequestUnauthenticated) { "}")); } -TEST_F(JsonRequestTest, BuildRequestExcludedIds) { +TEST_F(JsonRequestTest, ShouldNotTruncateExcludedIdsList) { JsonRequest::Builder builder; RequestParams params; params.interactive_request = false; @@ -180,9 +180,27 @@ TEST_F(JsonRequestTest, BuildRequestExcludedIds) { " \"080\", \"081\", \"082\", \"083\", \"084\"," " \"085\", \"086\", \"087\", \"088\", \"089\"," " \"090\", \"091\", \"092\", \"093\", \"094\"," - " \"095\", \"096\", \"097\", \"098\", \"099\"" - // Truncated to 100 entries. Currently, they happen to - // be those lexically first. + " \"095\", \"096\", \"097\", \"098\", \"099\"," + " \"100\", \"101\", \"102\", \"103\", \"104\"," + " \"105\", \"106\", \"107\", \"108\", \"109\"," + " \"110\", \"111\", \"112\", \"113\", \"114\"," + " \"115\", \"116\", \"117\", \"118\", \"119\"," + " \"120\", \"121\", \"122\", \"123\", \"124\"," + " \"125\", \"126\", \"127\", \"128\", \"129\"," + " \"130\", \"131\", \"132\", \"133\", \"134\"," + " \"135\", \"136\", \"137\", \"138\", \"139\"," + " \"140\", \"141\", \"142\", \"143\", \"144\"," + " \"145\", \"146\", \"147\", \"148\", \"149\"," + " \"150\", \"151\", \"152\", \"153\", \"154\"," + " \"155\", \"156\", \"157\", \"158\", \"159\"," + " \"160\", \"161\", \"162\", \"163\", \"164\"," + " \"165\", \"166\", \"167\", \"168\", \"169\"," + " \"170\", \"171\", \"172\", \"173\", \"174\"," + " \"175\", \"176\", \"177\", \"178\", \"179\"," + " \"180\", \"181\", \"182\", \"183\", \"184\"," + " \"185\", \"186\", \"187\", \"188\", \"189\"," + " \"190\", \"191\", \"192\", \"193\", \"194\"," + " \"195\", \"196\", \"197\", \"198\", \"199\"" " ]," " \"userActivenessClass\": \"ACTIVE_NTP_USER\"" "}")); diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc index 7607efe00ca..e5ffb4d0821 100644 --- a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc +++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc @@ -30,6 +30,7 @@ #include "components/variations/entropy_provider.h" #include "components/variations/variations_params_manager.h" #include "google_apis/gaia/fake_oauth2_token_service_delegate.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" @@ -182,14 +183,16 @@ class DelegateCallingTestURLFetcherFactory int id, const GURL& url, net::URLFetcher::RequestType request_type, - net::URLFetcherDelegate* d) override { + net::URLFetcherDelegate* d, + net::NetworkTrafficAnnotationTag traffic_annotation) override { if (GetFetcherByID(id)) { LOG(WARNING) << "The ID " << id << " was already assigned to a fetcher." << "Its delegate will thereforde be called right now."; DropAndCallDelegate(id); } fetchers_.push_back(id); - return TestURLFetcherFactory::CreateURLFetcher(id, url, request_type, d); + return TestURLFetcherFactory::CreateURLFetcher(id, url, request_type, d, + traffic_annotation); } // Returns the raw pointer of the last created URL fetcher. @@ -235,7 +238,8 @@ class FailingFakeURLFetcherFactory : public net::URLFetcherFactory { int id, const GURL& url, net::URLFetcher::RequestType request_type, - net::URLFetcherDelegate* d) override { + net::URLFetcherDelegate* d, + net::NetworkTrafficAnnotationTag traffic_annotation) override { return base::MakeUnique<net::FakeURLFetcher>( url, d, /*response_data=*/std::string(), net::HTTP_NOT_FOUND, net::URLRequestStatus::FAILED); diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc index 2c637e3a57a..a3a8b9f6873 100644 --- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc +++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc @@ -33,6 +33,7 @@ #include "components/prefs/pref_service.h" #include "components/strings/grit/components_strings.h" #include "components/variations/variations_associated_data.h" +#include "net/traffic_annotation/network_traffic_annotation.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image.h" @@ -47,6 +48,10 @@ const int kMaxSuggestionCount = 10; // Number of archived suggestions we keep around in memory. const int kMaxArchivedSuggestionCount = 200; +// Maximal number of dismissed suggestions to exclude when fetching new +// suggestions from the server. This limit exists to decrease data usage. +const int kMaxExcludedDismissedIds = 100; + // Keys for storing CategoryContent info in prefs. const char kCategoryContentId[] = "id"; const char kCategoryContentTitle[] = "title"; @@ -192,6 +197,17 @@ void CallWithEmptyResults(const FetchDoneCallback& callback, callback.Run(status, std::vector<ContentSuggestion>()); } +void AddDismissedIdsToRequest(const RemoteSuggestion::PtrVector& dismissed, + RequestParams* request_params) { + // The latest ids are added first, because they are more relevant. + for (auto it = dismissed.rbegin(); it != dismissed.rend(); ++it) { + if (request_params->excluded_ids.size() == kMaxExcludedDismissedIds) { + break; + } + request_params->excluded_ids.insert((*it)->id()); + } +} + } // namespace CachedImageFetcher::CachedImageFetcher( @@ -288,10 +304,34 @@ void CachedImageFetcher::FetchImageFromNetwork( return; } + net::NetworkTrafficAnnotationTag traffic_annotation = + net::DefineNetworkTrafficAnnotation("remote_suggestions_provider", R"( + semantics { + sender: "Content Suggestion Thumbnail Fetch" + description: + "Retrieves thumbnails for content suggestions, for display on the " + "New Tab page or Chrome Home." + trigger: + "Triggered when the user looks at a content suggestion (and its " + "thumbnail isn't cached yet)." + data: "None." + destination: GOOGLE_OWNED_SERVICE + } + policy { + cookies_allowed: false + setting: "Currently not available, but in progress: crbug.com/703684" + chrome_policy { + NTPContentSuggestionsEnabled { + policy_options {mode: MANDATORY} + NTPContentSuggestionsEnabled: false + } + } + })"); image_fetcher_->StartOrQueueNetworkRequest( suggestion_id.id_within_category(), url, base::Bind(&CachedImageFetcher::OnImageDecodingDone, - base::Unretained(this), callback)); + base::Unretained(this), callback), + traffic_annotation); } RemoteSuggestionsProviderImpl::RemoteSuggestionsProviderImpl( @@ -408,7 +448,7 @@ void RemoteSuggestionsProviderImpl::FetchSuggestions( MarkEmptyCategoriesAsLoading(); - RequestParams params = BuildFetchParams(); + RequestParams params = BuildFetchParams(/*fetched_category=*/base::nullopt); params.interactive_request = interactive_request; suggestions_fetcher_->FetchSnippets( params, @@ -441,7 +481,7 @@ void RemoteSuggestionsProviderImpl::Fetch( }, base::Unretained(remote_suggestions_scheduler_), callback); - RequestParams params = BuildFetchParams(); + RequestParams params = BuildFetchParams(category); params.excluded_ids.insert(known_suggestion_ids.begin(), known_suggestion_ids.end()); params.interactive_request = true; @@ -454,15 +494,23 @@ void RemoteSuggestionsProviderImpl::Fetch( } // Builds default fetcher params. -RequestParams RemoteSuggestionsProviderImpl::BuildFetchParams() const { +RequestParams RemoteSuggestionsProviderImpl::BuildFetchParams( + base::Optional<Category> fetched_category) const { RequestParams result; result.language_code = application_language_code_; result.count_to_fetch = kMaxSuggestionCount; + // If this is a fetch for a specific category, its dismissed suggestions are + // added first to truncate them less. + if (fetched_category.has_value()) { + DCHECK(category_contents_.count(*fetched_category)); + AddDismissedIdsToRequest( + category_contents_.find(*fetched_category)->second.dismissed, &result); + } for (const auto& map_entry : category_contents_) { - const CategoryContent& content = map_entry.second; - for (const auto& dismissed_suggestion : content.dismissed) { - result.excluded_ids.insert(dismissed_suggestion->id()); + if (fetched_category.has_value() && map_entry.first == *fetched_category) { + continue; } + AddDismissedIdsToRequest(map_entry.second.dismissed, &result); } return result; } @@ -684,31 +732,12 @@ void RemoteSuggestionsProviderImpl::OnFetchMoreFinished( UpdateCategoryInfo(category, fetched_category.info); SanitizeReceivedSuggestions(existing_content->dismissed, &fetched_category.suggestions); - // We compute the result now before modifying |fetched_category.suggestions|. - // However, we wait with notifying the caller until the end of the method when - // all state is updated. std::vector<ContentSuggestion> result = ConvertToContentSuggestions(category, fetched_category.suggestions); - - // Fill up the newly fetched suggestions with existing ones, store them, and - // notify observers about new data. - while (fetched_category.suggestions.size() < - static_cast<size_t>(kMaxSuggestionCount) && - !existing_content->suggestions.empty()) { - fetched_category.suggestions.emplace( - fetched_category.suggestions.begin(), - std::move(existing_content->suggestions.back())); - existing_content->suggestions.pop_back(); - } - std::vector<std::string> to_dismiss = - *GetSuggestionIDVector(existing_content->suggestions); - for (const auto& id : to_dismiss) { - DismissSuggestionFromCategoryContent(existing_content, id); - } - DCHECK(existing_content->suggestions.empty()); - - IntegrateSuggestions(existing_content, - std::move(fetched_category.suggestions)); + // Store the additional suggestions into the archive to be able to fetch + // images and favicons for them. Note that ArchiveSuggestions clears + // |fetched_category.suggestions|. + ArchiveSuggestions(existing_content, &fetched_category.suggestions); // TODO(tschumann): We should properly honor the existing category state, // e.g. to make sure we don't serve results after the sign-out. Revisit this: @@ -716,7 +745,6 @@ void RemoteSuggestionsProviderImpl::OnFetchMoreFinished( // status? UpdateCategoryStatus(category, CategoryStatus::AVAILABLE); fetching_callback.Run(Status::Success(), std::move(result)); - NotifyNewSuggestions(category, *existing_content); } void RemoteSuggestionsProviderImpl::OnFetchFinished( diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h index 2540c3abae3..d84d00d4548 100644 --- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h +++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h @@ -17,6 +17,7 @@ #include "base/callback_forward.h" #include "base/gtest_prod_util.h" #include "base/macros.h" +#include "base/optional.h" #include "base/time/clock.h" #include "base/time/time.h" #include "components/image_fetcher/core/image_fetcher_delegate.h" @@ -409,7 +410,9 @@ class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider { void RestoreCategoriesFromPrefs(); void StoreCategoriesToPrefs(); - RequestParams BuildFetchParams() const; + // Absence of fetched category corresponds to fetching all categories. + RequestParams BuildFetchParams( + base::Optional<Category> fetched_category) const; void MarkEmptyCategoriesAsLoading(); diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc index ee6175526de..0759b4f1cb6 100644 --- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc +++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc @@ -16,6 +16,7 @@ #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -48,6 +49,7 @@ #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" #include "components/signin/core/browser/fake_signin_manager.h" #include "components/variations/variations_params_manager.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" @@ -336,7 +338,8 @@ class FailingFakeURLFetcherFactory : public net::URLFetcherFactory { int id, const GURL& url, net::URLFetcher::RequestType request_type, - net::URLFetcherDelegate* d) override { + net::URLFetcherDelegate* d, + net::NetworkTrafficAnnotationTag traffic_annotation) override { return base::MakeUnique<net::FakeURLFetcher>( url, d, /*response_data=*/std::string(), net::HTTP_NOT_FOUND, net::URLRequestStatus::FAILED); @@ -350,10 +353,11 @@ class MockImageFetcher : public ImageFetcher { MOCK_METHOD1(SetImageDownloadLimit, void(base::Optional<int64_t> max_download_bytes)); MOCK_METHOD1(SetDesiredImageFrameSize, void(const gfx::Size&)); - MOCK_METHOD3(StartOrQueueNetworkRequest, + MOCK_METHOD4(StartOrQueueNetworkRequest, void(const std::string&, const GURL&, - const ImageFetcherCallback&)); + const ImageFetcherCallback&, + const net::NetworkTrafficAnnotationTag&)); MOCK_METHOD0(GetImageDecoder, image_fetcher::ImageDecoder*()); }; @@ -1001,7 +1005,7 @@ TEST_F(RemoteSuggestionsProviderImplTest, LoadsAdditionalSuggestions) { // Verify we can resolve the image of the new suggestions. ServeImageCallback cb = base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting()); - EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) + EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _)) .Times(2) .WillRepeatedly(WithArgs<0, 2>(Invoke(CreateFunctor(cb)))); image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1)); @@ -1012,12 +1016,6 @@ TEST_F(RemoteSuggestionsProviderImplTest, LoadsAdditionalSuggestions) { image = FetchImage(service.get(), MakeArticleID("http://second")); EXPECT_FALSE(image.IsEmpty()); EXPECT_EQ(1, image.Width()); - - // Verify that the observer received the update as well. We should see the - // newly-fetched items filled up with existing ones. - EXPECT_THAT(observer().SuggestionsForCategory(articles_category()), - ElementsAre(IdWithinCategoryEq("http://first"), - IdWithinCategoryEq("http://second"))); } // The tests TestMergingFetchedMoreSuggestionsFillup and @@ -1025,14 +1023,14 @@ TEST_F(RemoteSuggestionsProviderImplTest, LoadsAdditionalSuggestions) { // story: // 1) fetch suggestions in NTP A // 2) fetch more suggestions in NTP A. -// 3) open new NTP B: See the last 10 results visible in step 2). -// 4) fetch more suggestions in NTP B. Make sure no results from step 1) which -// were superseded in step 2) get merged back in again. +// 3) open new NTP B: See the first 10 results from step 1). +// 4) fetch more suggestions in NTP B. Make sure the results are independent +// from step 2) // TODO(tschumann): Test step 4) on a higher level instead of peeking into the // internal 'dismissed' data. The proper check is to make sure we tell the // backend to exclude these suggestions. TEST_F(RemoteSuggestionsProviderImplTest, - TestMergingFetchedMoreSuggestionsFillup) { + FewMoreFetchedSuggestionsShouldNotInterfere) { auto service = MakeSuggestionsProvider(/*set_empty_response=*/false); LoadFromJSONString(service.get(), GetTestJson({GetSuggestionWithUrl("http://id-1"), @@ -1071,65 +1069,37 @@ TEST_F(RemoteSuggestionsProviderImplTest, "http://id-9", "http://id-10"}, expect_receiving_two_new_suggestions); - // Verify that the observer received the update as well. We should see the - // newly-fetched items filled up with existing ones. The merging is done - // mimicking a scrolling behavior. + // Verify that the observer still has the old set. EXPECT_THAT( observer().SuggestionsForCategory(articles_category()), ElementsAre( + IdWithinCategoryEq("http://id-1"), IdWithinCategoryEq("http://id-2"), IdWithinCategoryEq("http://id-3"), IdWithinCategoryEq("http://id-4"), IdWithinCategoryEq("http://id-5"), IdWithinCategoryEq("http://id-6"), IdWithinCategoryEq("http://id-7"), IdWithinCategoryEq("http://id-8"), - IdWithinCategoryEq("http://id-9"), IdWithinCategoryEq("http://id-10"), - IdWithinCategoryEq("http://more-id-1"), - IdWithinCategoryEq("http://more-id-2"))); - // Verify the superseded suggestions got marked as dismissed. - EXPECT_THAT(service->GetDismissedSuggestionsForTesting(articles_category()), - ElementsAre(IdEq("http://id-1"), IdEq("http://id-2"))); -} - -TEST_F(RemoteSuggestionsProviderImplTest, - ClearHistoryShouldDeleteArchivedSuggestions) { - auto service = MakeSuggestionsProvider(/*set_empty_response=*/false); - // First get suggestions into the archived state which happens through - // subsequent fetches. Then we verify the entries are gone from the 'archived' - // state by trying to load their images (and we shouldn't even know the URLs - // anymore). - LoadFromJSONString(service.get(), - GetTestJson({GetSuggestionWithUrl("http://id-1"), - GetSuggestionWithUrl("http://id-2")})); - LoadFromJSONString(service.get(), - GetTestJson({GetSuggestionWithUrl("http://new-id-1"), - GetSuggestionWithUrl("http://new-id-2")})); - // Make sure images of both batches are available. This is to sanity check our - // assumptions for the test are right. - ServeImageCallback cb = - base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting()); - EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) - .Times(2) - .WillRepeatedly(WithArgs<0, 2>(Invoke(CreateFunctor(cb)))); - image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1)); - gfx::Image image = FetchImage(service.get(), MakeArticleID("http://id-1")); - ASSERT_FALSE(image.IsEmpty()); - ASSERT_EQ(1, image.Width()); - image = FetchImage(service.get(), MakeArticleID("http://new-id-1")); - ASSERT_FALSE(image.IsEmpty()); - ASSERT_EQ(1, image.Width()); - - service->ClearHistory(base::Time::UnixEpoch(), base::Time::Max(), - base::Callback<bool(const GURL& url)>()); + IdWithinCategoryEq("http://id-9"), + IdWithinCategoryEq("http://id-10"))); - // Make sure images of both batches are gone. - // Verify we cannot resolve the image of the new suggestions. - image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1)); - EXPECT_TRUE( - FetchImage(service.get(), MakeArticleID("http://id-1")).IsEmpty()); - EXPECT_TRUE( - FetchImage(service.get(), MakeArticleID("http://new-id-1")).IsEmpty()); + // No interference from previous Fetch more: we can receive two other ones. + expect_receiving_two_new_suggestions = + base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) { + ASSERT_THAT(suggestions, SizeIs(2)); + EXPECT_THAT(suggestions[0], IdWithinCategoryEq("http://more-id-3")); + EXPECT_THAT(suggestions[1], IdWithinCategoryEq("http://more-id-4")); + }); + LoadMoreFromJSONString( + service.get(), articles_category(), + GetTestJson({GetSuggestionWithUrl("http://more-id-3"), + GetSuggestionWithUrl("http://more-id-4")}), + /*known_ids=*/ + {"http://id-1", "http://id-2", "http://id-3", "http://id-4", + "http://id-5", "http://id-6", "http://id-7", "http://id-8", + "http://id-9", "http://id-10"}, + expect_receiving_two_new_suggestions); } TEST_F(RemoteSuggestionsProviderImplTest, - TestMergingFetchedMoreSuggestionsReplaceAll) { + TenMoreFetchedSuggestionsShouldNotInterfere) { auto service = MakeSuggestionsProvider(/*set_empty_response=*/false); LoadFromJSONString(service.get(), GetTestJson({GetSuggestionWithUrl("http://id-1"), @@ -1183,24 +1153,88 @@ TEST_F(RemoteSuggestionsProviderImplTest, "http://id-5", "http://id-6", "http://id-7", "http://id-8", "http://id-9", "http://id-10"}, expect_receiving_ten_new_suggestions); - EXPECT_THAT(observer().SuggestionsForCategory(articles_category()), - ElementsAre(IdWithinCategoryEq("http://more-id-1"), - IdWithinCategoryEq("http://more-id-2"), - IdWithinCategoryEq("http://more-id-3"), - IdWithinCategoryEq("http://more-id-4"), - IdWithinCategoryEq("http://more-id-5"), - IdWithinCategoryEq("http://more-id-6"), - IdWithinCategoryEq("http://more-id-7"), - IdWithinCategoryEq("http://more-id-8"), - IdWithinCategoryEq("http://more-id-9"), - IdWithinCategoryEq("http://more-id-10"))); - // Verify the superseded suggestions got marked as dismissed. EXPECT_THAT( - service->GetDismissedSuggestionsForTesting(articles_category()), - ElementsAre(IdEq("http://id-1"), IdEq("http://id-2"), IdEq("http://id-3"), - IdEq("http://id-4"), IdEq("http://id-5"), IdEq("http://id-6"), - IdEq("http://id-7"), IdEq("http://id-8"), IdEq("http://id-9"), - IdEq("http://id-10"))); + observer().SuggestionsForCategory(articles_category()), + ElementsAre( + IdWithinCategoryEq("http://id-1"), IdWithinCategoryEq("http://id-2"), + IdWithinCategoryEq("http://id-3"), IdWithinCategoryEq("http://id-4"), + IdWithinCategoryEq("http://id-5"), IdWithinCategoryEq("http://id-6"), + IdWithinCategoryEq("http://id-7"), IdWithinCategoryEq("http://id-8"), + IdWithinCategoryEq("http://id-9"), + IdWithinCategoryEq("http://id-10"))); + + // This time, test receiving the same set. + expect_receiving_ten_new_suggestions = + base::Bind([](Status status, std::vector<ContentSuggestion> suggestions) { + EXPECT_THAT(suggestions, + ElementsAre(IdWithinCategoryEq("http://more-id-1"), + IdWithinCategoryEq("http://more-id-2"), + IdWithinCategoryEq("http://more-id-3"), + IdWithinCategoryEq("http://more-id-4"), + IdWithinCategoryEq("http://more-id-5"), + IdWithinCategoryEq("http://more-id-6"), + IdWithinCategoryEq("http://more-id-7"), + IdWithinCategoryEq("http://more-id-8"), + IdWithinCategoryEq("http://more-id-9"), + IdWithinCategoryEq("http://more-id-10"))); + }); + LoadMoreFromJSONString( + service.get(), articles_category(), + GetTestJson({GetSuggestionWithUrl("http://more-id-1"), + GetSuggestionWithUrl("http://more-id-2"), + GetSuggestionWithUrl("http://more-id-3"), + GetSuggestionWithUrl("http://more-id-4"), + GetSuggestionWithUrl("http://more-id-5"), + GetSuggestionWithUrl("http://more-id-6"), + GetSuggestionWithUrl("http://more-id-7"), + GetSuggestionWithUrl("http://more-id-8"), + GetSuggestionWithUrl("http://more-id-9"), + GetSuggestionWithUrl("http://more-id-10")}), + /*known_ids=*/ + {"http://id-1", "http://id-2", "http://id-3", "http://id-4", + "http://id-5", "http://id-6", "http://id-7", "http://id-8", + "http://id-9", "http://id-10"}, + expect_receiving_ten_new_suggestions); +} + +TEST_F(RemoteSuggestionsProviderImplTest, + ClearHistoryShouldDeleteArchivedSuggestions) { + auto service = MakeSuggestionsProvider(/*set_empty_response=*/false); + // First get suggestions into the archived state which happens through + // subsequent fetches. Then we verify the entries are gone from the 'archived' + // state by trying to load their images (and we shouldn't even know the URLs + // anymore). + LoadFromJSONString(service.get(), + GetTestJson({GetSuggestionWithUrl("http://id-1"), + GetSuggestionWithUrl("http://id-2")})); + LoadFromJSONString(service.get(), + GetTestJson({GetSuggestionWithUrl("http://new-id-1"), + GetSuggestionWithUrl("http://new-id-2")})); + // Make sure images of both batches are available. This is to sanity check our + // assumptions for the test are right. + ServeImageCallback cb = + base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting()); + EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _)) + .Times(2) + .WillRepeatedly(WithArgs<0, 2>(Invoke(CreateFunctor(cb)))); + image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1)); + gfx::Image image = FetchImage(service.get(), MakeArticleID("http://id-1")); + ASSERT_FALSE(image.IsEmpty()); + ASSERT_EQ(1, image.Width()); + image = FetchImage(service.get(), MakeArticleID("http://new-id-1")); + ASSERT_FALSE(image.IsEmpty()); + ASSERT_EQ(1, image.Width()); + + service->ClearHistory(base::Time::UnixEpoch(), base::Time::Max(), + base::Callback<bool(const GURL& url)>()); + + // Make sure images of both batches are gone. + // Verify we cannot resolve the image of the new suggestions. + image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1)); + EXPECT_TRUE( + FetchImage(service.get(), MakeArticleID("http://id-1")).IsEmpty()); + EXPECT_TRUE( + FetchImage(service.get(), MakeArticleID("http://new-id-1")).IsEmpty()); } // TODO(tschumann): We don't have test making sure the RemoteSuggestionsFetcher @@ -1345,7 +1379,7 @@ TEST_F(RemoteSuggestionsProviderImplTest, Dismiss) { // Load the image to store it in the database. ServeImageCallback cb = base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting()); - EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) + EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _)) .WillOnce(WithArgs<0, 2>(Invoke(CreateFunctor(cb)))); image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1)); gfx::Image image = FetchImage(service.get(), MakeArticleID(kSuggestionUrl)); @@ -1450,7 +1484,7 @@ TEST_F(RemoteSuggestionsProviderImplTest, RemoveExpiredDismissedContent) { // fetching expectations. ServeImageCallback cb = base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting()); - EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) + EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _)) .WillOnce(WithArgs<0, 2>(Invoke(CreateFunctor(cb)))); image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1)); gfx::Image image = FetchImage(service.get(), MakeArticleID(kSuggestionUrl)); @@ -1624,7 +1658,7 @@ TEST_F(RemoteSuggestionsProviderImplTest, ImageReturnedWithTheSameId) { base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting()); { InSequence s; - EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) + EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _)) .WillOnce(WithArgs<0, 2>(Invoke(CreateFunctor(cb)))); EXPECT_CALL(image_fetched, Call(_)).WillOnce(SaveArg<0>(&image)); } @@ -1731,7 +1765,7 @@ TEST_F(RemoteSuggestionsProviderImplTest, ShouldClearOrphanedImagesOnRestart) { ServeImageCallback cb = base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting()); - EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) + EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _, _)) .WillOnce(WithArgs<0, 2>(Invoke(CreateFunctor(cb)))); image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1)); diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc index 4b5a0e10b42..193628a55e4 100644 --- a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc +++ b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc @@ -31,23 +31,27 @@ namespace { // The FetchingInterval enum specifies overlapping time intervals that are used // for scheduling the next remote suggestion fetch. Therefore a timer is created // for each interval. Initially all the timers are started at the same time. -// Fetches are only performed when conditions associated with the intervals are -// met: -// - A "soft" fetch is performed only at a moment when the user actively uses -// Chrome after the interval has elapsed and causes a trigger that is currently -// enabled while -// - a "persistent" fetch is initiated automatically by the OS after the -// interval elapses. -// - A "wifi" fetch is performed only when the user is on a connection that -// the OS classifies as unmetered while -// - a "fallback" fetch happens on any connection. +// A fetch for a given interval is only performed at the moment (after the +// interval has elapsed) when the combination of two conditions associated with +// the interval is met. +// 1. Trigger contidion: +// - STARTUP_*: the user starts / switches to Chrome; +// - SHOWN_*: the suggestions surface is shown to the user; +// - PERSISTENT_*: the OS initiates the scheduled fetching task (which has +// been scheduled according to this interval). +// 2. Connectivity condition: +// - *_WIFI: the user is on a connection that the OS classifies as unmetered; +// - *_FALLBACK: holds for any functional internet connection. +// // If a fetch failed, then only the corresponding timer is reset. The other // timers are not touched. enum class FetchingInterval { PERSISTENT_FALLBACK, PERSISTENT_WIFI, - SOFT_FALLBACK, - SOFT_WIFI, + STARTUP_FALLBACK, + STARTUP_WIFI, + SHOWN_FALLBACK, + SHOWN_WIFI, COUNT }; @@ -57,28 +61,36 @@ enum class FetchingInterval { // The values of each array specify a default time interval for the intervals // defined by the enum FetchingInterval. The default time intervals defined in // the arrays can be overridden using different variation parameters. -const double kDefaultFetchingIntervalHoursRareNtpUser[] = {48.0, 24.0, 8.0, - 4.0}; -const double kDefaultFetchingIntervalHoursActiveNtpUser[] = {24.0, 8.0, 6.0, - 3.0}; +const double kDefaultFetchingIntervalHoursRareNtpUser[] = {192.0, 96.0, 48.0, + 24.0, 12.0, 8.0}; +const double kDefaultFetchingIntervalHoursActiveNtpUser[] = {96.0, 48.0, 48.0, + 24.0, 12.0, 8.0}; const double kDefaultFetchingIntervalHoursActiveSuggestionsConsumer[] = { - 24.0, 6.0, 2.0, 1.0}; + 48.0, 24.0, 24.0, 6.0, 2.0, 1.0}; // Variation parameters than can be used to override the default fetching -// intervals. +// intervals. For backwards compatibility, we do not rename +// - the "fetching_" params (should be "persistent_fetching_"); +// - the "soft_" params (should be "shown_"). const char* kFetchingIntervalParamNameRareNtpUser[] = { "fetching_interval_hours-fallback-rare_ntp_user", "fetching_interval_hours-wifi-rare_ntp_user", + "startup_fetching_interval_hours-fallback-rare_ntp_user", + "startup_fetching_interval_hours-wifi-rare_ntp_user", "soft_fetching_interval_hours-fallback-rare_ntp_user", "soft_fetching_interval_hours-wifi-rare_ntp_user"}; const char* kFetchingIntervalParamNameActiveNtpUser[] = { "fetching_interval_hours-fallback-active_ntp_user", "fetching_interval_hours-wifi-active_ntp_user", + "startup_fetching_interval_hours-fallback-active_ntp_user", + "startup_fetching_interval_hours-wifi-active_ntp_user", "soft_fetching_interval_hours-fallback-active_ntp_user", "soft_fetching_interval_hours-wifi-active_ntp_user"}; const char* kFetchingIntervalParamNameActiveSuggestionsConsumer[] = { "fetching_interval_hours-fallback-active_suggestions_consumer", "fetching_interval_hours-wifi-active_suggestions_consumer", + "startup_fetching_interval_hours-fallback-active_suggestions_consumer", + "startup_fetching_interval_hours-wifi-active_suggestions_consumer", "soft_fetching_interval_hours-fallback-active_suggestions_consumer", "soft_fetching_interval_hours-wifi-active_suggestions_consumer"}; @@ -174,14 +186,14 @@ void ReportTimeUntilFirstSoftTrigger(UserClassifier::UserClass user_class, } } -void ReportTimeUntilSoftFetch(UserClassifier::UserClass user_class, - base::TimeDelta time_until_soft_fetch) { +void ReportTimeUntilShownFetch(UserClassifier::UserClass user_class, + base::TimeDelta time_until_shown_fetch) { switch (user_class) { case UserClassifier::UserClass::RARE_NTP_USER: UMA_HISTOGRAM_CUSTOM_TIMES( "NewTabPage.ContentSuggestions.TimeUntilSoftFetch." "RareNTPUser", - time_until_soft_fetch, base::TimeDelta::FromSeconds(1), + time_until_shown_fetch, base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(7), /*bucket_count=*/50); break; @@ -189,7 +201,7 @@ void ReportTimeUntilSoftFetch(UserClassifier::UserClass user_class, UMA_HISTOGRAM_CUSTOM_TIMES( "NewTabPage.ContentSuggestions.TimeUntilSoftFetch." "ActiveNTPUser", - time_until_soft_fetch, base::TimeDelta::FromSeconds(1), + time_until_shown_fetch, base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(7), /*bucket_count=*/50); break; @@ -197,7 +209,37 @@ void ReportTimeUntilSoftFetch(UserClassifier::UserClass user_class, UMA_HISTOGRAM_CUSTOM_TIMES( "NewTabPage.ContentSuggestions.TimeUntilSoftFetch." "ActiveSuggestionsConsumer", - time_until_soft_fetch, base::TimeDelta::FromSeconds(1), + time_until_shown_fetch, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + } +} + +void ReportTimeUntilStartupFetch(UserClassifier::UserClass user_class, + base::TimeDelta time_until_startup_fetch) { + switch (user_class) { + case UserClassifier::UserClass::RARE_NTP_USER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilStartupFetch." + "RareNTPUser", + time_until_startup_fetch, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + case UserClassifier::UserClass::ACTIVE_NTP_USER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilStartupFetch." + "ActiveNTPUser", + time_until_startup_fetch, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilStartupFetch." + "ActiveSuggestionsConsumer", + time_until_startup_fetch, base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(7), /*bucket_count=*/50); break; @@ -288,8 +330,10 @@ bool RemoteSuggestionsSchedulerImpl::FetchingSchedule::operator==( const FetchingSchedule& other) const { return interval_persistent_wifi == other.interval_persistent_wifi && interval_persistent_fallback == other.interval_persistent_fallback && - interval_soft_wifi == other.interval_soft_wifi && - interval_soft_fallback == other.interval_soft_fallback; + interval_startup_wifi == other.interval_startup_wifi && + interval_startup_fallback == other.interval_startup_fallback && + interval_shown_wifi == other.interval_shown_wifi && + interval_shown_fallback == other.interval_shown_fallback; } bool RemoteSuggestionsSchedulerImpl::FetchingSchedule::operator!=( @@ -300,7 +344,9 @@ bool RemoteSuggestionsSchedulerImpl::FetchingSchedule::operator!=( bool RemoteSuggestionsSchedulerImpl::FetchingSchedule::is_empty() const { return interval_persistent_wifi.is_zero() && interval_persistent_fallback.is_zero() && - interval_soft_wifi.is_zero() && interval_soft_fallback.is_zero(); + interval_startup_wifi.is_zero() && + interval_startup_fallback.is_zero() && interval_shown_wifi.is_zero() && + interval_shown_fallback.is_zero(); } // The TriggerType enum specifies values for the events that can trigger @@ -361,8 +407,11 @@ void RemoteSuggestionsSchedulerImpl::RegisterProfilePrefs( registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalWifi, 0); registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalFallback, 0); - registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalWifi, 0); - registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalFallback, 0); + registry->RegisterInt64Pref(prefs::kSnippetStartupFetchingIntervalWifi, 0); + registry->RegisterInt64Pref(prefs::kSnippetStartupFetchingIntervalFallback, + 0); + registry->RegisterInt64Pref(prefs::kSnippetShownFetchingIntervalWifi, 0); + registry->RegisterInt64Pref(prefs::kSnippetShownFetchingIntervalFallback, 0); registry->RegisterInt64Pref(prefs::kSnippetLastFetchAttempt, 0); // Cleanup procedure in M59. Remove for M62. @@ -488,10 +537,14 @@ RemoteSuggestionsSchedulerImpl::GetDesiredFetchingSchedule() const { GetDesiredFetchingInterval(FetchingInterval::PERSISTENT_WIFI, user_class); schedule.interval_persistent_fallback = GetDesiredFetchingInterval( FetchingInterval::PERSISTENT_FALLBACK, user_class); - schedule.interval_soft_wifi = - GetDesiredFetchingInterval(FetchingInterval::SOFT_WIFI, user_class); - schedule.interval_soft_fallback = - GetDesiredFetchingInterval(FetchingInterval::SOFT_FALLBACK, user_class); + schedule.interval_startup_wifi = + GetDesiredFetchingInterval(FetchingInterval::STARTUP_WIFI, user_class); + schedule.interval_startup_fallback = GetDesiredFetchingInterval( + FetchingInterval::STARTUP_FALLBACK, user_class); + schedule.interval_shown_wifi = + GetDesiredFetchingInterval(FetchingInterval::SHOWN_WIFI, user_class); + schedule.interval_shown_fallback = + GetDesiredFetchingInterval(FetchingInterval::SHOWN_FALLBACK, user_class); return schedule; } @@ -502,10 +555,14 @@ void RemoteSuggestionsSchedulerImpl::LoadLastFetchingSchedule() { schedule_.interval_persistent_fallback = base::TimeDelta::FromInternalValue(profile_prefs_->GetInt64( prefs::kSnippetPersistentFetchingIntervalFallback)); - schedule_.interval_soft_wifi = base::TimeDelta::FromInternalValue( - profile_prefs_->GetInt64(prefs::kSnippetSoftFetchingIntervalWifi)); - schedule_.interval_soft_fallback = base::TimeDelta::FromInternalValue( - profile_prefs_->GetInt64(prefs::kSnippetSoftFetchingIntervalFallback)); + schedule_.interval_startup_wifi = base::TimeDelta::FromInternalValue( + profile_prefs_->GetInt64(prefs::kSnippetStartupFetchingIntervalWifi)); + schedule_.interval_startup_fallback = base::TimeDelta::FromInternalValue( + profile_prefs_->GetInt64(prefs::kSnippetStartupFetchingIntervalFallback)); + schedule_.interval_shown_wifi = base::TimeDelta::FromInternalValue( + profile_prefs_->GetInt64(prefs::kSnippetShownFetchingIntervalWifi)); + schedule_.interval_shown_fallback = base::TimeDelta::FromInternalValue( + profile_prefs_->GetInt64(prefs::kSnippetShownFetchingIntervalFallback)); } void RemoteSuggestionsSchedulerImpl::StoreFetchingSchedule() { @@ -515,10 +572,15 @@ void RemoteSuggestionsSchedulerImpl::StoreFetchingSchedule() { profile_prefs_->SetInt64( prefs::kSnippetPersistentFetchingIntervalFallback, schedule_.interval_persistent_fallback.ToInternalValue()); - profile_prefs_->SetInt64(prefs::kSnippetSoftFetchingIntervalWifi, - schedule_.interval_soft_wifi.ToInternalValue()); - profile_prefs_->SetInt64(prefs::kSnippetSoftFetchingIntervalFallback, - schedule_.interval_soft_fallback.ToInternalValue()); + profile_prefs_->SetInt64(prefs::kSnippetStartupFetchingIntervalWifi, + schedule_.interval_startup_wifi.ToInternalValue()); + profile_prefs_->SetInt64( + prefs::kSnippetStartupFetchingIntervalFallback, + schedule_.interval_startup_fallback.ToInternalValue()); + profile_prefs_->SetInt64(prefs::kSnippetShownFetchingIntervalWifi, + schedule_.interval_shown_wifi.ToInternalValue()); + profile_prefs_->SetInt64(prefs::kSnippetShownFetchingIntervalFallback, + schedule_.interval_shown_fallback.ToInternalValue()); } void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate( @@ -541,7 +603,8 @@ void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate( clock_->Now() - last_fetch_attempt_time); } - if (is_soft && !ShouldRefetchInTheBackgroundNow(last_fetch_attempt_time)) { + if (is_soft && + !ShouldRefetchInTheBackgroundNow(last_fetch_attempt_time, trigger)) { return; } @@ -549,12 +612,20 @@ void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate( return; } - if (is_soft) { - ReportTimeUntilSoftFetch(user_classifier_->GetUserClass(), - clock_->Now() - last_fetch_attempt_time); - } else { - ReportTimeUntilPersistentFetch(user_classifier_->GetUserClass(), - clock_->Now() - last_fetch_attempt_time); + base::TimeDelta diff = clock_->Now() - last_fetch_attempt_time; + switch (trigger) { + case TriggerType::PERSISTENT_SCHEDULER_WAKE_UP: + ReportTimeUntilPersistentFetch(user_classifier_->GetUserClass(), diff); + break; + case TriggerType::NTP_OPENED: + ReportTimeUntilShownFetch(user_classifier_->GetUserClass(), diff); + break; + case TriggerType::BROWSER_FOREGROUNDED: + case TriggerType::BROWSER_COLD_START: + ReportTimeUntilStartupFetch(user_classifier_->GetUserClass(), diff); + break; + case TriggerType::COUNT: + NOTREACHED(); } UMA_HISTOGRAM_ENUMERATION( @@ -568,16 +639,23 @@ void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate( } bool RemoteSuggestionsSchedulerImpl::ShouldRefetchInTheBackgroundNow( - base::Time last_fetch_attempt_time) { + base::Time last_fetch_attempt_time, + TriggerType trigger) { // If we have no persistent scheduler to ask, err on the side of caution. bool wifi = false; if (persistent_scheduler_) { wifi = persistent_scheduler_->IsOnUnmeteredConnection(); } - base::Time first_allowed_fetch_time = - last_fetch_attempt_time + - (wifi ? schedule_.interval_soft_wifi : schedule_.interval_soft_fallback); + base::Time first_allowed_fetch_time = last_fetch_attempt_time; + if (trigger == TriggerType::NTP_OPENED) { + first_allowed_fetch_time += (wifi ? schedule_.interval_shown_wifi + : schedule_.interval_shown_fallback); + } else { + first_allowed_fetch_time += (wifi ? schedule_.interval_startup_wifi + : schedule_.interval_startup_fallback); + } + base::Time now = clock_->Now(); return background_fetches_allowed_after_ <= now && first_allowed_fetch_time <= now; @@ -686,7 +764,8 @@ RemoteSuggestionsSchedulerImpl::GetEnabledTriggerTypes() { std::set<RemoteSuggestionsSchedulerImpl::TriggerType> RemoteSuggestionsSchedulerImpl::GetDefaultEnabledTriggerTypes() { - return {TriggerType::PERSISTENT_SCHEDULER_WAKE_UP, TriggerType::NTP_OPENED}; + return {TriggerType::PERSISTENT_SCHEDULER_WAKE_UP, TriggerType::NTP_OPENED, + TriggerType::BROWSER_COLD_START, TriggerType::BROWSER_FOREGROUNDED}; } } // namespace ntp_snippets diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h index 9921f51489a..3e103624bec 100644 --- a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h +++ b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h @@ -71,8 +71,10 @@ class RemoteSuggestionsSchedulerImpl : public RemoteSuggestionsScheduler { base::TimeDelta interval_persistent_wifi; base::TimeDelta interval_persistent_fallback; - base::TimeDelta interval_soft_wifi; - base::TimeDelta interval_soft_fallback; + base::TimeDelta interval_startup_wifi; + base::TimeDelta interval_startup_fallback; + base::TimeDelta interval_shown_wifi; + base::TimeDelta interval_shown_fallback; }; enum class TriggerType; @@ -91,9 +93,10 @@ class RemoteSuggestionsSchedulerImpl : public RemoteSuggestionsScheduler { // timing is appropriate for another fetch. void RefetchInTheBackgroundIfAppropriate(TriggerType trigger); - // Checks whether it is time to perform a soft background fetch, according to - // |schedule|. - bool ShouldRefetchInTheBackgroundNow(base::Time last_fetch_attempt_time); + // 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); // Returns whether background fetching (for the given |trigger|) is disabled. bool BackgroundFetchesDisabled(TriggerType trigger) const; diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc index ea142787bcd..0c22282ea0a 100644 --- a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc +++ b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc @@ -489,8 +489,8 @@ TEST_F(RemoteSuggestionsSchedulerImplTest, // Make the first soft fetch successful. scheduler()->OnBrowserForegrounded(); signal_fetch_done.Run(Status::Success()); - // Open NTP again after 4hrs. - test_clock()->Advance(base::TimeDelta::FromHours(4)); + // Open NTP again after 9hrs. + test_clock()->Advance(base::TimeDelta::FromHours(24)); scheduler()->OnBrowserForegrounded(); } @@ -603,7 +603,7 @@ TEST_F(RemoteSuggestionsSchedulerImplTest, } TEST_F(RemoteSuggestionsSchedulerImplTest, - ReschedulesWhenSoftWifiParamChanges) { + ReschedulesWhenShownWifiParamChanges) { EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2); ActivateProvider(); @@ -617,7 +617,7 @@ TEST_F(RemoteSuggestionsSchedulerImplTest, } TEST_F(RemoteSuggestionsSchedulerImplTest, - ReschedulesWhenSoftFallbackParamChanges) { + ReschedulesWhenShownFallbackParamChanges) { EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2); ActivateProvider(); @@ -630,12 +630,40 @@ TEST_F(RemoteSuggestionsSchedulerImplTest, ActivateProvider(); } -TEST_F(RemoteSuggestionsSchedulerImplTest, FetchIntervalForSoftTriggerOnWifi) { +TEST_F(RemoteSuggestionsSchedulerImplTest, + ReschedulesWhenStartupWifiParamChanges) { + EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2); + ActivateProvider(); + + // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is + // null. Change the on usage interval for this class. + SetVariationParameter("startup_fetching_interval_hours-wifi-active_ntp_user", + "1.5"); + + // Schedule() should get called for the second time after params have changed. + ActivateProvider(); +} + +TEST_F(RemoteSuggestionsSchedulerImplTest, + ReschedulesWhenStartupFallbackParamChanges) { + EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2); + ActivateProvider(); + + // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is + // null. Change the fallback interval for this class. + SetVariationParameter( + "startup_fetching_interval_hours-fallback-active_ntp_user", "1.5"); + + // Schedule() should get called for the second time after params have changed. + ActivateProvider(); +} + +TEST_F(RemoteSuggestionsSchedulerImplTest, FetchIntervalForShownTriggerOnWifi) { // Pretend we are on WiFi (already done in ctor, we make it explicit here). EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection()) .WillRepeatedly(Return(true)); - // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 3h time - // interval by default for soft background fetches on WiFi. + // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 8h time + // interval by default for shown trigger on WiFi. // Initial scheduling after being enabled. EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)); @@ -651,22 +679,22 @@ TEST_F(RemoteSuggestionsSchedulerImplTest, FetchIntervalForSoftTriggerOnWifi) { signal_fetch_done.Run(Status::Success()); // Open NTP again after too short delay. This time no fetch is executed. - test_clock()->Advance(base::TimeDelta::FromMinutes(30)); + test_clock()->Advance(base::TimeDelta::FromHours(1)); scheduler()->OnNTPOpened(); // Open NTP after another delay, now together long enough to issue a fetch. - test_clock()->Advance(base::TimeDelta::FromMinutes(150)); + test_clock()->Advance(base::TimeDelta::FromHours(7)); EXPECT_CALL(*provider(), RefetchInTheBackground(_)); scheduler()->OnNTPOpened(); } TEST_F(RemoteSuggestionsSchedulerImplTest, - OverrideFetchIntervalForSoftTriggerOnWifi) { + OverrideFetchIntervalForShownTriggerOnWifi) { // Pretend we are on WiFi (already done in ctor, we make it explicit here). EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection()) .WillRepeatedly(Return(true)); // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is - // null. Change the on usage interval for this class from 2h to 30min. + // null. Change the interval for this class from 4h to 30min. SetVariationParameter("soft_fetching_interval_hours-wifi-active_ntp_user", "0.5"); @@ -694,12 +722,12 @@ TEST_F(RemoteSuggestionsSchedulerImplTest, } TEST_F(RemoteSuggestionsSchedulerImplTest, - FetchIntervalForSoftTriggerOnFallback) { + FetchIntervalForShownTriggerOnFallback) { // Pretend we are not on wifi -> fallback connection. EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection()) .WillRepeatedly(Return(false)); - // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 6h time - // interval by default for soft background fetches not on WiFi. + // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 12h time + // interval by default for shown trigger not on WiFi. // Initial scheduling after being enabled. EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)); @@ -715,22 +743,22 @@ TEST_F(RemoteSuggestionsSchedulerImplTest, signal_fetch_done.Run(Status::Success()); // Open NTP again after too short delay. This time no fetch is executed. - test_clock()->Advance(base::TimeDelta::FromMinutes(300)); + test_clock()->Advance(base::TimeDelta::FromHours(5)); scheduler()->OnNTPOpened(); // Open NTP after another delay, now together long enough to issue a fetch. - test_clock()->Advance(base::TimeDelta::FromMinutes(60)); + test_clock()->Advance(base::TimeDelta::FromHours(7)); EXPECT_CALL(*provider(), RefetchInTheBackground(_)); scheduler()->OnNTPOpened(); } TEST_F(RemoteSuggestionsSchedulerImplTest, - OverrideFetchIntervalForSoftTriggerOnFallback) { + OverrideFetchIntervalForShownTriggerOnFallback) { // Pretend we are not on wifi -> fallback connection. EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection()) .WillRepeatedly(Return(false)); // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is - // null. Change the on usage interval for this class from 4h to 30min. + // null. Change the interval for this class from 4h to 30min. SetVariationParameter("soft_fetching_interval_hours-fallback-active_ntp_user", "0.5"); diff --git a/chromium/components/ntp_snippets/user_classifier.cc b/chromium/components/ntp_snippets/user_classifier.cc index 92b0312de49..a2461be9ef9 100644 --- a/chromium/components/ntp_snippets/user_classifier.cc +++ b/chromium/components/ntp_snippets/user_classifier.cc @@ -41,11 +41,13 @@ const double kMinHours = 0.5; const char kMinHoursParam[] = "user_classifier_min_hours"; // Classification constants. -const double kActiveConsumerClicksAtLeastOncePerHours = 72; +const double kActiveConsumerClicksAtLeastOncePerHours = 96; const char kActiveConsumerClicksAtLeastOncePerHoursParam[] = "user_classifier_active_consumer_clicks_at_least_once_per_hours"; -const double kRareUserOpensNTPAtMostOncePerHours = 96; +// The previous value in production was 72, i.e. 3 days. The new value is a +// cautios shift in the direction we want (having slightly more rare users). +const double kRareUserOpensNTPAtMostOncePerHours = 66; const char kRareUserOpensNTPAtMostOncePerHoursParam[] = "user_classifier_rare_user_opens_ntp_at_most_once_per_hours"; @@ -73,7 +75,7 @@ const char* kLastTimeKeys[] = {prefs::kUserClassifierLastTimeToOpenNTP, prefs::kUserClassifierLastTimeToUseSuggestions}; // Default lengths of the intervals for new users for the metrics. -const double kInitialHoursBetweenEvents[] = {24, 48, 96}; +const double kInitialHoursBetweenEvents[] = {24, 48, 120}; const char* kInitialHoursBetweenEventsParams[] = { "user_classifier_default_interval_ntp_opened", "user_classifier_default_interval_suggestions_shown", |