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

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

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

Powered by Google App Engine
This is Rietveld 408576698