blob: 8a564fc30d7e7e1927e34d1adf09149053878c6a [file] [log] [blame]
[email protected]c764b2822013-07-03 04:16:481// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
sfiera3ff01c0d2016-06-13 15:33:385#include "components/ntp_tiles/most_visited_sites.h"
[email protected]c764b2822013-07-03 04:16:486
sfiera3ff01c0d2016-06-13 15:33:387#include <algorithm>
Friedrich Horschigb1733402017-08-28 17:06:118#include <iterator>
dchengbc6125d2015-12-30 01:54:079#include <utility>
10
mastiz73693f12017-01-27 15:52:5711#include "base/bind.h"
[email protected]d33adb40d2014-06-03 21:38:4612#include "base/callback.h"
mastizf3468a82016-06-15 11:05:4113#include "base/feature_list.h"
dgnf6500c12017-05-09 17:05:3114#include "base/metrics/user_metrics.h"
Friedrich Horschigb1733402017-08-28 17:06:1115#include "base/strings/string_util.h"
[email protected]bc093e82014-03-04 21:49:1116#include "base/strings/utf_string_conversions.h"
sdefresne0da3bc02015-01-29 18:26:3517#include "components/history/core/browser/top_sites.h"
treibdb416392016-06-29 17:20:5018#include "components/ntp_tiles/constants.h"
Eric Noyau4793d932016-11-16 16:19:2319#include "components/ntp_tiles/field_trial.h"
sfieraae1ae692016-10-24 18:19:1520#include "components/ntp_tiles/icon_cacher.h"
sfiera0dc4da52016-04-29 09:23:0521#include "components/ntp_tiles/pref_names.h"
22#include "components/ntp_tiles/switches.h"
knncbbd754d2015-09-09 15:43:4023#include "components/pref_registry/pref_registry_syncable.h"
brettwb1fc1b82016-02-02 00:19:0824#include "components/prefs/pref_service.h"
[email protected]c764b2822013-07-03 04:16:4825
[email protected]c764b2822013-07-03 04:16:4826using history::TopSites;
[email protected]bc093e82014-03-04 21:49:1127using suggestions::ChromeSuggestion;
28using suggestions::SuggestionsProfile;
sfiera7e952d42016-05-04 17:56:3329using suggestions::SuggestionsService;
[email protected]c764b2822013-07-03 04:16:4830
sfiera08009fe2016-06-15 17:07:2631namespace ntp_tiles {
32
[email protected]c764b2822013-07-03 04:16:4833namespace {
34
mastizf3468a82016-06-15 11:05:4135const base::Feature kDisplaySuggestionsServiceTiles{
36 "DisplaySuggestionsServiceTiles", base::FEATURE_ENABLED_BY_DEFAULT};
37
Friedrich Horschigb1733402017-08-28 17:06:1138// URL host prefixes. Hosts with these prefixes often redirect to each other, or
39// have the same content.
40// Popular sites are excluded if the user has visited a page whose host only
41// differs by one of these prefixes. Even if the URL does not point to the exact
42// same page, the user will have a personalized suggestion that is more likely
43// to be of use for them.
44// A cleaner way could be checking the history for redirects but this requires
45// the page to be visited on the device.
46const char* kKnownGenericPagePrefixes[] = {
47 "m.", "mobile.", // Common prefixes among popular sites.
48 "edition.", // Used among news papers (CNN, Independent, ...)
49 "www.", // Usually no-www domains redirect to www or vice-versa.
50 // The following entry MUST REMAIN LAST as it is prefix of every string!
51 ""}; // The no-www domain matches domains on same level .
52
sfiera635d21d2016-08-04 07:56:0953// Determine whether we need any tiles from PopularSites to fill up a grid of
Friedrich Horschig9e8749f02017-08-10 09:17:3154// |num_tiles| tiles. If exploration sections are used, we need popular sites
55// regardless of how many tiles we already have.
treibcd3d2ef2016-07-13 16:53:3156bool NeedPopularSites(const PrefService* prefs, int num_tiles) {
Nicolas Dossou-gbete56f37dc2017-08-10 16:14:0457 return base::FeatureList::IsEnabled(kSiteExplorationUiFeature) ||
Friedrich Horschig9e8749f02017-08-10 09:17:3158 prefs->GetInteger(prefs::kNumPersonalTiles) < num_tiles;
treibf79ed40d2015-09-22 12:38:0659}
60
atanasova9c0dda602016-03-22 17:41:1361bool AreURLsEquivalent(const GURL& url1, const GURL& url2) {
csharrison88b3b712016-11-14 23:12:3562 return url1.host_piece() == url2.host_piece() &&
63 url1.path_piece() == url2.path_piece();
atanasova9c0dda602016-03-22 17:41:1364}
65
Friedrich Horschig8f4cce62017-07-07 11:25:1366bool HasHomeTile(const NTPTilesVector& tiles) {
67 for (const auto& tile : tiles) {
68 if (tile.source == TileSource::HOMEPAGE) {
69 return true;
70 }
71 }
72 return false;
73}
74
Friedrich Horschigb1733402017-08-28 17:06:1175std::string StripFirstGenericPrefix(const std::string& host) {
76 for (const char* prefix : kKnownGenericPagePrefixes) {
77 if (base::StartsWith(host, prefix, base::CompareCase::INSENSITIVE_ASCII)) {
78 return std::string(
79 base::TrimString(host, prefix, base::TrimPositions::TRIM_LEADING));
80 }
81 }
82 return host;
83}
84
sfiera8df7b382016-04-26 14:06:3385} // namespace
86
sfierac35d2722016-12-14 09:41:4587MostVisitedSites::MostVisitedSites(
88 PrefService* prefs,
89 scoped_refptr<history::TopSites> top_sites,
90 SuggestionsService* suggestions,
91 std::unique_ptr<PopularSites> popular_sites,
92 std::unique_ptr<IconCacher> icon_cacher,
fhorschig3fc1f812017-06-01 12:28:2893 std::unique_ptr<MostVisitedSitesSupervisor> supervisor)
sfiera79e643b2016-05-13 16:52:4994 : prefs_(prefs),
sfiera685a987e2016-05-12 17:08:4795 top_sites_(top_sites),
96 suggestions_service_(suggestions),
sfiera96c03252016-09-14 18:58:1697 popular_sites_(std::move(popular_sites)),
sfieraae1ae692016-10-24 18:19:1598 icon_cacher_(std::move(icon_cacher)),
sfierac35d2722016-12-14 09:41:4599 supervisor_(std::move(supervisor)),
sfiera685a987e2016-05-12 17:08:47100 observer_(nullptr),
fhorschigd3222452017-05-22 08:36:44101 num_sites_(0u),
treibbaada0f2016-08-01 11:30:33102 top_sites_observer_(this),
treib231212e2017-04-04 17:09:22103 mv_source_(TileSource::TOP_SITES),
mastizd0da75e2017-01-09 11:36:48104 top_sites_weak_ptr_factory_(this) {
sfiera87db8582016-08-05 10:46:42105 DCHECK(prefs_);
sfieraaa1b8862016-10-04 16:09:23106 // top_sites_ can be null in tests.
107 // TODO(sfiera): have iOS use a dummy TopSites in its tests.
mastizc60d74e2016-06-14 14:02:49108 DCHECK(suggestions_service_);
sfiera87db8582016-08-05 10:46:42109 if (supervisor_)
110 supervisor_->SetObserver(this);
[email protected]7ceb4e32013-12-06 04:13:04111}
[email protected]c764b2822013-07-03 04:16:48112
[email protected]7ceb4e32013-12-06 04:13:04113MostVisitedSites::~MostVisitedSites() {
sfiera87db8582016-08-05 10:46:42114 if (supervisor_)
115 supervisor_->SetObserver(nullptr);
[email protected]7ceb4e32013-12-06 04:13:04116}
[email protected]c764b2822013-07-03 04:16:48117
Friedrich Horschigb1733402017-08-28 17:06:11118// static
119bool MostVisitedSites::IsHostOrMobilePageKnown(
120 const std::set<std::string>& hosts_to_skip,
121 const std::string& host) {
122 std::string no_prefix_host = StripFirstGenericPrefix(host);
123 for (const char* prefix : kKnownGenericPagePrefixes) {
124 if (hosts_to_skip.count(prefix + no_prefix_host) ||
125 hosts_to_skip.count(prefix + host)) {
126 return true;
127 }
128 }
129 return false;
130}
131
treib231212e2017-04-04 17:09:22132bool MostVisitedSites::DoesSourceExist(TileSource source) const {
sfiera65cefe9d2017-02-07 11:02:50133 switch (source) {
treib231212e2017-04-04 17:09:22134 case TileSource::TOP_SITES:
sfiera65cefe9d2017-02-07 11:02:50135 return top_sites_ != nullptr;
treib231212e2017-04-04 17:09:22136 case TileSource::SUGGESTIONS_SERVICE:
sfiera65cefe9d2017-02-07 11:02:50137 return suggestions_service_ != nullptr;
Friedrich Horschig7706ef62017-08-25 08:20:00138 case TileSource::POPULAR_BAKED_IN:
treib231212e2017-04-04 17:09:22139 case TileSource::POPULAR:
sfiera65cefe9d2017-02-07 11:02:50140 return popular_sites_ != nullptr;
treib231212e2017-04-04 17:09:22141 case TileSource::WHITELIST:
sfiera65cefe9d2017-02-07 11:02:50142 return supervisor_ != nullptr;
fhorschigd3222452017-05-22 08:36:44143 case TileSource::HOMEPAGE:
144 return home_page_client_ != nullptr;
sfiera65cefe9d2017-02-07 11:02:50145 }
146 NOTREACHED();
147 return false;
148}
149
fhorschig3fc1f812017-06-01 12:28:28150void MostVisitedSites::SetHomePageClient(
151 std::unique_ptr<HomePageClient> client) {
152 DCHECK(client);
153 home_page_client_ = std::move(client);
154}
155
treib5a59f542016-05-18 12:17:29156void MostVisitedSites::SetMostVisitedURLsObserver(Observer* observer,
fhorschigd3222452017-05-22 08:36:44157 size_t num_sites) {
treibc1bd1f12016-04-29 15:06:05158 DCHECK(observer);
sfiera929502f32016-04-21 12:52:48159 observer_ = observer;
[email protected]7ceb4e32013-12-06 04:13:04160 num_sites_ = num_sites;
[email protected]852fc792013-10-04 04:15:12161
treib99f6b992016-11-23 13:59:50162 // The order for this condition is important, ShouldShowPopularSites() should
Eric Noyau4793d932016-11-16 16:19:23163 // always be called last to keep metrics as relevant as possible.
164 if (popular_sites_ && NeedPopularSites(prefs_, num_sites_) &&
165 ShouldShowPopularSites()) {
mastizfd2c7ab2017-01-27 19:35:00166 popular_sites_->MaybeStartFetch(
167 false, base::Bind(&MostVisitedSites::OnPopularSitesDownloaded,
sfiera00a58ab2016-07-28 10:20:49168 base::Unretained(this)));
treibf79ed40d2015-09-22 12:38:06169 }
170
treibf7d11512016-06-29 09:06:15171 if (top_sites_) {
treibf7d11512016-06-29 09:06:15172 // Register as TopSitesObserver so that we can update ourselves when the
173 // TopSites changes.
treibbaada0f2016-08-01 11:30:33174 top_sites_observer_.Add(top_sites_.get());
treibf7d11512016-06-29 09:06:15175 }
treibcb9c93b2016-04-01 13:22:52176
mastizbad28af2017-01-30 16:04:37177 suggestions_subscription_ = suggestions_service_->AddCallback(base::Bind(
178 &MostVisitedSites::OnSuggestionsProfileChanged, base::Unretained(this)));
treibcb9c93b2016-04-01 13:22:52179
sfiera635d21d2016-08-04 07:56:09180 // Immediately build the current set of tiles, getting suggestions from the
181 // SuggestionsService's cache or, if that is empty, sites from TopSites.
182 BuildCurrentTiles();
treibcb9c93b2016-04-01 13:22:52183 // Also start a request for fresh suggestions.
treibbb0c5af52016-12-09 17:34:15184 Refresh();
185}
186
187void MostVisitedSites::Refresh() {
Mikel Astiz1d90c812017-07-21 13:52:51188 if (top_sites_) {
189 // TopSites updates itself after a delay. To ensure up-to-date results,
190 // force an update now.
191 // TODO(mastiz): Is seems unnecessary to refresh TopSites if we will end up
192 // using server-side suggestions.
193 top_sites_->SyncWithHistory();
194 }
195
sfiera728962f2016-04-29 11:04:02196 suggestions_service_->FetchSuggestionsData();
[email protected]c764b2822013-07-03 04:16:48197}
198
Friedrich Horschig1a5feba2017-07-04 17:22:04199void MostVisitedSites::OnHomePageStateChanged() {
200 BuildCurrentTiles();
201}
202
treib5a59f542016-05-18 12:17:29203void MostVisitedSites::AddOrRemoveBlacklistedUrl(const GURL& url,
204 bool add_url) {
dgnf6500c12017-05-09 17:05:31205 if (add_url) {
206 base::RecordAction(base::UserMetricsAction("Suggestions.Site.Removed"));
207 } else {
208 base::RecordAction(
209 base::UserMetricsAction("Suggestions.Site.RemovalUndone"));
210 }
211
treibf7d11512016-06-29 09:06:15212 if (top_sites_) {
213 // Always blacklist in the local TopSites.
214 if (add_url)
215 top_sites_->AddBlacklistedURL(url);
216 else
217 top_sites_->RemoveBlacklistedURL(url);
218 }
[email protected]d4975242014-06-02 18:16:20219
treibfac75192015-08-17 13:21:24220 // Only blacklist in the server-side suggestions service if it's active.
treib231212e2017-04-04 17:09:22221 if (mv_source_ == TileSource::SUGGESTIONS_SERVICE) {
treib16070ce2016-03-11 11:57:40222 if (add_url)
sfiera728962f2016-04-29 11:04:02223 suggestions_service_->BlacklistURL(url);
treib16070ce2016-03-11 11:57:40224 else
sfiera728962f2016-04-29 11:04:02225 suggestions_service_->UndoBlacklistURL(url);
[email protected]d4975242014-06-02 18:16:20226 }
[email protected]a4c2f282013-07-20 05:26:05227}
[email protected]7ceb4e32013-12-06 04:13:04228
treibbb0c5af52016-12-09 17:34:15229void MostVisitedSites::ClearBlacklistedUrls() {
230 if (top_sites_) {
231 // Always update the blacklist in the local TopSites.
232 top_sites_->ClearBlacklistedURLs();
233 }
234
235 // Only update the server-side blacklist if it's active.
treib231212e2017-04-04 17:09:22236 if (mv_source_ == TileSource::SUGGESTIONS_SERVICE) {
treibbb0c5af52016-12-09 17:34:15237 suggestions_service_->ClearBlacklist();
238 }
239}
240
sfiera79e643b2016-05-13 16:52:49241void MostVisitedSites::OnBlockedSitesChanged() {
sfiera635d21d2016-08-04 07:56:09242 BuildCurrentTiles();
atanasova9572aaf2016-02-26 18:08:26243}
244
[email protected]7ceb4e32013-12-06 04:13:04245// static
knncbbd754d2015-09-09 15:43:40246void MostVisitedSites::RegisterProfilePrefs(
247 user_prefs::PrefRegistrySyncable* registry) {
sfiera635d21d2016-08-04 07:56:09248 registry->RegisterIntegerPref(prefs::kNumPersonalTiles, 0);
knncbbd754d2015-09-09 15:43:40249}
250
[email protected]bc093e82014-03-04 21:49:11251void MostVisitedSites::InitiateTopSitesQuery() {
treibf7d11512016-06-29 09:06:15252 if (!top_sites_)
253 return;
mastizd0da75e2017-01-09 11:36:48254 if (top_sites_weak_ptr_factory_.HasWeakPtrs())
255 return; // Ongoing query.
sfiera728962f2016-04-29 11:04:02256 top_sites_->GetMostVisitedURLs(
knn6cf777fc2015-05-06 13:21:48257 base::Bind(&MostVisitedSites::OnMostVisitedURLsAvailable,
mastizd0da75e2017-01-09 11:36:48258 top_sites_weak_ptr_factory_.GetWeakPtr()),
[email protected]7ceb4e32013-12-06 04:13:04259 false);
260}
261
atanasova9c0dda602016-03-22 17:41:13262base::FilePath MostVisitedSites::GetWhitelistLargeIconPath(const GURL& url) {
sfiera87db8582016-08-05 10:46:42263 if (supervisor_) {
thestigcff6d872017-06-08 18:30:35264 for (const auto& whitelist : supervisor_->GetWhitelists()) {
sfiera87db8582016-08-05 10:46:42265 if (AreURLsEquivalent(whitelist.entry_point, url))
266 return whitelist.large_icon_path;
267 }
atanasova9c0dda602016-03-22 17:41:13268 }
269 return base::FilePath();
270}
271
[email protected]d4975242014-06-02 18:16:20272void MostVisitedSites::OnMostVisitedURLsAvailable(
[email protected]d4975242014-06-02 18:16:20273 const history::MostVisitedURLList& visited_list) {
mastiz64840862017-01-05 12:42:31274 // Ignore the event if tiles provided by the Suggestions Service, which take
275 // precedence.
treib231212e2017-04-04 17:09:22276 if (mv_source_ == TileSource::SUGGESTIONS_SERVICE) {
mastiz64840862017-01-05 12:42:31277 return;
278 }
279
sfiera635d21d2016-08-04 07:56:09280 NTPTilesVector tiles;
fhorschigd3222452017-05-22 08:36:44281 size_t num_tiles = std::min(visited_list.size(), num_sites_);
knncbbd754d2015-09-09 15:43:40282 for (size_t i = 0; i < num_tiles; ++i) {
knn6cf777fc2015-05-06 13:21:48283 const history::MostVisitedURL& visited = visited_list[i];
vitaliii875bfee2017-02-08 10:54:53284 if (visited.url.is_empty())
knn6cf777fc2015-05-06 13:21:48285 break; // This is the signal that there are no more real visited sites.
sfiera87db8582016-08-05 10:46:42286 if (supervisor_ && supervisor_->IsBlocked(visited.url))
atanasova9514b302016-02-26 23:09:28287 continue;
atanasova9514b302016-02-26 23:09:28288
sfiera635d21d2016-08-04 07:56:09289 NTPTile tile;
290 tile.title = visited.title;
291 tile.url = visited.url;
treib231212e2017-04-04 17:09:22292 tile.source = TileSource::TOP_SITES;
sfiera635d21d2016-08-04 07:56:09293 tile.whitelist_icon_path = GetWhitelistLargeIconPath(visited.url);
Friedrich Horschigefec0812017-09-28 18:50:17294 // MostVisitedURL.title is either the title or the URL which is treated
295 // exactly as the title. Differentiating here is not worth the overhead.
296 tile.title_source = TileTitleSource::TITLE_TAG;
Mikel Astiza0f98cc2017-10-12 19:48:48297 // TODO(crbug.com/773278): Populate |data_generation_time| here in order to
298 // log UMA metrics of age.
sfiera635d21d2016-08-04 07:56:09299 tiles.push_back(std::move(tile));
knn6cf777fc2015-05-06 13:21:48300 }
[email protected]d4975242014-06-02 18:16:20301
treib231212e2017-04-04 17:09:22302 mv_source_ = TileSource::TOP_SITES;
Friedrich Horschig8f4cce62017-07-07 11:25:13303 InitiateNotificationForNewTiles(std::move(tiles));
[email protected]d4975242014-06-02 18:16:20304}
305
mastizbad28af2017-01-30 16:04:37306void MostVisitedSites::OnSuggestionsProfileChanged(
[email protected]bc093e82014-03-04 21:49:11307 const SuggestionsProfile& suggestions_profile) {
mastizbad28af2017-01-30 16:04:37308 if (suggestions_profile.suggestions_size() == 0 &&
treib231212e2017-04-04 17:09:22309 mv_source_ != TileSource::SUGGESTIONS_SERVICE) {
mastizbad28af2017-01-30 16:04:37310 return;
311 }
312
313 BuildCurrentTilesGivenSuggestionsProfile(suggestions_profile);
314}
315
316void MostVisitedSites::BuildCurrentTiles() {
317 BuildCurrentTilesGivenSuggestionsProfile(
318 suggestions_service_->GetSuggestionsDataFromCache().value_or(
319 SuggestionsProfile()));
320}
321
322void MostVisitedSites::BuildCurrentTilesGivenSuggestionsProfile(
323 const suggestions::SuggestionsProfile& suggestions_profile) {
fhorschigd3222452017-05-22 08:36:44324 size_t num_tiles = suggestions_profile.suggestions_size();
treibc1bd1f12016-04-29 15:06:05325 // With no server suggestions, fall back to local TopSites.
mastizf3468a82016-06-15 11:05:41326 if (num_tiles == 0 ||
327 !base::FeatureList::IsEnabled(kDisplaySuggestionsServiceTiles)) {
treib231212e2017-04-04 17:09:22328 mv_source_ = TileSource::TOP_SITES;
[email protected]bc093e82014-03-04 21:49:11329 InitiateTopSitesQuery();
330 return;
331 }
knn6cf777fc2015-05-06 13:21:48332 if (num_sites_ < num_tiles)
333 num_tiles = num_sites_;
[email protected]bc093e82014-03-04 21:49:11334
Mikel Astiza0f98cc2017-10-12 19:48:48335 const base::Time profile_timestamp =
336 base::Time::UnixEpoch() +
337 base::TimeDelta::FromMicroseconds(suggestions_profile.timestamp());
338
sfiera635d21d2016-08-04 07:56:09339 NTPTilesVector tiles;
fhorschigd3222452017-05-22 08:36:44340 for (size_t i = 0; i < num_tiles; ++i) {
treibd2698222016-07-14 11:32:44341 const ChromeSuggestion& suggestion_pb = suggestions_profile.suggestions(i);
342 GURL url(suggestion_pb.url());
sfiera87db8582016-08-05 10:46:42343 if (supervisor_ && supervisor_->IsBlocked(url))
atanasova9514b302016-02-26 23:09:28344 continue;
atanasova9514b302016-02-26 23:09:28345
sfiera635d21d2016-08-04 07:56:09346 NTPTile tile;
347 tile.title = base::UTF8ToUTF16(suggestion_pb.title());
348 tile.url = url;
treib231212e2017-04-04 17:09:22349 tile.source = TileSource::SUGGESTIONS_SERVICE;
Friedrich Horschigefec0812017-09-28 18:50:17350 // The title is an aggregation of multiple history entries of one site.
351 tile.title_source = TileTitleSource::INFERRED;
sfiera635d21d2016-08-04 07:56:09352 tile.whitelist_icon_path = GetWhitelistLargeIconPath(url);
treibbb0c5af52016-12-09 17:34:15353 tile.thumbnail_url = GURL(suggestion_pb.thumbnail());
354 tile.favicon_url = GURL(suggestion_pb.favicon_url());
Mikel Astiza0f98cc2017-10-12 19:48:48355 tile.data_generation_time = profile_timestamp;
356
jkrcal43f24f82017-05-12 18:01:52357 if (AreNtpMostLikelyFaviconsFromServerEnabled()) {
358 icon_cacher_->StartFetchMostLikely(
359 url, base::Bind(&MostVisitedSites::OnIconMadeAvailable,
360 base::Unretained(this), url));
361 }
atanasovac3c25d12016-03-17 18:33:57362
sfiera635d21d2016-08-04 07:56:09363 tiles.push_back(std::move(tile));
[email protected]bc093e82014-03-04 21:49:11364 }
treib783a3372015-08-25 16:24:39365
treib231212e2017-04-04 17:09:22366 mv_source_ = TileSource::SUGGESTIONS_SERVICE;
Friedrich Horschig8f4cce62017-07-07 11:25:13367 InitiateNotificationForNewTiles(std::move(tiles));
knn6cf777fc2015-05-06 13:21:48368}
[email protected]d4975242014-06-02 18:16:20369
sfiera635d21d2016-08-04 07:56:09370NTPTilesVector MostVisitedSites::CreateWhitelistEntryPointTiles(
fhorschigd3222452017-05-22 08:36:44371 const std::set<std::string>& used_hosts,
372 size_t num_actual_tiles) {
sfiera87db8582016-08-05 10:46:42373 if (!supervisor_) {
374 return NTPTilesVector();
375 }
376
sfiera635d21d2016-08-04 07:56:09377 NTPTilesVector whitelist_tiles;
thestigcff6d872017-06-08 18:30:35378 for (const auto& whitelist : supervisor_->GetWhitelists()) {
fhorschigd3222452017-05-22 08:36:44379 if (whitelist_tiles.size() + num_actual_tiles >= num_sites_)
sfierae56d2a02017-02-07 09:40:09380 break;
381
atanasova9514b302016-02-26 23:09:28382 // Skip blacklisted sites.
treibf7d11512016-06-29 09:06:15383 if (top_sites_ && top_sites_->IsBlacklisted(whitelist.entry_point))
atanasova9514b302016-02-26 23:09:28384 continue;
385
sfiera635d21d2016-08-04 07:56:09386 // Skip tiles already present.
fhorschigd3222452017-05-22 08:36:44387 if (used_hosts.find(whitelist.entry_point.host()) != used_hosts.end())
atanasova9514b302016-02-26 23:09:28388 continue;
389
390 // Skip whitelist entry points that are manually blocked.
sfiera79e643b2016-05-13 16:52:49391 if (supervisor_->IsBlocked(whitelist.entry_point))
atanasova9514b302016-02-26 23:09:28392 continue;
atanasova9514b302016-02-26 23:09:28393
sfiera635d21d2016-08-04 07:56:09394 NTPTile tile;
395 tile.title = whitelist.title;
396 tile.url = whitelist.entry_point;
treib231212e2017-04-04 17:09:22397 tile.source = TileSource::WHITELIST;
Friedrich Horschigefec0812017-09-28 18:50:17398 // User-set. Might be the title but we cannot be sure.
399 tile.title_source = TileTitleSource::UNKNOWN;
sfiera635d21d2016-08-04 07:56:09400 tile.whitelist_icon_path = whitelist.large_icon_path;
sfiera635d21d2016-08-04 07:56:09401 whitelist_tiles.push_back(std::move(tile));
atanasova9572aaf2016-02-26 18:08:26402 }
403
sfiera635d21d2016-08-04 07:56:09404 return whitelist_tiles;
atanasova9572aaf2016-02-26 18:08:26405}
406
Friedrich Horschig9e8749f02017-08-10 09:17:31407std::map<SectionType, NTPTilesVector>
408MostVisitedSites::CreatePopularSitesSections(
fhorschigd3222452017-05-22 08:36:44409 const std::set<std::string>& used_hosts,
410 size_t num_actual_tiles) {
Friedrich Horschig9e8749f02017-08-10 09:17:31411 std::map<SectionType, NTPTilesVector> sections = {
412 std::make_pair(SectionType::PERSONALIZED, NTPTilesVector())};
sfiera635d21d2016-08-04 07:56:09413 // For child accounts popular sites tiles will not be added.
fhorschigd3222452017-05-22 08:36:44414 if (supervisor_ && supervisor_->IsChildProfile()) {
Friedrich Horschig9e8749f02017-08-10 09:17:31415 return sections;
fhorschigd3222452017-05-22 08:36:44416 }
atanasova9514b302016-02-26 23:09:28417
fhorschigd3222452017-05-22 08:36:44418 if (!popular_sites_ || !ShouldShowPopularSites()) {
Friedrich Horschig9e8749f02017-08-10 09:17:31419 return sections;
fhorschigd3222452017-05-22 08:36:44420 }
atanasova9572aaf2016-02-26 18:08:26421
Friedrich Horschig9e8749f02017-08-10 09:17:31422 const std::set<std::string> no_hosts;
423 for (const auto& section_type_and_sites : popular_sites()->sections()) {
424 SectionType type = section_type_and_sites.first;
425 const PopularSites::SitesVector& sites = section_type_and_sites.second;
426 if (type == SectionType::PERSONALIZED) {
427 size_t num_required_tiles = num_sites_ - num_actual_tiles;
428 sections[type] =
429 CreatePopularSitesTiles(/*popular_sites=*/sites,
430 /*hosts_to_skip=*/used_hosts,
431 /*num_max_tiles=*/num_required_tiles);
432 } else {
433 sections[type] = CreatePopularSitesTiles(/*popular_sites=*/sites,
434 /*hosts_to_skip=*/no_hosts,
435 /*num_max_tiles=*/num_sites_);
436 }
437 }
438 return sections;
439}
440
441NTPTilesVector MostVisitedSites::CreatePopularSitesTiles(
442 const PopularSites::SitesVector& sites_vector,
443 const std::set<std::string>& hosts_to_skip,
444 size_t num_max_tiles) {
knncbbd754d2015-09-09 15:43:40445 // Collect non-blacklisted popular suggestions, skipping those already present
446 // in the personal suggestions.
sfiera635d21d2016-08-04 07:56:09447 NTPTilesVector popular_sites_tiles;
Friedrich Horschig9e8749f02017-08-10 09:17:31448 for (const PopularSites::Site& popular_site : sites_vector) {
449 if (popular_sites_tiles.size() >= num_max_tiles) {
fhorschigd3222452017-05-22 08:36:44450 break;
Friedrich Horschig9e8749f02017-08-10 09:17:31451 }
treibcffa6502015-08-06 09:12:27452
fhorschigd3222452017-05-22 08:36:44453 // Skip blacklisted sites.
454 if (top_sites_ && top_sites_->IsBlacklisted(popular_site.url))
455 continue;
treibcffa6502015-08-06 09:12:27456
fhorschigd3222452017-05-22 08:36:44457 const std::string& host = popular_site.url.host();
Friedrich Horschigb1733402017-08-28 17:06:11458 if (IsHostOrMobilePageKnown(hosts_to_skip, host)) {
fhorschigd3222452017-05-22 08:36:44459 continue;
Friedrich Horschig9e8749f02017-08-10 09:17:31460 }
atanasovac3c25d12016-03-17 18:33:57461
fhorschigd3222452017-05-22 08:36:44462 NTPTile tile;
463 tile.title = popular_site.title;
464 tile.url = GURL(popular_site.url);
Friedrich Horschigefec0812017-09-28 18:50:17465 tile.title_source = popular_site.title_source;
Friedrich Horschig7706ef62017-08-25 08:20:00466 tile.source = popular_site.baked_in ? TileSource::POPULAR_BAKED_IN
467 : TileSource::POPULAR;
fhorschigd3222452017-05-22 08:36:44468 popular_sites_tiles.push_back(std::move(tile));
469 base::Closure icon_available =
470 base::Bind(&MostVisitedSites::OnIconMadeAvailable,
471 base::Unretained(this), popular_site.url);
472 icon_cacher_->StartFetchPopularSites(popular_site, icon_available,
473 icon_available);
treibcffa6502015-08-06 09:12:27474 }
sfiera635d21d2016-08-04 07:56:09475 return popular_sites_tiles;
atanasova9572aaf2016-02-26 18:08:26476}
477
Friedrich Horschig8f4cce62017-07-07 11:25:13478void MostVisitedSites::OnHomePageTitleDetermined(
479 NTPTilesVector tiles,
480 const base::Optional<base::string16>& title) {
481 if (!title.has_value()) {
482 return; // If there is no title, the most recent tile was already sent out.
483 }
484 SaveTilesAndNotify(InsertHomeTile(std::move(tiles), title.value()));
485}
486
487NTPTilesVector MostVisitedSites::InsertHomeTile(
488 NTPTilesVector tiles,
489 const base::string16& title) const {
fhorschigd3222452017-05-22 08:36:44490 DCHECK(home_page_client_);
491 DCHECK_GT(num_sites_, 0u);
treib5a59f542016-05-18 12:17:29492
Friedrich Horschig8f4cce62017-07-07 11:25:13493 const GURL& home_page_url = home_page_client_->GetHomePageUrl();
fhorschigd3222452017-05-22 08:36:44494 NTPTilesVector new_tiles;
Ondrej Skopekccd02e0492017-07-10 13:28:44495 bool home_tile_added = false;
fhorschigd3222452017-05-22 08:36:44496
Ondrej Skopek46ab3ff2017-07-21 12:05:12497 for (auto& tile : tiles) {
498 if (new_tiles.size() >= num_sites_) {
499 break;
500 }
Ondrej Skopekccd02e0492017-07-10 13:28:44501
Friedrich Horschig8f4cce62017-07-07 11:25:13502 // TODO(fhorschig): Introduce a more sophisticated deduplication.
Ondrej Skopek46ab3ff2017-07-21 12:05:12503 if (tile.url.host() == home_page_url.host()) {
504 tile.source = TileSource::HOMEPAGE;
Ondrej Skopekccd02e0492017-07-10 13:28:44505 home_tile_added = true;
fhorschigd3222452017-05-22 08:36:44506 }
Ondrej Skopek46ab3ff2017-07-21 12:05:12507 new_tiles.push_back(std::move(tile));
Ondrej Skopekccd02e0492017-07-10 13:28:44508 }
509
510 // Add the home page tile if there are less than 4 tiles
511 // and none of them is the home page (and there is space left).
Ondrej Skopek46ab3ff2017-07-21 12:05:12512 if (!home_tile_added) {
513 // Make room for the home page tile.
514 if (new_tiles.size() >= num_sites_) {
515 new_tiles.pop_back();
516 }
517
518 NTPTile home_tile;
519 home_tile.url = home_page_url;
520 home_tile.title = title;
521 home_tile.source = TileSource::HOMEPAGE;
Friedrich Horschigefec0812017-09-28 18:50:17522 home_tile.title_source = TileTitleSource::TITLE_TAG; // From history.
Ondrej Skopek46ab3ff2017-07-21 12:05:12523
Ondrej Skopekccd02e0492017-07-10 13:28:44524 new_tiles.push_back(std::move(home_tile));
fhorschigd3222452017-05-22 08:36:44525 }
526 return new_tiles;
527}
528
Friedrich Horschig8f4cce62017-07-07 11:25:13529void MostVisitedSites::InitiateNotificationForNewTiles(
530 NTPTilesVector new_tiles) {
531 if (ShouldAddHomeTile() && !HasHomeTile(new_tiles)) {
532 home_page_client_->QueryHomePageTitle(
533 base::BindOnce(&MostVisitedSites::OnHomePageTitleDetermined,
534 base::Unretained(this), new_tiles));
535 // Don't wait for the homepage title from history but immediately serve a
536 // copy of new tiles.
537 new_tiles = InsertHomeTile(std::move(new_tiles), base::string16());
538 }
539 SaveTilesAndNotify(std::move(new_tiles));
540}
541
542void MostVisitedSites::SaveTilesAndNotify(NTPTilesVector personal_tiles) {
fhorschigd3222452017-05-22 08:36:44543 std::set<std::string> used_hosts;
544 size_t num_actual_tiles = 0u;
fhorschigd3222452017-05-22 08:36:44545 AddToHostsAndTotalCount(personal_tiles, &used_hosts, &num_actual_tiles);
546
547 NTPTilesVector whitelist_tiles =
548 CreateWhitelistEntryPointTiles(used_hosts, num_actual_tiles);
549 AddToHostsAndTotalCount(whitelist_tiles, &used_hosts, &num_actual_tiles);
550
Friedrich Horschig9e8749f02017-08-10 09:17:31551 std::map<SectionType, NTPTilesVector> sections =
552 CreatePopularSitesSections(used_hosts, num_actual_tiles);
553 AddToHostsAndTotalCount(sections[SectionType::PERSONALIZED], &used_hosts,
554 &num_actual_tiles);
treib5a59f542016-05-18 12:17:29555
sfiera3140e142017-03-07 16:00:48556 NTPTilesVector new_tiles =
sfiera635d21d2016-08-04 07:56:09557 MergeTiles(std::move(personal_tiles), std::move(whitelist_tiles),
Friedrich Horschig9e8749f02017-08-10 09:17:31558 std::move(sections[SectionType::PERSONALIZED]));
sfiera3140e142017-03-07 16:00:48559 if (current_tiles_.has_value() && (*current_tiles_ == new_tiles)) {
560 return;
561 }
562
563 current_tiles_.emplace(std::move(new_tiles));
564 DCHECK_EQ(num_actual_tiles, current_tiles_->size());
treib5a59f542016-05-18 12:17:29565
sfiera635d21d2016-08-04 07:56:09566 int num_personal_tiles = 0;
sfiera3140e142017-03-07 16:00:48567 for (const auto& tile : *current_tiles_) {
treib231212e2017-04-04 17:09:22568 if (tile.source != TileSource::POPULAR)
sfiera635d21d2016-08-04 07:56:09569 num_personal_tiles++;
treibcd3d2ef2016-07-13 16:53:31570 }
sfiera635d21d2016-08-04 07:56:09571 prefs_->SetInteger(prefs::kNumPersonalTiles, num_personal_tiles);
sfiera3140e142017-03-07 16:00:48572 if (!observer_)
573 return;
Friedrich Horschig9e8749f02017-08-10 09:17:31574 sections[SectionType::PERSONALIZED] = *current_tiles_;
575 observer_->OnURLsAvailable(sections);
treibcf674e62015-08-17 15:43:01576}
577
578// static
sfiera635d21d2016-08-04 07:56:09579NTPTilesVector MostVisitedSites::MergeTiles(NTPTilesVector personal_tiles,
580 NTPTilesVector whitelist_tiles,
581 NTPTilesVector popular_tiles) {
582 NTPTilesVector merged_tiles;
583 std::move(personal_tiles.begin(), personal_tiles.end(),
584 std::back_inserter(merged_tiles));
585 std::move(whitelist_tiles.begin(), whitelist_tiles.end(),
586 std::back_inserter(merged_tiles));
587 std::move(popular_tiles.begin(), popular_tiles.end(),
588 std::back_inserter(merged_tiles));
589 return merged_tiles;
treibcffa6502015-08-06 09:12:27590}
591
mastizfd2c7ab2017-01-27 19:35:00592void MostVisitedSites::OnPopularSitesDownloaded(bool success) {
treibcffa6502015-08-06 09:12:27593 if (!success) {
594 LOG(WARNING) << "Download of popular sites failed";
595 return;
596 }
bauerb21e371e2017-03-03 18:48:29597
Friedrich Horschig9e8749f02017-08-10 09:17:31598 for (const auto& section : popular_sites_->sections()) {
599 for (const PopularSites::Site& site : section.second) {
600 // Ignore callback; these icons will be seen on the *next* NTP.
601 icon_cacher_->StartFetchPopularSites(site, base::Closure(),
602 base::Closure());
603 }
bauerb21e371e2017-03-03 18:48:29604 }
treibcffa6502015-08-06 09:12:27605}
606
fhorschigfed34be2017-03-02 23:16:09607void MostVisitedSites::OnIconMadeAvailable(const GURL& site_url) {
608 observer_->OnIconMadeAvailable(site_url);
sfieraae1ae692016-10-24 18:19:15609}
610
treib16070ce2016-03-11 11:57:40611void MostVisitedSites::TopSitesLoaded(TopSites* top_sites) {}
sdefresneedf9e01f2015-01-13 19:45:41612
treib16070ce2016-03-11 11:57:40613void MostVisitedSites::TopSitesChanged(TopSites* top_sites,
fserbdb575112015-06-29 21:31:59614 ChangeReason change_reason) {
treib231212e2017-04-04 17:09:22615 if (mv_source_ == TileSource::TOP_SITES) {
sfiera635d21d2016-08-04 07:56:09616 // The displayed tiles are invalidated.
newt2a47ce72015-10-01 20:09:49617 InitiateTopSitesQuery();
sdefresneedf9e01f2015-01-13 19:45:41618 }
619}
sfiera08009fe2016-06-15 17:07:26620
fhorschigd3222452017-05-22 08:36:44621bool MostVisitedSites::ShouldAddHomeTile() const {
fhorschigf770fd92017-06-01 15:51:20622 return num_sites_ > 0u &&
fhorschig3fc1f812017-06-01 12:28:28623 home_page_client_ && // No platform-specific implementation - no tile.
fhorschigd3222452017-05-22 08:36:44624 home_page_client_->IsHomePageEnabled() &&
625 !home_page_client_->IsNewTabPageUsedAsHomePage() &&
Friedrich Horschig8f4cce62017-07-07 11:25:13626 !home_page_client_->GetHomePageUrl().is_empty() &&
fhorschigd3222452017-05-22 08:36:44627 !(top_sites_ &&
Friedrich Horschig8f4cce62017-07-07 11:25:13628 top_sites_->IsBlacklisted(home_page_client_->GetHomePageUrl()));
fhorschigd3222452017-05-22 08:36:44629}
630
631void MostVisitedSites::AddToHostsAndTotalCount(const NTPTilesVector& new_tiles,
632 std::set<std::string>* hosts,
633 size_t* total_tile_count) const {
634 for (const auto& tile : new_tiles) {
635 hosts->insert(tile.url.host());
636 }
637 *total_tile_count += new_tiles.size();
638 DCHECK_LE(*total_tile_count, num_sites_);
639}
640
sfiera08009fe2016-06-15 17:07:26641} // namespace ntp_tiles