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

Side by Side Diff: chrome/browser/android/ntp/most_visited_sites.cc

Issue 1983063002: Move classes to //components/ntp_tiles. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 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 unified diff | Download patch
OLDNEW
(Empty)
1 // 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
5 #include "chrome/browser/android/ntp/most_visited_sites.h"
6
7 #include <utility>
8
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/metrics/field_trial.h"
12 #include "base/metrics/histogram.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/time/time.h"
19 #include "components/history/core/browser/top_sites.h"
20 #include "components/ntp_tiles/pref_names.h"
21 #include "components/ntp_tiles/switches.h"
22 #include "components/pref_registry/pref_registry_syncable.h"
23 #include "components/prefs/pref_service.h"
24 #include "components/variations/variations_associated_data.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "ui/gfx/codec/jpeg_codec.h"
28 #include "url/gurl.h"
29
30 using content::BrowserThread;
31 using history::TopSites;
32 using suggestions::ChromeSuggestion;
33 using suggestions::SuggestionsProfile;
34 using suggestions::SuggestionsService;
35
36 namespace {
37
38 // Identifiers for the various tile sources.
39 const char kHistogramClientName[] = "client";
40 const char kHistogramServerName[] = "server";
41 const char kHistogramServerFormat[] = "server%d";
42 const char kHistogramPopularName[] = "popular";
43 const char kHistogramWhitelistName[] = "whitelist";
44
45 const char kPopularSitesFieldTrialName[] = "NTPPopularSites";
46
47 // The visual type of a most visited tile.
48 //
49 // These values must stay in sync with the MostVisitedTileType enum
50 // in histograms.xml.
51 //
52 // A Java counterpart will be generated for this enum.
53 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp
54 enum MostVisitedTileType {
55 // The icon or thumbnail hasn't loaded yet.
56 NONE,
57 // The item displays a site's actual favicon or touch icon.
58 ICON_REAL,
59 // The item displays a color derived from the site's favicon or touch icon.
60 ICON_COLOR,
61 // The item displays a default gray box in place of an icon.
62 ICON_DEFAULT,
63 NUM_TILE_TYPES,
64 };
65
66 std::unique_ptr<SkBitmap> MaybeFetchLocalThumbnail(
67 const GURL& url,
68 const scoped_refptr<TopSites>& top_sites) {
69 DCHECK_CURRENTLY_ON(BrowserThread::DB);
70 scoped_refptr<base::RefCountedMemory> image;
71 std::unique_ptr<SkBitmap> bitmap;
72 if (top_sites && top_sites->GetPageThumbnail(url, false, &image))
73 bitmap = gfx::JPEGCodec::Decode(image->front(), image->size());
74 return bitmap;
75 }
76
77 // Log an event for a given |histogram| at a given element |position|. This
78 // routine exists because regular histogram macros are cached thus can't be used
79 // if the name of the histogram will change at a given call site.
80 void LogHistogramEvent(const std::string& histogram,
81 int position,
82 int num_sites) {
83 base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
84 histogram,
85 1,
86 num_sites,
87 num_sites + 1,
88 base::Histogram::kUmaTargetedHistogramFlag);
89 if (counter)
90 counter->Add(position);
91 }
92
93 bool ShouldShowPopularSites() {
94 // Note: It's important to query the field trial state first, to ensure that
95 // UMA reports the correct group.
96 const std::string group_name =
97 base::FieldTrialList::FindFullName(kPopularSitesFieldTrialName);
98 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
99 if (cmd_line->HasSwitch(ntp_tiles::switches::kDisableNTPPopularSites))
100 return false;
101 if (cmd_line->HasSwitch(ntp_tiles::switches::kEnableNTPPopularSites))
102 return true;
103 return base::StartsWith(group_name, "Enabled",
104 base::CompareCase::INSENSITIVE_ASCII);
105 }
106
107 std::string GetPopularSitesCountry() {
108 return variations::GetVariationParamValue(kPopularSitesFieldTrialName,
109 "country");
110 }
111
112 std::string GetPopularSitesVersion() {
113 return variations::GetVariationParamValue(kPopularSitesFieldTrialName,
114 "version");
115 }
116
117 // Determine whether we need any popular suggestions to fill up a grid of
118 // |num_tiles| tiles.
119 bool NeedPopularSites(const PrefService* prefs, size_t num_tiles) {
120 const base::ListValue* source_list =
121 prefs->GetList(ntp_tiles::prefs::kNTPSuggestionsIsPersonal);
122 // If there aren't enough previous suggestions to fill the grid, we need
123 // popular suggestions.
124 if (source_list->GetSize() < num_tiles)
125 return true;
126 // Otherwise, if any of the previous suggestions is not personal, then also
127 // get popular suggestions.
128 for (size_t i = 0; i < num_tiles; ++i) {
129 bool is_personal = false;
130 if (source_list->GetBoolean(i, &is_personal) && !is_personal)
131 return true;
132 }
133 // The whole grid is already filled with personal suggestions, no point in
134 // bothering with popular ones.
135 return false;
136 }
137
138 bool AreURLsEquivalent(const GURL& url1, const GURL& url2) {
139 return url1.host() == url2.host() && url1.path() == url2.path();
140 }
141
142 std::string GetSourceHistogramName(
143 const MostVisitedSites::Suggestion& suggestion) {
144 switch (suggestion.source) {
145 case MostVisitedSites::TOP_SITES:
146 return kHistogramClientName;
147 case MostVisitedSites::POPULAR:
148 return kHistogramPopularName;
149 case MostVisitedSites::WHITELIST:
150 return kHistogramWhitelistName;
151 case MostVisitedSites::SUGGESTIONS_SERVICE:
152 return suggestion.provider_index >= 0
153 ? base::StringPrintf(kHistogramServerFormat,
154 suggestion.provider_index)
155 : kHistogramServerName;
156 }
157 NOTREACHED();
158 return std::string();
159 }
160
161 } // namespace
162
163 MostVisitedSites::Suggestion::Suggestion() : provider_index(-1) {}
164
165 MostVisitedSites::Suggestion::~Suggestion() {}
166
167 MostVisitedSites::Suggestion::Suggestion(Suggestion&&) = default;
168 MostVisitedSites::Suggestion&
169 MostVisitedSites::Suggestion::operator=(Suggestion&&) = default;
170
171 MostVisitedSites::MostVisitedSites(
172 PrefService* prefs,
173 const TemplateURLService* template_url_service,
174 variations::VariationsService* variations_service,
175 net::URLRequestContextGetter* download_context,
176 const base::FilePath& popular_sites_directory,
177 scoped_refptr<history::TopSites> top_sites,
178 SuggestionsService* suggestions,
179 MostVisitedSitesSupervisor* supervisor)
180 : prefs_(prefs),
181 template_url_service_(template_url_service),
182 variations_service_(variations_service),
183 download_context_(download_context),
184 popular_sites_directory_(popular_sites_directory),
185 top_sites_(top_sites),
186 suggestions_service_(suggestions),
187 supervisor_(supervisor),
188 observer_(nullptr),
189 num_sites_(0),
190 received_most_visited_sites_(false),
191 received_popular_sites_(false),
192 recorded_uma_(false),
193 scoped_observer_(this),
194 mv_source_(SUGGESTIONS_SERVICE),
195 weak_ptr_factory_(this) {
196 supervisor_->SetObserver(this);
197 }
198
199 MostVisitedSites::~MostVisitedSites() {
200 supervisor_->SetObserver(nullptr);
201 }
202
203 void MostVisitedSites::SetMostVisitedURLsObserver(
204 MostVisitedSites::Observer* observer, int num_sites) {
205 DCHECK(observer);
206 observer_ = observer;
207 num_sites_ = num_sites;
208
209 if (ShouldShowPopularSites() &&
210 NeedPopularSites(prefs_, num_sites_)) {
211 popular_sites_.reset(new PopularSites(
212 prefs_, template_url_service_, variations_service_, download_context_,
213 popular_sites_directory_, GetPopularSitesCountry(),
214 GetPopularSitesVersion(), false,
215 base::Bind(&MostVisitedSites::OnPopularSitesAvailable,
216 base::Unretained(this))));
217 } else {
218 received_popular_sites_ = true;
219 }
220
221 if (top_sites_) {
222 // TopSites updates itself after a delay. To ensure up-to-date results,
223 // force an update now.
224 top_sites_->SyncWithHistory();
225
226 // Register as TopSitesObserver so that we can update ourselves when the
227 // TopSites changes.
228 scoped_observer_.Add(top_sites_.get());
229 }
230
231 suggestions_subscription_ = suggestions_service_->AddCallback(
232 base::Bind(&MostVisitedSites::OnSuggestionsProfileAvailable,
233 base::Unretained(this)));
234
235 // Immediately build the current suggestions, getting personal suggestions
236 // from the SuggestionsService's cache or, if that is empty, from TopSites.
237 BuildCurrentSuggestions();
238 // Also start a request for fresh suggestions.
239 suggestions_service_->FetchSuggestionsData();
240 }
241
242 void MostVisitedSites::GetURLThumbnail(
243 const GURL& url,
244 const ThumbnailCallback& callback) {
245 DCHECK_CURRENTLY_ON(BrowserThread::UI);
246
247 BrowserThread::PostTaskAndReplyWithResult(
248 BrowserThread::DB, FROM_HERE,
249 base::Bind(&MaybeFetchLocalThumbnail, url, top_sites_),
250 base::Bind(&MostVisitedSites::OnLocalThumbnailFetched,
251 weak_ptr_factory_.GetWeakPtr(), url, callback));
252 }
253
254 void MostVisitedSites::OnLocalThumbnailFetched(
255 const GURL& url,
256 const ThumbnailCallback& callback,
257 std::unique_ptr<SkBitmap> bitmap) {
258 DCHECK_CURRENTLY_ON(BrowserThread::UI);
259 if (!bitmap.get()) {
260 // A thumbnail is not locally available for |url|. Make sure it is put in
261 // the list to be fetched at the next visit to this site.
262 if (top_sites_)
263 top_sites_->AddForcedURL(url, base::Time::Now());
264 // Also fetch a remote thumbnail if possible. PopularSites or the
265 // SuggestionsService can supply a thumbnail download URL.
266 if (popular_sites_) {
267 const std::vector<PopularSites::Site>& sites = popular_sites_->sites();
268 auto it = std::find_if(
269 sites.begin(), sites.end(),
270 [&url](const PopularSites::Site& site) { return site.url == url; });
271 if (it != sites.end() && it->thumbnail_url.is_valid()) {
272 return suggestions_service_->GetPageThumbnailWithURL(
273 url, it->thumbnail_url,
274 base::Bind(&MostVisitedSites::OnObtainedThumbnail,
275 weak_ptr_factory_.GetWeakPtr(), false, callback));
276 }
277 }
278 if (mv_source_ == SUGGESTIONS_SERVICE) {
279 return suggestions_service_->GetPageThumbnail(
280 url, base::Bind(&MostVisitedSites::OnObtainedThumbnail,
281 weak_ptr_factory_.GetWeakPtr(), false, callback));
282 }
283 }
284 OnObtainedThumbnail(true, callback, url, bitmap.get());
285 }
286
287 void MostVisitedSites::OnObtainedThumbnail(
288 bool is_local_thumbnail,
289 const ThumbnailCallback& callback,
290 const GURL& url,
291 const SkBitmap* bitmap) {
292 DCHECK_CURRENTLY_ON(BrowserThread::UI);
293 callback.Run(is_local_thumbnail, bitmap);
294 }
295
296 void MostVisitedSites::AddOrRemoveBlacklistedUrl(
297 const GURL& url, bool add_url) {
298 // Always blacklist in the local TopSites.
299 if (top_sites_) {
300 if (add_url)
301 top_sites_->AddBlacklistedURL(url);
302 else
303 top_sites_->RemoveBlacklistedURL(url);
304 }
305
306 // Only blacklist in the server-side suggestions service if it's active.
307 if (mv_source_ == SUGGESTIONS_SERVICE) {
308 if (add_url)
309 suggestions_service_->BlacklistURL(url);
310 else
311 suggestions_service_->UndoBlacklistURL(url);
312 }
313 }
314
315 void MostVisitedSites::RecordTileTypeMetrics(
316 const std::vector<int>& tile_types) {
317 DCHECK_EQ(current_suggestions_.size(), tile_types.size());
318 int counts_per_type[NUM_TILE_TYPES] = {0};
319 for (size_t i = 0; i < tile_types.size(); ++i) {
320 int tile_type = tile_types[i];
321 ++counts_per_type[tile_type];
322 std::string histogram = base::StringPrintf(
323 "NewTabPage.TileType.%s",
324 GetSourceHistogramName(current_suggestions_[i]).c_str());
325 LogHistogramEvent(histogram, tile_type, NUM_TILE_TYPES);
326 }
327
328 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsReal",
329 counts_per_type[ICON_REAL]);
330 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsColor",
331 counts_per_type[ICON_COLOR]);
332 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsGray",
333 counts_per_type[ICON_DEFAULT]);
334 }
335
336 void MostVisitedSites::RecordOpenedMostVisitedItem(int index, int tile_type) {
337 DCHECK_GE(index, 0);
338 DCHECK_LT(index, static_cast<int>(current_suggestions_.size()));
339 std::string histogram = base::StringPrintf(
340 "NewTabPage.MostVisited.%s",
341 GetSourceHistogramName(current_suggestions_[index]).c_str());
342 LogHistogramEvent(histogram, index, num_sites_);
343
344 histogram = base::StringPrintf(
345 "NewTabPage.TileTypeClicked.%s",
346 GetSourceHistogramName(current_suggestions_[index]).c_str());
347 LogHistogramEvent(histogram, tile_type, NUM_TILE_TYPES);
348 }
349
350 void MostVisitedSites::OnBlockedSitesChanged() {
351 BuildCurrentSuggestions();
352 }
353
354 // static
355 void MostVisitedSites::RegisterProfilePrefs(
356 user_prefs::PrefRegistrySyncable* registry) {
357 registry->RegisterListPref(ntp_tiles::prefs::kNTPSuggestionsURL);
358 registry->RegisterListPref(ntp_tiles::prefs::kNTPSuggestionsIsPersonal);
359 }
360
361 void MostVisitedSites::BuildCurrentSuggestions() {
362 // Get the current suggestions from cache. If the cache is empty, this will
363 // fall back to TopSites.
364 OnSuggestionsProfileAvailable(
365 suggestions_service_->GetSuggestionsDataFromCache());
366 }
367
368 void MostVisitedSites::InitiateTopSitesQuery() {
369 if (!top_sites_)
370 return;
371
372 top_sites_->GetMostVisitedURLs(
373 base::Bind(&MostVisitedSites::OnMostVisitedURLsAvailable,
374 weak_ptr_factory_.GetWeakPtr()),
375 false);
376 }
377
378 base::FilePath MostVisitedSites::GetWhitelistLargeIconPath(const GURL& url) {
379 for (const auto& whitelist : supervisor_->whitelists()) {
380 if (AreURLsEquivalent(whitelist.entry_point, url))
381 return whitelist.large_icon_path;
382 }
383 return base::FilePath();
384 }
385
386 void MostVisitedSites::OnMostVisitedURLsAvailable(
387 const history::MostVisitedURLList& visited_list) {
388 MostVisitedSites::SuggestionsPtrVector suggestions;
389 size_t num_tiles =
390 std::min(visited_list.size(), static_cast<size_t>(num_sites_));
391 for (size_t i = 0; i < num_tiles; ++i) {
392 const history::MostVisitedURL& visited = visited_list[i];
393 if (visited.url.is_empty()) {
394 num_tiles = i;
395 break; // This is the signal that there are no more real visited sites.
396 }
397 if (supervisor_->IsBlocked(visited.url))
398 continue;
399
400 std::unique_ptr<Suggestion> suggestion(new Suggestion());
401 suggestion->title = visited.title;
402 suggestion->url = visited.url;
403 suggestion->source = TOP_SITES;
404 suggestion->whitelist_icon_path = GetWhitelistLargeIconPath(visited.url);
405
406 suggestions.push_back(std::move(suggestion));
407 }
408
409 received_most_visited_sites_ = true;
410 mv_source_ = TOP_SITES;
411 SaveNewNTPSuggestions(&suggestions);
412 NotifyMostVisitedURLsObserver();
413 }
414
415 void MostVisitedSites::OnSuggestionsProfileAvailable(
416 const SuggestionsProfile& suggestions_profile) {
417 int num_tiles = suggestions_profile.suggestions_size();
418 // With no server suggestions, fall back to local TopSites.
419 if (num_tiles == 0) {
420 InitiateTopSitesQuery();
421 return;
422 }
423 if (num_sites_ < num_tiles)
424 num_tiles = num_sites_;
425
426 MostVisitedSites::SuggestionsPtrVector suggestions;
427 for (int i = 0; i < num_tiles; ++i) {
428 const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
429 if (supervisor_->IsBlocked(GURL(suggestion.url())))
430 continue;
431
432 std::unique_ptr<Suggestion> generated_suggestion(new Suggestion());
433 generated_suggestion->title = base::UTF8ToUTF16(suggestion.title());
434 generated_suggestion->url = GURL(suggestion.url());
435 generated_suggestion->source = SUGGESTIONS_SERVICE;
436 generated_suggestion->whitelist_icon_path = GetWhitelistLargeIconPath(
437 GURL(suggestion.url()));
438 if (suggestion.providers_size() > 0)
439 generated_suggestion->provider_index = suggestion.providers(0);
440
441 suggestions.push_back(std::move(generated_suggestion));
442 }
443
444 received_most_visited_sites_ = true;
445 mv_source_ = SUGGESTIONS_SERVICE;
446 SaveNewNTPSuggestions(&suggestions);
447 NotifyMostVisitedURLsObserver();
448 }
449
450 MostVisitedSites::SuggestionsPtrVector
451 MostVisitedSites::CreateWhitelistEntryPointSuggestions(
452 const MostVisitedSites::SuggestionsPtrVector& personal_suggestions) {
453 size_t num_personal_suggestions = personal_suggestions.size();
454 DCHECK_LE(num_personal_suggestions, static_cast<size_t>(num_sites_));
455
456 size_t num_whitelist_suggestions = num_sites_ - num_personal_suggestions;
457 MostVisitedSites::SuggestionsPtrVector whitelist_suggestions;
458
459 std::set<std::string> personal_hosts;
460 for (const auto& suggestion : personal_suggestions)
461 personal_hosts.insert(suggestion->url.host());
462
463 for (const auto& whitelist : supervisor_->whitelists()) {
464 // Skip blacklisted sites.
465 if (top_sites_ && top_sites_->IsBlacklisted(whitelist.entry_point))
466 continue;
467
468 // Skip suggestions already present.
469 if (personal_hosts.find(whitelist.entry_point.host()) !=
470 personal_hosts.end())
471 continue;
472
473 // Skip whitelist entry points that are manually blocked.
474 if (supervisor_->IsBlocked(whitelist.entry_point))
475 continue;
476
477 std::unique_ptr<Suggestion> suggestion(new Suggestion());
478 suggestion->title = whitelist.title;
479 suggestion->url = whitelist.entry_point;
480 suggestion->source = WHITELIST;
481 suggestion->whitelist_icon_path = whitelist.large_icon_path;
482
483 whitelist_suggestions.push_back(std::move(suggestion));
484 if (whitelist_suggestions.size() >= num_whitelist_suggestions)
485 break;
486 }
487
488 return whitelist_suggestions;
489 }
490
491 MostVisitedSites::SuggestionsPtrVector
492 MostVisitedSites::CreatePopularSitesSuggestions(
493 const MostVisitedSites::SuggestionsPtrVector& personal_suggestions,
494 const MostVisitedSites::SuggestionsPtrVector& whitelist_suggestions) {
495 // For child accounts popular sites suggestions will not be added.
496 if (supervisor_->IsChildProfile())
497 return MostVisitedSites::SuggestionsPtrVector();
498
499 size_t num_suggestions =
500 personal_suggestions.size() + whitelist_suggestions.size();
501 DCHECK_LE(num_suggestions, static_cast<size_t>(num_sites_));
502
503 // Collect non-blacklisted popular suggestions, skipping those already present
504 // in the personal suggestions.
505 size_t num_popular_sites_suggestions = num_sites_ - num_suggestions;
506 MostVisitedSites::SuggestionsPtrVector popular_sites_suggestions;
507
508 if (num_popular_sites_suggestions > 0 && popular_sites_) {
509 std::set<std::string> hosts;
510 for (const auto& suggestion : personal_suggestions)
511 hosts.insert(suggestion->url.host());
512 for (const auto& suggestion : whitelist_suggestions)
513 hosts.insert(suggestion->url.host());
514 for (const PopularSites::Site& popular_site : popular_sites_->sites()) {
515 // Skip blacklisted sites.
516 if (top_sites_ && top_sites_->IsBlacklisted(popular_site.url))
517 continue;
518 std::string host = popular_site.url.host();
519 // Skip suggestions already present in personal or whitelists.
520 if (hosts.find(host) != hosts.end())
521 continue;
522
523 std::unique_ptr<Suggestion> suggestion(new Suggestion());
524 suggestion->title = popular_site.title;
525 suggestion->url = GURL(popular_site.url);
526 suggestion->source = POPULAR;
527
528 popular_sites_suggestions.push_back(std::move(suggestion));
529 if (popular_sites_suggestions.size() >= num_popular_sites_suggestions)
530 break;
531 }
532 }
533 return popular_sites_suggestions;
534 }
535
536 void MostVisitedSites::SaveNewNTPSuggestions(
537 MostVisitedSites::SuggestionsPtrVector* personal_suggestions) {
538 MostVisitedSites::SuggestionsPtrVector whitelist_suggestions =
539 CreateWhitelistEntryPointSuggestions(*personal_suggestions);
540 MostVisitedSites::SuggestionsPtrVector popular_sites_suggestions =
541 CreatePopularSitesSuggestions(*personal_suggestions,
542 whitelist_suggestions);
543 size_t num_actual_tiles = personal_suggestions->size() +
544 whitelist_suggestions.size() +
545 popular_sites_suggestions.size();
546 std::vector<std::string> old_sites_url;
547 std::vector<bool> old_sites_is_personal;
548 // TODO(treib): We used to call |GetPreviousNTPSites| here to populate
549 // |old_sites_url| and |old_sites_is_personal|, but that caused problems
550 // (crbug.com/585391). Either figure out a way to fix them and re-enable,
551 // or properly remove the order-persisting code. crbug.com/601734
552 MostVisitedSites::SuggestionsPtrVector merged_suggestions = MergeSuggestions(
553 personal_suggestions, &whitelist_suggestions, &popular_sites_suggestions,
554 old_sites_url, old_sites_is_personal);
555 DCHECK_EQ(num_actual_tiles, merged_suggestions.size());
556 current_suggestions_.resize(merged_suggestions.size());
557 for (size_t i = 0; i < merged_suggestions.size(); ++i)
558 std::swap(*merged_suggestions[i], current_suggestions_[i]);
559 if (received_popular_sites_)
560 SaveCurrentNTPSites();
561 }
562
563 // static
564 MostVisitedSites::SuggestionsPtrVector MostVisitedSites::MergeSuggestions(
565 MostVisitedSites::SuggestionsPtrVector* personal_suggestions,
566 MostVisitedSites::SuggestionsPtrVector* whitelist_suggestions,
567 MostVisitedSites::SuggestionsPtrVector* popular_suggestions,
568 const std::vector<std::string>& old_sites_url,
569 const std::vector<bool>& old_sites_is_personal) {
570 size_t num_personal_suggestions = personal_suggestions->size();
571 size_t num_whitelist_suggestions = whitelist_suggestions->size();
572 size_t num_popular_suggestions = popular_suggestions->size();
573 size_t num_tiles = num_popular_suggestions + num_whitelist_suggestions +
574 num_personal_suggestions;
575 MostVisitedSites::SuggestionsPtrVector merged_suggestions;
576 merged_suggestions.resize(num_tiles);
577
578 size_t num_old_tiles = old_sites_url.size();
579 DCHECK_LE(num_old_tiles, num_tiles);
580 DCHECK_EQ(num_old_tiles, old_sites_is_personal.size());
581 std::vector<std::string> old_sites_host;
582 old_sites_host.reserve(num_old_tiles);
583 // Only populate the hosts for popular suggestions as only they can be
584 // replaced by host. Personal suggestions require an exact url match to be
585 // replaced.
586 for (size_t i = 0; i < num_old_tiles; ++i) {
587 old_sites_host.push_back(old_sites_is_personal[i]
588 ? std::string()
589 : GURL(old_sites_url[i]).host());
590 }
591
592 // Insert personal suggestions if they existed previously.
593 std::vector<size_t> new_personal_suggestions = InsertMatchingSuggestions(
594 personal_suggestions, &merged_suggestions, old_sites_url, old_sites_host);
595 // Insert whitelist suggestions if they existed previously.
596 std::vector<size_t> new_whitelist_suggestions =
597 InsertMatchingSuggestions(whitelist_suggestions, &merged_suggestions,
598 old_sites_url, old_sites_host);
599 // Insert popular suggestions if they existed previously.
600 std::vector<size_t> new_popular_suggestions = InsertMatchingSuggestions(
601 popular_suggestions, &merged_suggestions, old_sites_url, old_sites_host);
602 // Insert leftover personal suggestions.
603 size_t filled_so_far = InsertAllSuggestions(
604 0, new_personal_suggestions, personal_suggestions, &merged_suggestions);
605 // Insert leftover whitelist suggestions.
606 filled_so_far =
607 InsertAllSuggestions(filled_so_far, new_whitelist_suggestions,
608 whitelist_suggestions, &merged_suggestions);
609 // Insert leftover popular suggestions.
610 InsertAllSuggestions(filled_so_far, new_popular_suggestions,
611 popular_suggestions, &merged_suggestions);
612 return merged_suggestions;
613 }
614
615 void MostVisitedSites::GetPreviousNTPSites(
616 size_t num_tiles,
617 std::vector<std::string>* old_sites_url,
618 std::vector<bool>* old_sites_is_personal) const {
619 const base::ListValue* url_list = prefs_->GetList(
620 ntp_tiles::prefs::kNTPSuggestionsURL);
621 const base::ListValue* source_list =
622 prefs_->GetList(ntp_tiles::prefs::kNTPSuggestionsIsPersonal);
623 DCHECK_EQ(url_list->GetSize(), source_list->GetSize());
624 if (url_list->GetSize() < num_tiles)
625 num_tiles = url_list->GetSize();
626 if (num_tiles == 0) {
627 // No fallback required as Personal suggestions take precedence anyway.
628 return;
629 }
630 old_sites_url->reserve(num_tiles);
631 old_sites_is_personal->reserve(num_tiles);
632 for (size_t i = 0; i < num_tiles; ++i) {
633 std::string url_string;
634 bool success = url_list->GetString(i, &url_string);
635 DCHECK(success);
636 old_sites_url->push_back(url_string);
637 bool is_personal;
638 success = source_list->GetBoolean(i, &is_personal);
639 DCHECK(success);
640 old_sites_is_personal->push_back(is_personal);
641 }
642 }
643
644 void MostVisitedSites::SaveCurrentNTPSites() {
645 base::ListValue url_list;
646 base::ListValue source_list;
647 for (const auto& suggestion : current_suggestions_) {
648 url_list.AppendString(suggestion.url.spec());
649 source_list.AppendBoolean(suggestion.source != MostVisitedSites::POPULAR);
650 }
651 prefs_->Set(ntp_tiles::prefs::kNTPSuggestionsIsPersonal, source_list);
652 prefs_->Set(ntp_tiles::prefs::kNTPSuggestionsURL, url_list);
653 }
654
655 // static
656 std::vector<size_t> MostVisitedSites::InsertMatchingSuggestions(
657 MostVisitedSites::SuggestionsPtrVector* src_suggestions,
658 MostVisitedSites::SuggestionsPtrVector* dst_suggestions,
659 const std::vector<std::string>& match_urls,
660 const std::vector<std::string>& match_hosts) {
661 std::vector<size_t> unmatched_suggestions;
662 size_t num_src_suggestions = src_suggestions->size();
663 size_t num_matchers = match_urls.size();
664 for (size_t i = 0; i < num_src_suggestions; ++i) {
665 size_t position;
666 for (position = 0; position < num_matchers; ++position) {
667 if ((*dst_suggestions)[position] != nullptr)
668 continue;
669 if (match_urls[position] == (*src_suggestions)[i]->url.spec())
670 break;
671 // match_hosts is only populated for suggestions which can be replaced by
672 // host matching like popular suggestions.
673 if (match_hosts[position] == (*src_suggestions)[i]->url.host())
674 break;
675 }
676 if (position == num_matchers) {
677 unmatched_suggestions.push_back(i);
678 } else {
679 // A move is required as the source and destination containers own the
680 // elements.
681 std::swap((*dst_suggestions)[position], (*src_suggestions)[i]);
682 }
683 }
684 return unmatched_suggestions;
685 }
686
687 // static
688 size_t MostVisitedSites::InsertAllSuggestions(
689 size_t start_position,
690 const std::vector<size_t>& insert_positions,
691 std::vector<std::unique_ptr<Suggestion>>* src_suggestions,
692 std::vector<std::unique_ptr<Suggestion>>* dst_suggestions) {
693 size_t num_inserts = insert_positions.size();
694 size_t num_dests = dst_suggestions->size();
695
696 size_t src_pos = 0;
697 size_t i = start_position;
698 for (; i < num_dests && src_pos < num_inserts; ++i) {
699 if ((*dst_suggestions)[i] != nullptr)
700 continue;
701 size_t src = insert_positions[src_pos++];
702 std::swap((*dst_suggestions)[i], (*src_suggestions)[src]);
703 }
704 // Return destination positions filled so far which becomes the start_position
705 // for future runs.
706 return i;
707 }
708
709 void MostVisitedSites::NotifyMostVisitedURLsObserver() {
710 size_t num_suggestions = current_suggestions_.size();
711 if (received_most_visited_sites_ && received_popular_sites_ &&
712 !recorded_uma_) {
713 RecordImpressionUMAMetrics();
714 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.NumberOfTiles", num_suggestions);
715 recorded_uma_ = true;
716 }
717
718 if (!observer_)
719 return;
720
721 observer_->OnMostVisitedURLsAvailable(current_suggestions_);
722 }
723
724 void MostVisitedSites::OnPopularSitesAvailable(bool success) {
725 received_popular_sites_ = true;
726
727 if (!success) {
728 LOG(WARNING) << "Download of popular sites failed";
729 return;
730 }
731
732 // Pass the popular sites to the observer. This will cause it to fetch any
733 // missing icons, but will *not* cause it to display the popular sites.
734 observer_->OnPopularURLsAvailable(popular_sites_->sites());
735
736 // Re-build the suggestions list. Once done, this will notify the observer.
737 BuildCurrentSuggestions();
738 }
739
740 void MostVisitedSites::RecordImpressionUMAMetrics() {
741 for (size_t i = 0; i < current_suggestions_.size(); i++) {
742 std::string histogram = base::StringPrintf(
743 "NewTabPage.SuggestionsImpression.%s",
744 GetSourceHistogramName(current_suggestions_[i]).c_str());
745 LogHistogramEvent(histogram, static_cast<int>(i), num_sites_);
746 }
747 }
748
749 void MostVisitedSites::TopSitesLoaded(TopSites* top_sites) {}
750
751 void MostVisitedSites::TopSitesChanged(TopSites* top_sites,
752 ChangeReason change_reason) {
753 if (mv_source_ == TOP_SITES) {
754 // The displayed suggestions are invalidated.
755 InitiateTopSitesQuery();
756 }
757 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698