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

Unified Diff: chrome/browser/android/most_visited_sites.cc

Issue 1330773002: [Android] Persist ordering of NTP suggestions. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/android/most_visited_sites.cc
diff --git a/chrome/browser/android/most_visited_sites.cc b/chrome/browser/android/most_visited_sites.cc
index 6c82bb37490e409e41c46c32c966f9b2640be105..1bae03b4aed1f1dca71e427af7f10e70dddf228b 100644
--- a/chrome/browser/android/most_visited_sites.cc
+++ b/chrome/browser/android/most_visited_sites.cc
@@ -12,6 +12,7 @@
#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "base/metrics/sparse_histogram.h"
+#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
@@ -26,7 +27,9 @@
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/thumbnails/thumbnail_list_source.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
#include "components/history/core/browser/top_sites.h"
+#include "components/pref_registry/pref_registry_syncable.h"
#include "components/suggestions/suggestions_service.h"
#include "components/suggestions/suggestions_utils.h"
#include "content/public/browser/browser_thread.h"
@@ -129,6 +132,52 @@ std::string GetPopularSitesFilename() {
"filename");
}
+void GetPreviousNTPSites(std::vector<std::string>* old_sites_url,
Marc Treib 2015/09/07 11:12:59 nit: Inputs first, then outputs.
knn 2015/09/08 10:48:55 Done.
+ std::vector<bool>* old_sites_source,
+ const PrefService* prefs,
+ size_t num_tiles,
+ const std::vector<std::string>& personal_urls) {
+ const base::ListValue* url_list = prefs->GetList(prefs::kNTPSuggestionsURL);
+ std::string url_string;
+ DCHECK_LE(personal_urls.size(), num_tiles);
+ if (url_list->GetSize() < num_tiles)
+ num_tiles = url_list->GetSize();
+ old_sites_url->reserve(num_tiles);
+ for (size_t i = 0; i < num_tiles; ++i) {
+ if (url_list->GetString(i, &url_string)) {
+ old_sites_url->emplace_back(url_string);
Marc Treib 2015/09/07 11:12:59 push_back - no C++11 library yet. It'll do the sam
knn 2015/09/08 10:48:55 Done.
+ }
+ }
+ if (num_tiles == 0) {
Marc Treib 2015/09/07 11:12:59 Move this above the loop.
knn 2015/09/08 10:48:55 Done.
+ // No fallback required as Personal suggestions take precedence anyway.
+ return;
+ }
+ old_sites_source->reserve(num_tiles);
+ const base::ListValue* host_list =
+ prefs->GetList(prefs::kNTPSuggestionsSource);
+ bool is_personal;
+ DCHECK_EQ(num_tiles, host_list->GetSize());
+ for (size_t i = 0; i < num_tiles; ++i) {
+ if (host_list->GetBoolean(i, &is_personal)) {
+ old_sites_source->push_back(is_personal);
+ }
+ }
+ DCHECK_EQ(num_tiles, old_sites_source->size());
+}
+
+void SaveCurrentNTPSites(const std::vector<std::string>& urls,
+ const std::vector<bool>& sources,
+ PrefService* prefs) {
+ base::ListValue url_list;
+ url_list.AppendStrings(urls);
+ prefs->Set(prefs::kNTPSuggestionsURL, url_list);
+
+ base::ListValue source_list;
+ for (const bool& source : sources)
+ source_list.AppendBoolean(source);
+ prefs->Set(prefs::kNTPSuggestionsSource, source_list);
+}
+
} // namespace
MostVisitedSites::MostVisitedSites(Profile* profile)
@@ -309,6 +358,13 @@ bool MostVisitedSites::Register(JNIEnv* env) {
return RegisterNativesImpl(env);
}
+// static
+void MostVisitedSites::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterListPref(prefs::kNTPSuggestionsURL);
+ registry->RegisterListPref(prefs::kNTPSuggestionsSource);
+}
+
void MostVisitedSites::QueryMostVisitedURLs() {
SuggestionsService* suggestions_service =
SuggestionsServiceFactory::GetForProfile(profile_);
@@ -353,8 +409,10 @@ void MostVisitedSites::OnMostVisitedURLsAvailable(
received_most_visited_sites_ = true;
mv_source_ = TOP_SITES;
- AddPopularSites(&titles, &urls);
- NotifyMostVisitedURLsObserver(titles, urls);
+ std::vector<base::string16> ordered_titles;
+ std::vector<std::string> ordered_urls;
+ AddPopularSites(&ordered_titles, &ordered_urls, &titles, &urls);
+ NotifyMostVisitedURLsObserver(ordered_titles, ordered_urls);
}
void MostVisitedSites::OnSuggestionsProfileAvailable(
@@ -387,118 +445,175 @@ void MostVisitedSites::OnSuggestionsProfileAvailable(
received_most_visited_sites_ = true;
mv_source_ = SUGGESTIONS_SERVICE;
- AddPopularSites(&titles, &urls);
- NotifyMostVisitedURLsObserver(titles, urls);
+ std::vector<base::string16> ordered_titles;
+ std::vector<std::string> ordered_urls;
+ AddPopularSites(&ordered_titles, &ordered_urls, &titles, &urls);
+ NotifyMostVisitedURLsObserver(ordered_titles, ordered_urls);
}
-void MostVisitedSites::AddPopularSites(std::vector<base::string16>* titles,
- std::vector<std::string>* urls) {
- if (!popular_sites_)
- return;
-
- DCHECK_EQ(titles->size(), urls->size());
- DCHECK_EQ(titles->size(), tile_sources_.size());
- DCHECK_LE(static_cast<int>(titles->size()), num_sites_);
+void MostVisitedSites::AddPopularSites(
Marc Treib 2015/09/07 11:12:59 Any reason why this doesn't just update |titles| a
knn 2015/09/08 10:48:56 Did you mean inplace? I did not want to complicate
Marc Treib 2015/09/08 11:39:49 I just meant that the caller shouldn't have to pas
+ std::vector<base::string16>* result_titles,
+ std::vector<std::string>* result_urls,
+ std::vector<base::string16>* personal_titles,
+ std::vector<std::string>* personal_urls) {
+ size_t num_personal_suggestions = personal_titles->size();
+ DCHECK_EQ(num_personal_suggestions, personal_urls->size());
+ DCHECK_EQ(num_personal_suggestions, tile_sources_.size());
+ DCHECK_LE(num_personal_suggestions, static_cast<size_t>(num_sites_));
+ std::vector<std::string> personal_hosts;
Marc Treib 2015/09/07 11:12:59 Make this an std::set? Would make checking members
knn 2015/09/08 10:48:55 Done.
+ for (const std::string& personal_url : *personal_urls) {
+ personal_hosts.emplace_back(GURL(personal_url).host());
+ }
- // Collect all non-blacklisted popular suggestions.
+ // Collect non-blacklisted popular suggestions removing duplicates from
Marc Treib 2015/09/07 11:12:59 nit: You're not removing anything from personal su
knn 2015/09/08 10:48:55 Done.
+ // personal suggestions.
+ size_t num_popular_suggestions = num_sites_ - num_personal_suggestions;
std::vector<base::string16> popular_titles;
+ popular_titles.reserve(num_popular_suggestions);
std::vector<std::string> popular_urls;
- scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_));
- for (const PopularSites::Site& popular_site : popular_sites_->sites()) {
- // Skip blacklisted sites.
- if (top_sites && top_sites->IsBlacklisted(popular_site.url))
- continue;
+ popular_urls.reserve(num_popular_suggestions);
+ std::vector<std::string> popular_hosts;
+ popular_hosts.reserve(num_popular_suggestions);
- popular_titles.push_back(popular_site.title);
- popular_urls.push_back(popular_site.url.spec());
- if (static_cast<int>(popular_titles.size()) >= num_sites_)
- break;
- }
+ if (num_popular_suggestions > 0 && popular_sites_) {
+ scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_));
+ for (const PopularSites::Site& popular_site : popular_sites_->sites()) {
+ if (popular_titles.size() >= num_popular_suggestions)
Marc Treib 2015/09/07 11:12:59 Put this at the end, right after you've added one?
knn 2015/09/08 10:48:55 Done.
+ break;
+ // Skip blacklisted sites.
+ if (top_sites && top_sites->IsBlacklisted(popular_site.url))
+ continue;
+ std::string host = popular_site.url.host();
+ // Skip suggestions already present in personal.
+ if (std::find(personal_hosts.begin(), personal_hosts.end(), host) !=
+ personal_hosts.end()) {
+ continue;
+ }
- AddPopularSitesImpl(
- num_sites_, popular_titles, popular_urls, titles, urls, &tile_sources_);
+ popular_titles.push_back(popular_site.title);
+ popular_urls.push_back(popular_site.url.spec());
+ popular_hosts.emplace_back(host);
Marc Treib 2015/09/07 11:12:59 push_back
knn 2015/09/08 10:48:55 Done.
+ }
+ }
+ num_popular_suggestions = popular_titles.size();
+ size_t num_actual_tiles = num_personal_suggestions + num_popular_suggestions;
+ std::vector<std::string> old_sites_url;
+ std::vector<bool> old_sites_source;
+ GetPreviousNTPSites(&old_sites_url, &old_sites_source, profile_->GetPrefs(),
+ num_actual_tiles, *personal_urls);
+ std::vector<bool> result_is_personal;
+ MergeSuggestions(&result_is_personal, result_urls, result_titles,
+ num_actual_tiles, num_personal_suggestions,
+ num_popular_suggestions, personal_urls, personal_titles,
+ &popular_urls, &popular_titles, personal_hosts,
+ popular_hosts, old_sites_url, old_sites_source);
+ DCHECK_EQ(num_actual_tiles, result_urls->size());
+ DCHECK_EQ(num_actual_tiles, result_titles->size());
+ DCHECK_EQ(num_actual_tiles, result_is_personal.size());
+ SaveCurrentNTPSites(*result_urls, result_is_personal, profile_->GetPrefs());
}
// static
-void MostVisitedSites::AddPopularSitesImpl(
- int num_sites,
- const std::vector<base::string16>& popular_titles,
- const std::vector<std::string>& popular_urls,
- std::vector<base::string16>* titles,
- std::vector<std::string>* urls,
- std::vector<std::string>* tile_sources) {
- // Start off with the popular suggestions.
- std::vector<base::string16> new_titles(popular_titles);
- std::vector<std::string> new_urls(popular_urls);
- std::vector<std::string> new_tile_sources(new_titles.size(),
- kHistogramPopularName);
-
- // Now, go over the personalized suggestions and replace matching popular
- // suggestions. This is so that when some of the popular suggestions become
- // personal, they retain their absolute positions.
- std::vector<base::string16> titles_to_insert;
- std::vector<std::string> urls_to_insert;
- std::vector<std::string> tile_sources_to_insert;
- for (size_t site_index = 0; site_index < titles->size(); site_index++) {
- const base::string16& title = (*titles)[site_index];
- const std::string& url = (*urls)[site_index];
- const std::string& tile_source = (*tile_sources)[site_index];
- // See if we already have a matching popular site.
- bool found = false;
- for (size_t i = 0; i < new_urls.size(); i++) {
- if (new_tile_sources[i] == kHistogramPopularName &&
- GURL(new_urls[i]).host() == GURL(url).host()) {
- // We have a matching popular sites suggestion. Replace it with the
- // actual URL and title.
- new_titles[i] = title;
- new_urls[i] = url;
- new_tile_sources[i] = tile_source;
- found = true;
+void MostVisitedSites::MergeSuggestions(
+ // Return values.
+ std::vector<bool>* result_is_personal,
+ std::vector<std::string>* result_urls,
+ std::vector<base::string16>* result_titles,
+ // Arguments.
+ size_t num_tiles,
+ size_t num_personal_suggestions,
+ size_t num_popular_suggestions,
+ // Passed as ptr for move semantics only.
+ std::vector<std::string>* personal_urls,
+ std::vector<base::string16>* personal_titles,
+ std::vector<std::string>* popular_urls,
+ std::vector<base::string16>* popular_titles,
+ const std::vector<std::string>& personal_hosts,
+ const std::vector<std::string>& popular_hosts,
+ const std::vector<std::string>& old_sites_url,
+ const std::vector<bool>& old_sites_source) {
+ // Initialize return values.
+ result_is_personal->resize(num_tiles, false);
+ result_urls->resize(num_tiles);
+ result_titles->resize(num_tiles);
+
+ std::vector<bool> result_is_filled(num_tiles, false);
+ size_t num_old_tiles = old_sites_url.size();
+ DCHECK_LE(num_old_tiles, num_tiles);
Marc Treib 2015/09/07 11:12:59 I don't think this is necessarily always true?
knn 2015/09/08 10:48:56 It is because GetPreviousNTPSites ensures that.
+ std::vector<std::string> old_sites_host;
+ old_sites_host.reserve(num_old_tiles);
+ for (size_t i = 0; i < num_old_tiles; ++i) {
+ old_sites_host.emplace_back(
+ old_sites_source[i] ? std::string() : GURL(old_sites_url[i]).host());
+ }
+
+ std::vector<size_t> personal_suggestions_to_insert;
+ // Insert personal suggestions if they existed previously.
+ for (size_t i = 0; i < num_personal_suggestions; ++i) {
+ size_t position;
+ for (position = 0; position < num_old_tiles; ++position) {
+ if (result_is_filled[position])
+ continue;
+ if (old_sites_url[position] == (*personal_urls)[i])
+ break;
+ if (old_sites_host[position] == personal_hosts[i])
Marc Treib 2015/09/07 11:12:59 I think here you also need to check that the old s
knn 2015/09/08 10:48:55 I do that by only populating the host field for po
Marc Treib 2015/09/08 11:39:49 Acknowledged.
break;
- }
}
- if (!found) {
- titles_to_insert.push_back(title);
- urls_to_insert.push_back(url);
- tile_sources_to_insert.push_back(tile_source);
+ if (position == num_old_tiles) {
+ personal_suggestions_to_insert.push_back(i);
+ } else {
+ (*result_urls)[position] = std::move((*personal_urls)[i]);
Marc Treib 2015/09/07 11:12:59 We're not allowed to use C++11 library features ye
knn 2015/09/08 10:48:55 Done.
+ (*result_titles)[position] = std::move((*personal_titles)[i]);
+ (*result_is_personal)[position] = true;
+ result_is_filled[position] = true;
}
}
- // Append personalized suggestions at the end if there's room.
- size_t num_to_append =
- std::min(static_cast<size_t>(num_sites) - new_titles.size(),
- titles_to_insert.size());
- new_titles.insert(new_titles.end(),
- titles_to_insert.end() - num_to_append,
- titles_to_insert.end());
- new_urls.insert(new_urls.end(),
- urls_to_insert.end() - num_to_append,
- urls_to_insert.end());
- new_tile_sources.insert(new_tile_sources.end(),
- tile_sources_to_insert.end() - num_to_append,
- tile_sources_to_insert.end());
-
- // Finally, go over the remaining personalized suggestions and evict popular
- // suggestions to accommodate them. Do it in reverse order, so the least
- // important popular suggestions will be evicted.
- for (size_t i = titles_to_insert.size() - num_to_append; i > 0; --i) {
- const base::string16& title = titles_to_insert[i - 1];
- const std::string& url = urls_to_insert[i - 1];
- const std::string& tile_source = tile_sources_to_insert[i - 1];
- for (size_t insert_i = new_titles.size(); insert_i > 0; --insert_i) {
- size_t insert_index = insert_i - 1;
- if (new_tile_sources[insert_index] == kHistogramPopularName) {
- new_titles[insert_index] = title;
- new_urls[insert_index] = url;
- new_tile_sources[insert_index] = tile_source;
+ std::vector<size_t> popular_suggestions_to_insert;
+ // Insert popular suggestions if they existed previously.
+ for (size_t i = 0; i < num_popular_suggestions; ++i) {
+ size_t position;
+ for (position = 0; position < num_old_tiles; ++position) {
+ if (result_is_filled[position])
+ continue;
+ if (old_sites_host[position] == popular_hosts[i])
break;
- }
+ }
+ if (position == num_old_tiles) {
+ popular_suggestions_to_insert.push_back(i);
+ } else {
+ (*result_urls)[position] = std::move((*popular_urls)[i]);
Marc Treib 2015/09/07 11:12:59 ^^
+ (*result_titles)[position] = std::move((*popular_titles)[i]);
+ result_is_filled[position] = true;
}
}
- titles->swap(new_titles);
- urls->swap(new_urls);
- tile_sources->swap(new_tile_sources);
+ // Insert leftover personal suggestions.
+ size_t insert_position = 0;
+ size_t insert_size = personal_suggestions_to_insert.size();
+ for (/*insert_position continued*/ size_t i = 0;
+ insert_position < num_tiles && i < insert_size; ++insert_position) {
+ if (result_is_filled[insert_position])
+ continue;
+ size_t to_insert = personal_suggestions_to_insert[i++];
+ (*result_urls)[insert_position] = std::move((*personal_urls)[to_insert]);
+ (*result_titles)[insert_position] =
+ std::move((*personal_titles)[to_insert]);
+ (*result_is_personal)[insert_position] = true;
+ // Ignore setting result_is_filled as it is isn't used anymore.
Marc Treib 2015/09/07 11:12:59 s/Ignore/Skip/g
+ }
+
+ // Insert leftover popular suggestions.
+ insert_size = popular_suggestions_to_insert.size();
+ for (/*insert_position continued*/ size_t i = 0;
+ insert_position < num_tiles && i < insert_size; ++insert_position) {
+ if (result_is_filled[insert_position])
+ continue;
+ size_t to_insert = popular_suggestions_to_insert[i++];
+ (*result_urls)[insert_position] = std::move((*popular_urls)[to_insert]);
+ (*result_titles)[insert_position] = std::move((*popular_titles)[to_insert]);
+ // Ignore setting result_is_filled as it is isn't used anymore.
+ }
}
void MostVisitedSites::NotifyMostVisitedURLsObserver(

Powered by Google App Engine
This is Rietveld 408576698