blob: 8c62dc6c1e996d778ca464e0236d8a0945262c50 [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>
8#include <set>
dchengbc6125d2015-12-30 01:54:079#include <utility>
10
[email protected]d33adb40d2014-06-03 21:38:4611#include "base/callback.h"
treibcffa6502015-08-06 09:12:2712#include "base/command_line.h"
mastizf3468a82016-06-15 11:05:4113#include "base/feature_list.h"
zea9c7707d2015-11-03 21:41:1414#include "base/metrics/field_trial.h"
[email protected]9ed2f5c2014-06-13 14:55:2415#include "base/metrics/histogram.h"
[email protected]1d778e12014-06-17 02:28:5116#include "base/metrics/sparse_histogram.h"
[email protected]9ed2f5c2014-06-13 14:55:2417#include "base/strings/string_number_conversions.h"
treibeba6cee2015-09-09 13:25:5918#include "base/strings/string_util.h"
[email protected]9ed2f5c2014-06-13 14:55:2419#include "base/strings/stringprintf.h"
[email protected]bc093e82014-03-04 21:49:1120#include "base/strings/utf_string_conversions.h"
[email protected]6d21c702014-05-15 06:10:2121#include "base/time/time.h"
sdefresne0da3bc02015-01-29 18:26:3522#include "components/history/core/browser/top_sites.h"
sfiera0dc4da52016-04-29 09:23:0523#include "components/ntp_tiles/pref_names.h"
24#include "components/ntp_tiles/switches.h"
knncbbd754d2015-09-09 15:43:4025#include "components/pref_registry/pref_registry_syncable.h"
brettwb1fc1b82016-02-02 00:19:0826#include "components/prefs/pref_service.h"
zea9c7707d2015-11-03 21:41:1427#include "components/variations/variations_associated_data.h"
[email protected]c764b2822013-07-03 04:16:4828#include "third_party/skia/include/core/SkBitmap.h"
[email protected]c764b2822013-07-03 04:16:4829#include "ui/gfx/codec/jpeg_codec.h"
treibcffa6502015-08-06 09:12:2730#include "url/gurl.h"
[email protected]c764b2822013-07-03 04:16:4831
[email protected]c764b2822013-07-03 04:16:4832using history::TopSites;
[email protected]bc093e82014-03-04 21:49:1133using suggestions::ChromeSuggestion;
34using suggestions::SuggestionsProfile;
sfiera7e952d42016-05-04 17:56:3335using suggestions::SuggestionsService;
[email protected]c764b2822013-07-03 04:16:4836
[email protected]c764b2822013-07-03 04:16:4837namespace {
38
treib783a3372015-08-25 16:24:3939// Identifiers for the various tile sources.
40const char kHistogramClientName[] = "client";
41const char kHistogramServerName[] = "server";
42const char kHistogramServerFormat[] = "server%d";
43const char kHistogramPopularName[] = "popular";
atanasova9572aaf2016-02-26 18:08:2644const char kHistogramWhitelistName[] = "whitelist";
[email protected]9ed2f5c2014-06-13 14:55:2445
treib508939a2015-08-25 10:07:5646const char kPopularSitesFieldTrialName[] = "NTPPopularSites";
47
mastizf3468a82016-06-15 11:05:4148const base::Feature kDisplaySuggestionsServiceTiles{
49 "DisplaySuggestionsServiceTiles", base::FEATURE_ENABLED_BY_DEFAULT};
50
newt2a47ce72015-10-01 20:09:4951// The visual type of a most visited tile.
52//
53// These values must stay in sync with the MostVisitedTileType enum
54// in histograms.xml.
55//
56// A Java counterpart will be generated for this enum.
57// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp
58enum MostVisitedTileType {
59 // The icon or thumbnail hasn't loaded yet.
60 NONE,
61 // The item displays a site's actual favicon or touch icon.
62 ICON_REAL,
63 // The item displays a color derived from the site's favicon or touch icon.
64 ICON_COLOR,
65 // The item displays a default gray box in place of an icon.
66 ICON_DEFAULT,
newt2a47ce72015-10-01 20:09:4967 NUM_TILE_TYPES,
68};
69
sfiera3e0b88b2016-05-27 12:28:3870// May only be called from blocking thread pool.
mastizc60d74e2016-06-14 14:02:4971std::unique_ptr<SkBitmap> TryFetchLocalThumbnail(
knn6cf777fc2015-05-06 13:21:4872 const GURL& url,
73 const scoped_refptr<TopSites>& top_sites) {
knn6cf777fc2015-05-06 13:21:4874 scoped_refptr<base::RefCountedMemory> image;
dchengaeceb05e2016-04-07 23:41:1075 std::unique_ptr<SkBitmap> bitmap;
mastizc60d74e2016-06-14 14:02:4976 if (top_sites->GetPageThumbnail(url, false, &image))
dchengb587f9a42016-04-11 19:51:2577 bitmap = gfx::JPEGCodec::Decode(image->front(), image->size());
dchengbc6125d2015-12-30 01:54:0778 return bitmap;
[email protected]d33adb40d2014-06-03 21:38:4679}
80
[email protected]92a598822014-06-17 10:25:5681// Log an event for a given |histogram| at a given element |position|. This
82// routine exists because regular histogram macros are cached thus can't be used
83// if the name of the histogram will change at a given call site.
treib783a3372015-08-25 16:24:3984void LogHistogramEvent(const std::string& histogram,
85 int position,
[email protected]92a598822014-06-17 10:25:5686 int num_sites) {
[email protected]9ed2f5c2014-06-13 14:55:2487 base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
[email protected]92a598822014-06-17 10:25:5688 histogram,
[email protected]9ed2f5c2014-06-13 14:55:2489 1,
90 num_sites,
91 num_sites + 1,
92 base::Histogram::kUmaTargetedHistogramFlag);
[email protected]6f674482014-08-11 22:50:4993 if (counter)
94 counter->Add(position);
[email protected]9ed2f5c2014-06-13 14:55:2495}
96
treibcffa6502015-08-06 09:12:2797bool ShouldShowPopularSites() {
98 // Note: It's important to query the field trial state first, to ensure that
99 // UMA reports the correct group.
100 const std::string group_name =
treib508939a2015-08-25 10:07:56101 base::FieldTrialList::FindFullName(kPopularSitesFieldTrialName);
treibcffa6502015-08-06 09:12:27102 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
sfiera0dc4da52016-04-29 09:23:05103 if (cmd_line->HasSwitch(ntp_tiles::switches::kDisableNTPPopularSites))
treibcffa6502015-08-06 09:12:27104 return false;
sfiera0dc4da52016-04-29 09:23:05105 if (cmd_line->HasSwitch(ntp_tiles::switches::kEnableNTPPopularSites))
treibcffa6502015-08-06 09:12:27106 return true;
treibeba6cee2015-09-09 13:25:59107 return base::StartsWith(group_name, "Enabled",
108 base::CompareCase::INSENSITIVE_ASCII);
treibcffa6502015-08-06 09:12:27109}
110
treibb28d6502015-09-22 14:28:50111std::string GetPopularSitesCountry() {
112 return variations::GetVariationParamValue(kPopularSitesFieldTrialName,
113 "country");
114}
115
116std::string GetPopularSitesVersion() {
117 return variations::GetVariationParamValue(kPopularSitesFieldTrialName,
118 "version");
119}
120
treibf79ed40d2015-09-22 12:38:06121// Determine whether we need any popular suggestions to fill up a grid of
122// |num_tiles| tiles.
123bool NeedPopularSites(const PrefService* prefs, size_t num_tiles) {
124 const base::ListValue* source_list =
sfiera0dc4da52016-04-29 09:23:05125 prefs->GetList(ntp_tiles::prefs::kNTPSuggestionsIsPersonal);
treibf79ed40d2015-09-22 12:38:06126 // If there aren't enough previous suggestions to fill the grid, we need
127 // popular suggestions.
128 if (source_list->GetSize() < num_tiles)
129 return true;
130 // Otherwise, if any of the previous suggestions is not personal, then also
131 // get popular suggestions.
132 for (size_t i = 0; i < num_tiles; ++i) {
133 bool is_personal = false;
134 if (source_list->GetBoolean(i, &is_personal) && !is_personal)
135 return true;
136 }
137 // The whole grid is already filled with personal suggestions, no point in
138 // bothering with popular ones.
139 return false;
140}
141
atanasova9c0dda602016-03-22 17:41:13142bool AreURLsEquivalent(const GURL& url1, const GURL& url2) {
143 return url1.host() == url2.host() && url1.path() == url2.path();
144}
145
sfiera8df7b382016-04-26 14:06:33146std::string GetSourceHistogramName(
147 const MostVisitedSites::Suggestion& suggestion) {
148 switch (suggestion.source) {
knncbbd754d2015-09-09 15:43:40149 case MostVisitedSites::TOP_SITES:
150 return kHistogramClientName;
151 case MostVisitedSites::POPULAR:
152 return kHistogramPopularName;
atanasova9572aaf2016-02-26 18:08:26153 case MostVisitedSites::WHITELIST:
154 return kHistogramWhitelistName;
knncbbd754d2015-09-09 15:43:40155 case MostVisitedSites::SUGGESTIONS_SERVICE:
sfiera8df7b382016-04-26 14:06:33156 return suggestion.provider_index >= 0
157 ? base::StringPrintf(kHistogramServerFormat,
158 suggestion.provider_index)
knncbbd754d2015-09-09 15:43:40159 : kHistogramServerName;
160 }
161 NOTREACHED();
162 return std::string();
163}
164
sfiera8df7b382016-04-26 14:06:33165} // namespace
166
167MostVisitedSites::Suggestion::Suggestion() : provider_index(-1) {}
168
169MostVisitedSites::Suggestion::~Suggestion() {}
170
171MostVisitedSites::Suggestion::Suggestion(Suggestion&&) = default;
172MostVisitedSites::Suggestion&
173MostVisitedSites::Suggestion::operator=(Suggestion&&) = default;
174
sfiera9a201522016-05-02 10:47:58175MostVisitedSites::MostVisitedSites(
sfiera3e0b88b2016-05-27 12:28:38176 scoped_refptr<base::SequencedWorkerPool> blocking_pool,
sfiera7e952d42016-05-04 17:56:33177 PrefService* prefs,
178 const TemplateURLService* template_url_service,
179 variations::VariationsService* variations_service,
180 net::URLRequestContextGetter* download_context,
sfiera685a987e2016-05-12 17:08:47181 const base::FilePath& popular_sites_directory,
sfiera7e952d42016-05-04 17:56:33182 scoped_refptr<history::TopSites> top_sites,
183 SuggestionsService* suggestions,
sfiera79e643b2016-05-13 16:52:49184 MostVisitedSitesSupervisor* supervisor)
185 : prefs_(prefs),
sfiera7e952d42016-05-04 17:56:33186 template_url_service_(template_url_service),
187 variations_service_(variations_service),
sfiera685a987e2016-05-12 17:08:47188 download_context_(download_context),
189 popular_sites_directory_(popular_sites_directory),
190 top_sites_(top_sites),
191 suggestions_service_(suggestions),
sfiera79e643b2016-05-13 16:52:49192 supervisor_(supervisor),
sfiera685a987e2016-05-12 17:08:47193 observer_(nullptr),
194 num_sites_(0),
195 received_most_visited_sites_(false),
196 received_popular_sites_(false),
197 recorded_uma_(false),
198 scoped_observer_(this),
199 mv_source_(SUGGESTIONS_SERVICE),
sfiera3e0b88b2016-05-27 12:28:38200 blocking_pool_(std::move(blocking_pool)),
201 blocking_runner_(blocking_pool_->GetTaskRunnerWithShutdownBehavior(
202 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)),
sfiera7e952d42016-05-04 17:56:33203 weak_ptr_factory_(this) {
mastizc60d74e2016-06-14 14:02:49204 DCHECK(top_sites_);
205 DCHECK(suggestions_service_);
sfiera79e643b2016-05-13 16:52:49206 supervisor_->SetObserver(this);
[email protected]7ceb4e32013-12-06 04:13:04207}
[email protected]c764b2822013-07-03 04:16:48208
[email protected]7ceb4e32013-12-06 04:13:04209MostVisitedSites::~MostVisitedSites() {
sfiera79e643b2016-05-13 16:52:49210 supervisor_->SetObserver(nullptr);
[email protected]7ceb4e32013-12-06 04:13:04211}
[email protected]c764b2822013-07-03 04:16:48212
treib5a59f542016-05-18 12:17:29213void MostVisitedSites::SetMostVisitedURLsObserver(Observer* observer,
214 int num_sites) {
treibc1bd1f12016-04-29 15:06:05215 DCHECK(observer);
sfiera929502f32016-04-21 12:52:48216 observer_ = observer;
[email protected]7ceb4e32013-12-06 04:13:04217 num_sites_ = num_sites;
[email protected]852fc792013-10-04 04:15:12218
treibf79ed40d2015-09-22 12:38:06219 if (ShouldShowPopularSites() &&
sfiera7e952d42016-05-04 17:56:33220 NeedPopularSites(prefs_, num_sites_)) {
treibf79ed40d2015-09-22 12:38:06221 popular_sites_.reset(new PopularSites(
sfiera3e0b88b2016-05-27 12:28:38222 blocking_pool_, prefs_, template_url_service_, variations_service_,
223 download_context_, popular_sites_directory_, GetPopularSitesCountry(),
sfiera685a987e2016-05-12 17:08:47224 GetPopularSitesVersion(), false,
treibf79ed40d2015-09-22 12:38:06225 base::Bind(&MostVisitedSites::OnPopularSitesAvailable,
226 base::Unretained(this))));
227 } else {
228 received_popular_sites_ = true;
229 }
230
mastizc60d74e2016-06-14 14:02:49231 // TopSites updates itself after a delay. To ensure up-to-date results,
232 // force an update now.
233 top_sites_->SyncWithHistory();
[email protected]7ceb4e32013-12-06 04:13:04234
mastizc60d74e2016-06-14 14:02:49235 // Register as TopSitesObserver so that we can update ourselves when the
236 // TopSites changes.
237 scoped_observer_.Add(top_sites_.get());
treibcb9c93b2016-04-01 13:22:52238
sfiera728962f2016-04-29 11:04:02239 suggestions_subscription_ = suggestions_service_->AddCallback(
treibcb9c93b2016-04-01 13:22:52240 base::Bind(&MostVisitedSites::OnSuggestionsProfileAvailable,
241 base::Unretained(this)));
242
treibc1bd1f12016-04-29 15:06:05243 // Immediately build the current suggestions, getting personal suggestions
244 // from the SuggestionsService's cache or, if that is empty, from TopSites.
245 BuildCurrentSuggestions();
treibcb9c93b2016-04-01 13:22:52246 // Also start a request for fresh suggestions.
sfiera728962f2016-04-29 11:04:02247 suggestions_service_->FetchSuggestionsData();
[email protected]c764b2822013-07-03 04:16:48248}
249
treib5a59f542016-05-18 12:17:29250void MostVisitedSites::GetURLThumbnail(const GURL& url,
251 const ThumbnailCallback& callback) {
sfiera3e0b88b2016-05-27 12:28:38252 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]d33adb40d2014-06-03 21:38:46253
sfiera3e0b88b2016-05-27 12:28:38254 base::PostTaskAndReplyWithResult(
255 blocking_runner_.get(), FROM_HERE,
mastizc60d74e2016-06-14 14:02:49256 base::Bind(&TryFetchLocalThumbnail, url, top_sites_),
knn6cf777fc2015-05-06 13:21:48257 base::Bind(&MostVisitedSites::OnLocalThumbnailFetched,
sfiera1e7993c62016-04-18 09:43:26258 weak_ptr_factory_.GetWeakPtr(), url, callback));
knn6cf777fc2015-05-06 13:21:48259}
260
261void MostVisitedSites::OnLocalThumbnailFetched(
262 const GURL& url,
sfiera1e7993c62016-04-18 09:43:26263 const ThumbnailCallback& callback,
dchengaeceb05e2016-04-07 23:41:10264 std::unique_ptr<SkBitmap> bitmap) {
sfiera3e0b88b2016-05-27 12:28:38265 DCHECK(thread_checker_.CalledOnValidThread());
markusheintz55ae1162016-05-25 08:20:57266 if (bitmap.get()) {
267 callback.Run(true /* is_local_thumbnail */, bitmap.get());
268 return;
269 }
270
271 // A thumbnail is not locally available for |url|. Make sure it is put in
272 // the list to be fetched at the next visit to this site.
mastizc60d74e2016-06-14 14:02:49273 top_sites_->AddForcedURL(url, base::Time::Now());
markusheintz55ae1162016-05-25 08:20:57274 // Also fetch a remote thumbnail if possible. PopularSites or the
275 // SuggestionsService can supply a thumbnail download URL.
276 if (popular_sites_) {
277 const std::vector<PopularSites::Site>& sites = popular_sites_->sites();
278 auto it = std::find_if(
279 sites.begin(), sites.end(),
280 [&url](const PopularSites::Site& site) { return site.url == url; });
281 if (it != sites.end() && it->thumbnail_url.is_valid()) {
282 return suggestions_service_->GetPageThumbnailWithURL(
283 url, it->thumbnail_url,
284 base::Bind(&MostVisitedSites::OnObtainedThumbnail,
285 weak_ptr_factory_.GetWeakPtr(), false, callback));
knn6cf777fc2015-05-06 13:21:48286 }
287 }
markusheintz55ae1162016-05-25 08:20:57288 if (mv_source_ == SUGGESTIONS_SERVICE) {
289 return suggestions_service_->GetPageThumbnail(
290 url, base::Bind(&MostVisitedSites::OnObtainedThumbnail,
291 weak_ptr_factory_.GetWeakPtr(), false, callback));
292 }
293 // If no bitmap could be fetched and neither PopularSites nor the
294 // SuggestionsService is available then a nullptr is passed to the callback.
295 callback.Run(true /* is_local_thumbnail */, nullptr);
knn6cf777fc2015-05-06 13:21:48296}
297
treib5a59f542016-05-18 12:17:29298void MostVisitedSites::OnObtainedThumbnail(bool is_local_thumbnail,
299 const ThumbnailCallback& callback,
300 const GURL& url,
markusheintz55ae1162016-05-25 08:20:57301 const gfx::Image& image) {
sfiera3e0b88b2016-05-27 12:28:38302 DCHECK(thread_checker_.CalledOnValidThread());
markusheintz55ae1162016-05-25 08:20:57303 const SkBitmap* bitmap = nullptr;
304 if (!image.IsEmpty())
305 bitmap = image.ToSkBitmap();
sfiera1e7993c62016-04-18 09:43:26306 callback.Run(is_local_thumbnail, bitmap);
307}
308
treib5a59f542016-05-18 12:17:29309void MostVisitedSites::AddOrRemoveBlacklistedUrl(const GURL& url,
310 bool add_url) {
treibfac75192015-08-17 13:21:24311 // Always blacklist in the local TopSites.
mastizc60d74e2016-06-14 14:02:49312 if (add_url)
313 top_sites_->AddBlacklistedURL(url);
314 else
315 top_sites_->RemoveBlacklistedURL(url);
[email protected]d4975242014-06-02 18:16:20316
treibfac75192015-08-17 13:21:24317 // Only blacklist in the server-side suggestions service if it's active.
318 if (mv_source_ == SUGGESTIONS_SERVICE) {
treib16070ce2016-03-11 11:57:40319 if (add_url)
sfiera728962f2016-04-29 11:04:02320 suggestions_service_->BlacklistURL(url);
treib16070ce2016-03-11 11:57:40321 else
sfiera728962f2016-04-29 11:04:02322 suggestions_service_->UndoBlacklistURL(url);
[email protected]d4975242014-06-02 18:16:20323 }
[email protected]a4c2f282013-07-20 05:26:05324}
[email protected]7ceb4e32013-12-06 04:13:04325
tornee621a272015-11-30 18:40:53326void MostVisitedSites::RecordTileTypeMetrics(
sfiera1e7993c62016-04-18 09:43:26327 const std::vector<int>& tile_types) {
sfiera929502f32016-04-21 12:52:48328 DCHECK_EQ(current_suggestions_.size(), tile_types.size());
newt2a47ce72015-10-01 20:09:49329 int counts_per_type[NUM_TILE_TYPES] = {0};
330 for (size_t i = 0; i < tile_types.size(); ++i) {
331 int tile_type = tile_types[i];
332 ++counts_per_type[tile_type];
333 std::string histogram = base::StringPrintf(
334 "NewTabPage.TileType.%s",
sfiera8df7b382016-04-26 14:06:33335 GetSourceHistogramName(current_suggestions_[i]).c_str());
newt2a47ce72015-10-01 20:09:49336 LogHistogramEvent(histogram, tile_type, NUM_TILE_TYPES);
337 }
338
Newton Allenea5f0c02015-12-04 03:02:41339 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsReal",
340 counts_per_type[ICON_REAL]);
341 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsColor",
342 counts_per_type[ICON_COLOR]);
343 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsGray",
344 counts_per_type[ICON_DEFAULT]);
newt2a47ce72015-10-01 20:09:49345}
346
sfiera1e7993c62016-04-18 09:43:26347void MostVisitedSites::RecordOpenedMostVisitedItem(int index, int tile_type) {
treib783a3372015-08-25 16:24:39348 DCHECK_GE(index, 0);
knncbbd754d2015-09-09 15:43:40349 DCHECK_LT(index, static_cast<int>(current_suggestions_.size()));
350 std::string histogram = base::StringPrintf(
newt2a47ce72015-10-01 20:09:49351 "NewTabPage.MostVisited.%s",
sfiera8df7b382016-04-26 14:06:33352 GetSourceHistogramName(current_suggestions_[index]).c_str());
treib783a3372015-08-25 16:24:39353 LogHistogramEvent(histogram, index, num_sites_);
newt2a47ce72015-10-01 20:09:49354
355 histogram = base::StringPrintf(
356 "NewTabPage.TileTypeClicked.%s",
sfiera8df7b382016-04-26 14:06:33357 GetSourceHistogramName(current_suggestions_[index]).c_str());
newt2a47ce72015-10-01 20:09:49358 LogHistogramEvent(histogram, tile_type, NUM_TILE_TYPES);
[email protected]92a598822014-06-17 10:25:56359}
360
sfiera79e643b2016-05-13 16:52:49361void MostVisitedSites::OnBlockedSitesChanged() {
treibc1bd1f12016-04-29 15:06:05362 BuildCurrentSuggestions();
atanasova9572aaf2016-02-26 18:08:26363}
364
[email protected]7ceb4e32013-12-06 04:13:04365// static
knncbbd754d2015-09-09 15:43:40366void MostVisitedSites::RegisterProfilePrefs(
367 user_prefs::PrefRegistrySyncable* registry) {
treib5a59f542016-05-18 12:17:29368 // TODO(treib): Remove this, it's unused. Do we need migration code to clean
369 // up existing entries?
sfiera0dc4da52016-04-29 09:23:05370 registry->RegisterListPref(ntp_tiles::prefs::kNTPSuggestionsURL);
treib5a59f542016-05-18 12:17:29371 // TODO(treib): Remove this. It's only used to determine if we need
372 // PopularSites at all. Find a way to do that without prefs, or failing that,
373 // replace this list pref by a simple bool.
sfiera0dc4da52016-04-29 09:23:05374 registry->RegisterListPref(ntp_tiles::prefs::kNTPSuggestionsIsPersonal);
knncbbd754d2015-09-09 15:43:40375}
376
treibc1bd1f12016-04-29 15:06:05377void MostVisitedSites::BuildCurrentSuggestions() {
378 // Get the current suggestions from cache. If the cache is empty, this will
379 // fall back to TopSites.
treibba0dee72016-03-31 13:25:59380 OnSuggestionsProfileAvailable(
sfiera728962f2016-04-29 11:04:02381 suggestions_service_->GetSuggestionsDataFromCache());
[email protected]bc093e82014-03-04 21:49:11382}
383
384void MostVisitedSites::InitiateTopSitesQuery() {
sfiera728962f2016-04-29 11:04:02385 top_sites_->GetMostVisitedURLs(
knn6cf777fc2015-05-06 13:21:48386 base::Bind(&MostVisitedSites::OnMostVisitedURLsAvailable,
387 weak_ptr_factory_.GetWeakPtr()),
[email protected]7ceb4e32013-12-06 04:13:04388 false);
389}
390
atanasova9c0dda602016-03-22 17:41:13391base::FilePath MostVisitedSites::GetWhitelistLargeIconPath(const GURL& url) {
sfiera79e643b2016-05-13 16:52:49392 for (const auto& whitelist : supervisor_->whitelists()) {
393 if (AreURLsEquivalent(whitelist.entry_point, url))
394 return whitelist.large_icon_path;
atanasova9c0dda602016-03-22 17:41:13395 }
396 return base::FilePath();
397}
398
[email protected]d4975242014-06-02 18:16:20399void MostVisitedSites::OnMostVisitedURLsAvailable(
[email protected]d4975242014-06-02 18:16:20400 const history::MostVisitedURLList& visited_list) {
treib5a59f542016-05-18 12:17:29401 SuggestionsPtrVector suggestions;
knncbbd754d2015-09-09 15:43:40402 size_t num_tiles =
403 std::min(visited_list.size(), static_cast<size_t>(num_sites_));
404 for (size_t i = 0; i < num_tiles; ++i) {
knn6cf777fc2015-05-06 13:21:48405 const history::MostVisitedURL& visited = visited_list[i];
406 if (visited.url.is_empty()) {
407 num_tiles = i;
408 break; // This is the signal that there are no more real visited sites.
409 }
sfiera79e643b2016-05-13 16:52:49410 if (supervisor_->IsBlocked(visited.url))
atanasova9514b302016-02-26 23:09:28411 continue;
atanasova9514b302016-02-26 23:09:28412
dchengaeceb05e2016-04-07 23:41:10413 std::unique_ptr<Suggestion> suggestion(new Suggestion());
atanasovac3c25d12016-03-17 18:33:57414 suggestion->title = visited.title;
415 suggestion->url = visited.url;
416 suggestion->source = TOP_SITES;
atanasova9c0dda602016-03-22 17:41:13417 suggestion->whitelist_icon_path = GetWhitelistLargeIconPath(visited.url);
atanasovac3c25d12016-03-17 18:33:57418
419 suggestions.push_back(std::move(suggestion));
knn6cf777fc2015-05-06 13:21:48420 }
[email protected]d4975242014-06-02 18:16:20421
treib783a3372015-08-25 16:24:39422 received_most_visited_sites_ = true;
knn6cf777fc2015-05-06 13:21:48423 mv_source_ = TOP_SITES;
treib5a59f542016-05-18 12:17:29424 SaveNewSuggestions(&suggestions);
knncbbd754d2015-09-09 15:43:40425 NotifyMostVisitedURLsObserver();
[email protected]d4975242014-06-02 18:16:20426}
427
[email protected]bc093e82014-03-04 21:49:11428void MostVisitedSites::OnSuggestionsProfileAvailable(
[email protected]bc093e82014-03-04 21:49:11429 const SuggestionsProfile& suggestions_profile) {
knn6cf777fc2015-05-06 13:21:48430 int num_tiles = suggestions_profile.suggestions_size();
treibc1bd1f12016-04-29 15:06:05431 // With no server suggestions, fall back to local TopSites.
mastizf3468a82016-06-15 11:05:41432 if (num_tiles == 0 ||
433 !base::FeatureList::IsEnabled(kDisplaySuggestionsServiceTiles)) {
[email protected]bc093e82014-03-04 21:49:11434 InitiateTopSitesQuery();
435 return;
436 }
knn6cf777fc2015-05-06 13:21:48437 if (num_sites_ < num_tiles)
438 num_tiles = num_sites_;
[email protected]bc093e82014-03-04 21:49:11439
treib5a59f542016-05-18 12:17:29440 SuggestionsPtrVector suggestions;
knn6cf777fc2015-05-06 13:21:48441 for (int i = 0; i < num_tiles; ++i) {
[email protected]bc093e82014-03-04 21:49:11442 const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
sfiera79e643b2016-05-13 16:52:49443 if (supervisor_->IsBlocked(GURL(suggestion.url())))
atanasova9514b302016-02-26 23:09:28444 continue;
atanasova9514b302016-02-26 23:09:28445
dchengaeceb05e2016-04-07 23:41:10446 std::unique_ptr<Suggestion> generated_suggestion(new Suggestion());
atanasovac3c25d12016-03-17 18:33:57447 generated_suggestion->title = base::UTF8ToUTF16(suggestion.title());
448 generated_suggestion->url = GURL(suggestion.url());
449 generated_suggestion->source = SUGGESTIONS_SERVICE;
atanasova9c0dda602016-03-22 17:41:13450 generated_suggestion->whitelist_icon_path = GetWhitelistLargeIconPath(
451 GURL(suggestion.url()));
atanasovac3c25d12016-03-17 18:33:57452 if (suggestion.providers_size() > 0)
453 generated_suggestion->provider_index = suggestion.providers(0);
454
455 suggestions.push_back(std::move(generated_suggestion));
[email protected]bc093e82014-03-04 21:49:11456 }
treib783a3372015-08-25 16:24:39457
458 received_most_visited_sites_ = true;
[email protected]d4975242014-06-02 18:16:20459 mv_source_ = SUGGESTIONS_SERVICE;
treib5a59f542016-05-18 12:17:29460 SaveNewSuggestions(&suggestions);
knncbbd754d2015-09-09 15:43:40461 NotifyMostVisitedURLsObserver();
knn6cf777fc2015-05-06 13:21:48462}
[email protected]d4975242014-06-02 18:16:20463
sfiera8df7b382016-04-26 14:06:33464MostVisitedSites::SuggestionsPtrVector
atanasova9572aaf2016-02-26 18:08:26465MostVisitedSites::CreateWhitelistEntryPointSuggestions(
treib5a59f542016-05-18 12:17:29466 const SuggestionsPtrVector& personal_suggestions) {
atanasova9572aaf2016-02-26 18:08:26467 size_t num_personal_suggestions = personal_suggestions.size();
knncbbd754d2015-09-09 15:43:40468 DCHECK_LE(num_personal_suggestions, static_cast<size_t>(num_sites_));
treibcffa6502015-08-06 09:12:27469
atanasova9572aaf2016-02-26 18:08:26470 size_t num_whitelist_suggestions = num_sites_ - num_personal_suggestions;
treib5a59f542016-05-18 12:17:29471 SuggestionsPtrVector whitelist_suggestions;
atanasova9572aaf2016-02-26 18:08:26472
atanasova9514b302016-02-26 23:09:28473 std::set<std::string> personal_hosts;
474 for (const auto& suggestion : personal_suggestions)
475 personal_hosts.insert(suggestion->url.host());
atanasova9572aaf2016-02-26 18:08:26476
sfiera79e643b2016-05-13 16:52:49477 for (const auto& whitelist : supervisor_->whitelists()) {
atanasova9514b302016-02-26 23:09:28478 // Skip blacklisted sites.
mastizc60d74e2016-06-14 14:02:49479 if (top_sites_->IsBlacklisted(whitelist.entry_point))
atanasova9514b302016-02-26 23:09:28480 continue;
481
482 // Skip suggestions already present.
sfiera79e643b2016-05-13 16:52:49483 if (personal_hosts.find(whitelist.entry_point.host()) !=
atanasova9514b302016-02-26 23:09:28484 personal_hosts.end())
485 continue;
486
487 // Skip whitelist entry points that are manually blocked.
sfiera79e643b2016-05-13 16:52:49488 if (supervisor_->IsBlocked(whitelist.entry_point))
atanasova9514b302016-02-26 23:09:28489 continue;
atanasova9514b302016-02-26 23:09:28490
dchengaeceb05e2016-04-07 23:41:10491 std::unique_ptr<Suggestion> suggestion(new Suggestion());
sfiera79e643b2016-05-13 16:52:49492 suggestion->title = whitelist.title;
493 suggestion->url = whitelist.entry_point;
atanasovac3c25d12016-03-17 18:33:57494 suggestion->source = WHITELIST;
sfiera79e643b2016-05-13 16:52:49495 suggestion->whitelist_icon_path = whitelist.large_icon_path;
atanasovac3c25d12016-03-17 18:33:57496
497 whitelist_suggestions.push_back(std::move(suggestion));
atanasova9572aaf2016-02-26 18:08:26498 if (whitelist_suggestions.size() >= num_whitelist_suggestions)
499 break;
500 }
501
502 return whitelist_suggestions;
503}
504
sfiera8df7b382016-04-26 14:06:33505MostVisitedSites::SuggestionsPtrVector
atanasova9572aaf2016-02-26 18:08:26506MostVisitedSites::CreatePopularSitesSuggestions(
treib5a59f542016-05-18 12:17:29507 const SuggestionsPtrVector& personal_suggestions,
508 const SuggestionsPtrVector& whitelist_suggestions) {
atanasova9514b302016-02-26 23:09:28509 // For child accounts popular sites suggestions will not be added.
sfiera79e643b2016-05-13 16:52:49510 if (supervisor_->IsChildProfile())
treib5a59f542016-05-18 12:17:29511 return SuggestionsPtrVector();
atanasova9514b302016-02-26 23:09:28512
atanasova9572aaf2016-02-26 18:08:26513 size_t num_suggestions =
514 personal_suggestions.size() + whitelist_suggestions.size();
515 DCHECK_LE(num_suggestions, static_cast<size_t>(num_sites_));
516
knncbbd754d2015-09-09 15:43:40517 // Collect non-blacklisted popular suggestions, skipping those already present
518 // in the personal suggestions.
atanasova9572aaf2016-02-26 18:08:26519 size_t num_popular_sites_suggestions = num_sites_ - num_suggestions;
treib5a59f542016-05-18 12:17:29520 SuggestionsPtrVector popular_sites_suggestions;
treibcffa6502015-08-06 09:12:27521
atanasova9572aaf2016-02-26 18:08:26522 if (num_popular_sites_suggestions > 0 && popular_sites_) {
atanasova9514b302016-02-26 23:09:28523 std::set<std::string> hosts;
atanasova9572aaf2016-02-26 18:08:26524 for (const auto& suggestion : personal_suggestions)
atanasova9514b302016-02-26 23:09:28525 hosts.insert(suggestion->url.host());
526 for (const auto& suggestion : whitelist_suggestions)
527 hosts.insert(suggestion->url.host());
knncbbd754d2015-09-09 15:43:40528 for (const PopularSites::Site& popular_site : popular_sites_->sites()) {
529 // Skip blacklisted sites.
mastizc60d74e2016-06-14 14:02:49530 if (top_sites_->IsBlacklisted(popular_site.url))
knncbbd754d2015-09-09 15:43:40531 continue;
532 std::string host = popular_site.url.host();
atanasova9514b302016-02-26 23:09:28533 // Skip suggestions already present in personal or whitelists.
534 if (hosts.find(host) != hosts.end())
knncbbd754d2015-09-09 15:43:40535 continue;
treibcffa6502015-08-06 09:12:27536
dchengaeceb05e2016-04-07 23:41:10537 std::unique_ptr<Suggestion> suggestion(new Suggestion());
atanasovac3c25d12016-03-17 18:33:57538 suggestion->title = popular_site.title;
539 suggestion->url = GURL(popular_site.url);
540 suggestion->source = POPULAR;
541
542 popular_sites_suggestions.push_back(std::move(suggestion));
atanasova9572aaf2016-02-26 18:08:26543 if (popular_sites_suggestions.size() >= num_popular_sites_suggestions)
knncbbd754d2015-09-09 15:43:40544 break;
545 }
treibcffa6502015-08-06 09:12:27546 }
atanasova9572aaf2016-02-26 18:08:26547 return popular_sites_suggestions;
548}
549
treib5a59f542016-05-18 12:17:29550void MostVisitedSites::SaveNewSuggestions(
551 SuggestionsPtrVector* personal_suggestions) {
552 SuggestionsPtrVector whitelist_suggestions =
atanasova9572aaf2016-02-26 18:08:26553 CreateWhitelistEntryPointSuggestions(*personal_suggestions);
treib5a59f542016-05-18 12:17:29554 SuggestionsPtrVector popular_sites_suggestions =
atanasova9572aaf2016-02-26 18:08:26555 CreatePopularSitesSuggestions(*personal_suggestions,
556 whitelist_suggestions);
treib5a59f542016-05-18 12:17:29557
atanasova9572aaf2016-02-26 18:08:26558 size_t num_actual_tiles = personal_suggestions->size() +
559 whitelist_suggestions.size() +
560 popular_sites_suggestions.size();
treib5a59f542016-05-18 12:17:29561 DCHECK_LE(num_actual_tiles, static_cast<size_t>(num_sites_));
562
563 SuggestionsPtrVector merged_suggestions = MergeSuggestions(
564 personal_suggestions, &whitelist_suggestions, &popular_sites_suggestions);
knncbbd754d2015-09-09 15:43:40565 DCHECK_EQ(num_actual_tiles, merged_suggestions.size());
treib5a59f542016-05-18 12:17:29566
sfiera8df7b382016-04-26 14:06:33567 current_suggestions_.resize(merged_suggestions.size());
568 for (size_t i = 0; i < merged_suggestions.size(); ++i)
569 std::swap(*merged_suggestions[i], current_suggestions_[i]);
treib5a59f542016-05-18 12:17:29570
knncbbd754d2015-09-09 15:43:40571 if (received_popular_sites_)
treib5a59f542016-05-18 12:17:29572 SaveCurrentSuggestionsToPrefs();
treibcf674e62015-08-17 15:43:01573}
574
575// static
sfiera8df7b382016-04-26 14:06:33576MostVisitedSites::SuggestionsPtrVector MostVisitedSites::MergeSuggestions(
treib5a59f542016-05-18 12:17:29577 SuggestionsPtrVector* personal_suggestions,
578 SuggestionsPtrVector* whitelist_suggestions,
579 SuggestionsPtrVector* popular_suggestions) {
knncbbd754d2015-09-09 15:43:40580 size_t num_personal_suggestions = personal_suggestions->size();
atanasova9572aaf2016-02-26 18:08:26581 size_t num_whitelist_suggestions = whitelist_suggestions->size();
knncbbd754d2015-09-09 15:43:40582 size_t num_popular_suggestions = popular_suggestions->size();
atanasova9572aaf2016-02-26 18:08:26583 size_t num_tiles = num_popular_suggestions + num_whitelist_suggestions +
584 num_personal_suggestions;
treib5a59f542016-05-18 12:17:29585 SuggestionsPtrVector merged_suggestions;
586 AppendSuggestions(personal_suggestions, &merged_suggestions);
587 AppendSuggestions(whitelist_suggestions, &merged_suggestions);
588 AppendSuggestions(popular_suggestions, &merged_suggestions);
589 DCHECK_EQ(num_tiles, merged_suggestions.size());
treibcf674e62015-08-17 15:43:01590
dchengbc6125d2015-12-30 01:54:07591 return merged_suggestions;
treibcffa6502015-08-06 09:12:27592}
593
treib5a59f542016-05-18 12:17:29594// TODO(treib): Once we use SuggestionsVector (non-Ptr) everywhere, move this
595// into an anonymous namespace.
596// static
597void MostVisitedSites::AppendSuggestions(SuggestionsPtrVector* src,
598 SuggestionsPtrVector* dst) {
599 dst->insert(dst->end(),
600 std::make_move_iterator(src->begin()),
601 std::make_move_iterator(src->end()));
knncbbd754d2015-09-09 15:43:40602}
603
treib5a59f542016-05-18 12:17:29604void MostVisitedSites::SaveCurrentSuggestionsToPrefs() {
knncbbd754d2015-09-09 15:43:40605 base::ListValue url_list;
606 base::ListValue source_list;
atanasova9572aaf2016-02-26 18:08:26607 for (const auto& suggestion : current_suggestions_) {
sfiera8df7b382016-04-26 14:06:33608 url_list.AppendString(suggestion.url.spec());
treib5a59f542016-05-18 12:17:29609 source_list.AppendBoolean(suggestion.source != POPULAR);
knncbbd754d2015-09-09 15:43:40610 }
sfiera7e952d42016-05-04 17:56:33611 prefs_->Set(ntp_tiles::prefs::kNTPSuggestionsIsPersonal, source_list);
612 prefs_->Set(ntp_tiles::prefs::kNTPSuggestionsURL, url_list);
knncbbd754d2015-09-09 15:43:40613}
614
knncbbd754d2015-09-09 15:43:40615void MostVisitedSites::NotifyMostVisitedURLsObserver() {
treib783a3372015-08-25 16:24:39616 if (received_most_visited_sites_ && received_popular_sites_ &&
617 !recorded_uma_) {
618 RecordImpressionUMAMetrics();
treib5a59f542016-05-18 12:17:29619 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.NumberOfTiles",
620 current_suggestions_.size());
treib783a3372015-08-25 16:24:39621 recorded_uma_ = true;
622 }
treib16070ce2016-03-11 11:57:40623
sfiera1e7993c62016-04-18 09:43:26624 if (!observer_)
treib16070ce2016-03-11 11:57:40625 return;
626
sfiera8df7b382016-04-26 14:06:33627 observer_->OnMostVisitedURLsAvailable(current_suggestions_);
[email protected]bc093e82014-03-04 21:49:11628}
629
treibcffa6502015-08-06 09:12:27630void MostVisitedSites::OnPopularSitesAvailable(bool success) {
treib783a3372015-08-25 16:24:39631 received_popular_sites_ = true;
632
treibcffa6502015-08-06 09:12:27633 if (!success) {
634 LOG(WARNING) << "Download of popular sites failed";
635 return;
636 }
637
treibc1bd1f12016-04-29 15:06:05638 // Pass the popular sites to the observer. This will cause it to fetch any
639 // missing icons, but will *not* cause it to display the popular sites.
sfiera8df7b382016-04-26 14:06:33640 observer_->OnPopularURLsAvailable(popular_sites_->sites());
treibc1bd1f12016-04-29 15:06:05641
642 // Re-build the suggestions list. Once done, this will notify the observer.
643 BuildCurrentSuggestions();
treibcffa6502015-08-06 09:12:27644}
645
treib783a3372015-08-25 16:24:39646void MostVisitedSites::RecordImpressionUMAMetrics() {
knncbbd754d2015-09-09 15:43:40647 for (size_t i = 0; i < current_suggestions_.size(); i++) {
648 std::string histogram = base::StringPrintf(
newt2a47ce72015-10-01 20:09:49649 "NewTabPage.SuggestionsImpression.%s",
sfiera8df7b382016-04-26 14:06:33650 GetSourceHistogramName(current_suggestions_[i]).c_str());
treib783a3372015-08-25 16:24:39651 LogHistogramEvent(histogram, static_cast<int>(i), num_sites_);
652 }
653}
654
treib16070ce2016-03-11 11:57:40655void MostVisitedSites::TopSitesLoaded(TopSites* top_sites) {}
sdefresneedf9e01f2015-01-13 19:45:41656
treib16070ce2016-03-11 11:57:40657void MostVisitedSites::TopSitesChanged(TopSites* top_sites,
fserbdb575112015-06-29 21:31:59658 ChangeReason change_reason) {
sdefresneedf9e01f2015-01-13 19:45:41659 if (mv_source_ == TOP_SITES) {
660 // The displayed suggestions are invalidated.
newt2a47ce72015-10-01 20:09:49661 InitiateTopSitesQuery();
sdefresneedf9e01f2015-01-13 19:45:41662 }
663}