blob: b9ecdde7a2ebd8d4811cba16af7857751b8e3a04 [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
5#include "chrome/browser/android/most_visited_sites.h"
6
7#include "base/android/jni_android.h"
8#include "base/android/jni_array.h"
9#include "base/android/jni_string.h"
10#include "base/android/scoped_java_ref.h"
[email protected]d33adb40d2014-06-03 21:38:4611#include "base/callback.h"
treibcffa6502015-08-06 09:12:2712#include "base/command_line.h"
[email protected]9ed2f5c2014-06-13 14:55:2413#include "base/metrics/histogram.h"
[email protected]1d778e12014-06-17 02:28:5114#include "base/metrics/sparse_histogram.h"
[email protected]9ed2f5c2014-06-13 14:55:2415#include "base/strings/string_number_conversions.h"
treibeba6cee2015-09-09 13:25:5916#include "base/strings/string_util.h"
[email protected]9ed2f5c2014-06-13 14:55:2417#include "base/strings/stringprintf.h"
[email protected]bc093e82014-03-04 21:49:1118#include "base/strings/utf_string_conversions.h"
[email protected]6d21c702014-05-15 06:10:2119#include "base/time/time.h"
treibcffa6502015-08-06 09:12:2720#include "chrome/browser/android/popular_sites.h"
jitendra.ks30f03392015-01-28 09:47:1821#include "chrome/browser/history/top_sites_factory.h"
[email protected]c764b2822013-07-03 04:16:4822#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/profiles/profile_android.h"
[email protected]bc093e82014-03-04 21:49:1124#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
[email protected]81e09e72014-05-07 13:41:2225#include "chrome/browser/search/suggestions/suggestions_source.h"
[email protected]1d1ab152014-08-21 17:14:1226#include "chrome/browser/sync/profile_sync_service.h"
27#include "chrome/browser/sync/profile_sync_service_factory.h"
[email protected]81e09e72014-05-07 13:41:2228#include "chrome/browser/thumbnails/thumbnail_list_source.h"
treibcffa6502015-08-06 09:12:2729#include "chrome/common/chrome_switches.h"
sdefresne0da3bc02015-01-29 18:26:3530#include "components/history/core/browser/top_sites.h"
[email protected]bdceb3ba2014-07-25 16:47:4831#include "components/suggestions/suggestions_service.h"
[email protected]1d1ab152014-08-21 17:14:1232#include "components/suggestions/suggestions_utils.h"
[email protected]c764b2822013-07-03 04:16:4833#include "content/public/browser/browser_thread.h"
[email protected]81e09e72014-05-07 13:41:2234#include "content/public/browser/url_data_source.h"
[email protected]c764b2822013-07-03 04:16:4835#include "jni/MostVisitedSites_jni.h"
36#include "third_party/skia/include/core/SkBitmap.h"
37#include "ui/gfx/android/java_bitmap.h"
38#include "ui/gfx/codec/jpeg_codec.h"
treibcffa6502015-08-06 09:12:2739#include "url/gurl.h"
[email protected]c764b2822013-07-03 04:16:4840
41using base::android::AttachCurrentThread;
[email protected]c764b2822013-07-03 04:16:4842using base::android::ConvertJavaStringToUTF8;
43using base::android::ScopedJavaGlobalRef;
knn6cf777fc2015-05-06 13:21:4844using base::android::ScopedJavaLocalRef;
[email protected]c764b2822013-07-03 04:16:4845using base::android::ToJavaArrayOfStrings;
[email protected]c764b2822013-07-03 04:16:4846using content::BrowserThread;
47using history::TopSites;
[email protected]bc093e82014-03-04 21:49:1148using suggestions::ChromeSuggestion;
49using suggestions::SuggestionsProfile;
50using suggestions::SuggestionsService;
51using suggestions::SuggestionsServiceFactory;
[email protected]1d1ab152014-08-21 17:14:1252using suggestions::SyncState;
[email protected]c764b2822013-07-03 04:16:4853
[email protected]c764b2822013-07-03 04:16:4854namespace {
55
[email protected]92a598822014-06-17 10:25:5656// Total number of tiles displayed.
[email protected]9ed2f5c2014-06-13 14:55:2457const char kNumTilesHistogramName[] = "NewTabPage.NumberOfTiles";
[email protected]92a598822014-06-17 10:25:5658// Tracking thumbnails.
[email protected]1d778e12014-06-17 02:28:5159const char kNumLocalThumbnailTilesHistogramName[] =
60 "NewTabPage.NumberOfThumbnailTiles";
61const char kNumEmptyTilesHistogramName[] = "NewTabPage.NumberOfGrayTiles";
62const char kNumServerTilesHistogramName[] = "NewTabPage.NumberOfExternalTiles";
treib783a3372015-08-25 16:24:3963
64// Format for tile clicks histogram.
65const char kOpenedItemHistogramFormat[] = "NewTabPage.MostVisited.%s";
66// Format for tile impressions histogram.
67const char kImpressionHistogramFormat[] = "NewTabPage.SuggestionsImpression.%s";
68// Identifiers for the various tile sources.
69const char kHistogramClientName[] = "client";
70const char kHistogramServerName[] = "server";
71const char kHistogramServerFormat[] = "server%d";
72const char kHistogramPopularName[] = "popular";
[email protected]9ed2f5c2014-06-13 14:55:2473
treib508939a2015-08-25 10:07:5674const char kPopularSitesFieldTrialName[] = "NTPPopularSites";
75
knn6cf777fc2015-05-06 13:21:4876scoped_ptr<SkBitmap> MaybeFetchLocalThumbnail(
77 const GURL& url,
78 const scoped_refptr<TopSites>& top_sites) {
79 DCHECK_CURRENTLY_ON(BrowserThread::DB);
80 scoped_refptr<base::RefCountedMemory> image;
81 scoped_ptr<SkBitmap> bitmap;
82 if (top_sites && top_sites->GetPageThumbnail(url, false, &image))
83 bitmap.reset(gfx::JPEGCodec::Decode(image->front(), image->size()));
84 return bitmap.Pass();
[email protected]d33adb40d2014-06-03 21:38:4685}
86
[email protected]92a598822014-06-17 10:25:5687// Log an event for a given |histogram| at a given element |position|. This
88// routine exists because regular histogram macros are cached thus can't be used
89// if the name of the histogram will change at a given call site.
treib783a3372015-08-25 16:24:3990void LogHistogramEvent(const std::string& histogram,
91 int position,
[email protected]92a598822014-06-17 10:25:5692 int num_sites) {
[email protected]9ed2f5c2014-06-13 14:55:2493 base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
[email protected]92a598822014-06-17 10:25:5694 histogram,
[email protected]9ed2f5c2014-06-13 14:55:2495 1,
96 num_sites,
97 num_sites + 1,
98 base::Histogram::kUmaTargetedHistogramFlag);
[email protected]6f674482014-08-11 22:50:4999 if (counter)
100 counter->Add(position);
[email protected]9ed2f5c2014-06-13 14:55:24101}
102
[email protected]1d1ab152014-08-21 17:14:12103// Return the current SyncState for use with the SuggestionsService.
104SyncState GetSyncState(Profile* profile) {
105 ProfileSyncService* sync =
106 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
107 if (!sync)
108 return SyncState::SYNC_OR_HISTORY_SYNC_DISABLED;
109 return suggestions::GetSyncState(
maxbogue96b7d272015-06-16 02:59:58110 sync->CanSyncStart(),
maxbogue192b3602015-06-04 00:33:55111 sync->IsSyncActive() && sync->ConfigurationDone(),
[email protected]1d1ab152014-08-21 17:14:12112 sync->GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES));
113}
114
treibcffa6502015-08-06 09:12:27115bool ShouldShowPopularSites() {
116 // Note: It's important to query the field trial state first, to ensure that
117 // UMA reports the correct group.
118 const std::string group_name =
treib508939a2015-08-25 10:07:56119 base::FieldTrialList::FindFullName(kPopularSitesFieldTrialName);
treibcffa6502015-08-06 09:12:27120 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
121 if (cmd_line->HasSwitch(switches::kDisableNTPPopularSites))
122 return false;
123 if (cmd_line->HasSwitch(switches::kEnableNTPPopularSites))
124 return true;
treibeba6cee2015-09-09 13:25:59125 return base::StartsWith(group_name, "Enabled",
126 base::CompareCase::INSENSITIVE_ASCII);
treibcffa6502015-08-06 09:12:27127}
128
treib508939a2015-08-25 10:07:56129std::string GetPopularSitesFilename() {
130 return variations::GetVariationParamValue(kPopularSitesFieldTrialName,
131 "filename");
132}
133
[email protected]c764b2822013-07-03 04:16:48134} // namespace
135
[email protected]7ceb4e32013-12-06 04:13:04136MostVisitedSites::MostVisitedSites(Profile* profile)
treib783a3372015-08-25 16:24:39137 : profile_(profile), num_sites_(0), received_most_visited_sites_(false),
138 received_popular_sites_(false), recorded_uma_(false),
mathpd91e8eb2015-03-24 17:37:47139 num_local_thumbs_(0), num_server_thumbs_(0), num_empty_thumbs_(0),
140 scoped_observer_(this), weak_ptr_factory_(this) {
[email protected]81e09e72014-05-07 13:41:22141 // Register the debugging page for the Suggestions Service and the thumbnails
142 // debugging page.
143 content::URLDataSource::Add(profile_,
144 new suggestions::SuggestionsSource(profile_));
145 content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
[email protected]1d1ab152014-08-21 17:14:12146
147 // Register this class as an observer to the sync service. It is important to
148 // be notified of changes in the sync state such as initialization, sync
149 // being enabled or disabled, etc.
150 ProfileSyncService* profile_sync_service =
151 ProfileSyncServiceFactory::GetForProfile(profile_);
152 if (profile_sync_service)
153 profile_sync_service->AddObserver(this);
treibcffa6502015-08-06 09:12:27154
155 if (ShouldShowPopularSites()) {
156 popular_sites_.reset(new PopularSites(
maybellea18d7d02015-09-08 14:28:25157 profile,
treib508939a2015-08-25 10:07:56158 GetPopularSitesFilename(),
treibcffa6502015-08-06 09:12:27159 profile_->GetRequestContext(),
160 base::Bind(&MostVisitedSites::OnPopularSitesAvailable,
161 base::Unretained(this))));
treib783a3372015-08-25 16:24:39162 } else {
163 received_popular_sites_ = true;
treibcffa6502015-08-06 09:12:27164 }
[email protected]7ceb4e32013-12-06 04:13:04165}
[email protected]c764b2822013-07-03 04:16:48166
[email protected]7ceb4e32013-12-06 04:13:04167MostVisitedSites::~MostVisitedSites() {
feng94a42102014-08-26 23:42:39168 ProfileSyncService* profile_sync_service =
169 ProfileSyncServiceFactory::GetForProfile(profile_);
170 if (profile_sync_service && profile_sync_service->HasObserver(this))
171 profile_sync_service->RemoveObserver(this);
[email protected]7ceb4e32013-12-06 04:13:04172}
[email protected]c764b2822013-07-03 04:16:48173
[email protected]7ceb4e32013-12-06 04:13:04174void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) {
175 delete this;
176}
[email protected]c764b2822013-07-03 04:16:48177
[email protected]1d778e12014-06-17 02:28:51178void MostVisitedSites::OnLoadingComplete(JNIEnv* env, jobject obj) {
treib783a3372015-08-25 16:24:39179 RecordThumbnailUMAMetrics();
[email protected]1d778e12014-06-17 02:28:51180}
181
[email protected]7ceb4e32013-12-06 04:13:04182void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv* env,
183 jobject obj,
184 jobject j_observer,
185 jint num_sites) {
186 observer_.Reset(env, j_observer);
187 num_sites_ = num_sites;
[email protected]852fc792013-10-04 04:15:12188
[email protected]7ceb4e32013-12-06 04:13:04189 QueryMostVisitedURLs();
190
jitendra.ks30f03392015-01-28 09:47:18191 scoped_refptr<history::TopSites> top_sites =
192 TopSitesFactory::GetForProfile(profile_);
[email protected]7ceb4e32013-12-06 04:13:04193 if (top_sites) {
194 // TopSites updates itself after a delay. To ensure up-to-date results,
195 // force an update now.
196 top_sites->SyncWithHistory();
197
sdefresneedf9e01f2015-01-13 19:45:41198 // Register as TopSitesObserver so that we can update ourselves when the
199 // TopSites changes.
jitendra.ks30f03392015-01-28 09:47:18200 scoped_observer_.Add(top_sites.get());
[email protected]7ceb4e32013-12-06 04:13:04201 }
[email protected]c764b2822013-07-03 04:16:48202}
203
[email protected]7ceb4e32013-12-06 04:13:04204void MostVisitedSites::GetURLThumbnail(JNIEnv* env,
205 jobject obj,
knn6cf777fc2015-05-06 13:21:48206 jstring j_url,
[email protected]7ceb4e32013-12-06 04:13:04207 jobject j_callback_obj) {
mostynb351bf982015-03-25 22:26:58208 DCHECK_CURRENTLY_ON(BrowserThread::UI);
knn6cf777fc2015-05-06 13:21:48209 scoped_ptr<ScopedJavaGlobalRef<jobject>> j_callback(
210 new ScopedJavaGlobalRef<jobject>());
[email protected]7ceb4e32013-12-06 04:13:04211 j_callback->Reset(env, j_callback_obj);
[email protected]c764b2822013-07-03 04:16:48212
knn6cf777fc2015-05-06 13:21:48213 GURL url(ConvertJavaStringToUTF8(env, j_url));
jitendra.ks30f03392015-01-28 09:47:18214 scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_));
[email protected]d33adb40d2014-06-03 21:38:46215
knn6cf777fc2015-05-06 13:21:48216 BrowserThread::PostTaskAndReplyWithResult(
[email protected]d33adb40d2014-06-03 21:38:46217 BrowserThread::DB, FROM_HERE,
knn6cf777fc2015-05-06 13:21:48218 base::Bind(&MaybeFetchLocalThumbnail, url, top_sites),
219 base::Bind(&MostVisitedSites::OnLocalThumbnailFetched,
220 weak_ptr_factory_.GetWeakPtr(), url,
221 base::Passed(&j_callback)));
222}
223
224void MostVisitedSites::OnLocalThumbnailFetched(
225 const GURL& url,
226 scoped_ptr<ScopedJavaGlobalRef<jobject>> j_callback,
227 scoped_ptr<SkBitmap> bitmap) {
228 DCHECK_CURRENTLY_ON(BrowserThread::UI);
229 if (!bitmap.get()) {
230 // A thumbnail is not locally available for |url|. Make sure it is put in
231 // the list to be fetched at the next visit to this site.
232 scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_));
233 if (top_sites)
234 top_sites->AddForcedURL(url, base::Time::Now());
treibac5372f2015-09-09 09:08:22235 // Also fetch a remote thumbnail if possible. PopularSites or the
236 // SuggestionsService can supply a thumbnail download URL.
knn6cf777fc2015-05-06 13:21:48237 SuggestionsService* suggestions_service =
treibac5372f2015-09-09 09:08:22238 SuggestionsServiceFactory::GetForProfile(profile_);
knn6cf777fc2015-05-06 13:21:48239 if (suggestions_service) {
treibac5372f2015-09-09 09:08:22240 if (popular_sites_) {
241 const std::vector<PopularSites::Site>& sites = popular_sites_->sites();
242 auto it = std::find_if(sites.begin(), sites.end(),
243 [&url](const PopularSites::Site& site) {
244 return site.url == url;
245 });
246 if (it != sites.end() && it->thumbnail_url.is_valid()) {
247 return suggestions_service->GetPageThumbnailWithURL(
248 url, it->thumbnail_url,
249 base::Bind(&MostVisitedSites::OnObtainedThumbnail,
250 weak_ptr_factory_.GetWeakPtr(), false,
251 base::Passed(&j_callback)));
252 }
253 }
254 if (mv_source_ == SUGGESTIONS_SERVICE) {
255 return suggestions_service->GetPageThumbnail(
256 url, base::Bind(&MostVisitedSites::OnObtainedThumbnail,
257 weak_ptr_factory_.GetWeakPtr(), false,
258 base::Passed(&j_callback)));
259 }
knn6cf777fc2015-05-06 13:21:48260 }
261 }
262 OnObtainedThumbnail(true, j_callback.Pass(), url, bitmap.get());
263}
264
265void MostVisitedSites::OnObtainedThumbnail(
266 bool is_local_thumbnail,
267 scoped_ptr<ScopedJavaGlobalRef<jobject>> j_callback,
268 const GURL& url,
269 const SkBitmap* bitmap) {
270 DCHECK_CURRENTLY_ON(BrowserThread::UI);
271 JNIEnv* env = AttachCurrentThread();
272 ScopedJavaLocalRef<jobject> j_bitmap;
273 if (bitmap) {
274 j_bitmap = gfx::ConvertToJavaBitmap(bitmap);
275 if (is_local_thumbnail) {
276 ++num_local_thumbs_;
277 } else {
278 ++num_server_thumbs_;
279 }
280 } else {
281 ++num_empty_thumbs_;
282 }
283 Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
284 env, j_callback->obj(), j_bitmap.obj());
[email protected]c764b2822013-07-03 04:16:48285}
[email protected]a4c2f282013-07-20 05:26:05286
[email protected]7ceb4e32013-12-06 04:13:04287void MostVisitedSites::BlacklistUrl(JNIEnv* env,
288 jobject obj,
289 jstring j_url) {
knn6cf777fc2015-05-06 13:21:48290 GURL url(ConvertJavaStringToUTF8(env, j_url));
[email protected]a4c2f282013-07-20 05:26:05291
treibfac75192015-08-17 13:21:24292 // Always blacklist in the local TopSites.
293 scoped_refptr<TopSites> top_sites = TopSitesFactory::GetForProfile(profile_);
294 if (top_sites)
295 top_sites->AddBlacklistedURL(url);
[email protected]d4975242014-06-02 18:16:20296
treibfac75192015-08-17 13:21:24297 // Only blacklist in the server-side suggestions service if it's active.
298 if (mv_source_ == SUGGESTIONS_SERVICE) {
299 SuggestionsService* suggestions_service =
300 SuggestionsServiceFactory::GetForProfile(profile_);
301 DCHECK(suggestions_service);
302 suggestions_service->BlacklistURL(
303 url, base::Bind(&MostVisitedSites::OnSuggestionsProfileAvailable,
304 weak_ptr_factory_.GetWeakPtr()),
305 base::Closure());
[email protected]d4975242014-06-02 18:16:20306 }
[email protected]a4c2f282013-07-20 05:26:05307}
[email protected]7ceb4e32013-12-06 04:13:04308
[email protected]92a598822014-06-17 10:25:56309void MostVisitedSites::RecordOpenedMostVisitedItem(JNIEnv* env,
310 jobject obj,
311 jint index) {
treib783a3372015-08-25 16:24:39312 DCHECK_GE(index, 0);
313 DCHECK_LT(index, static_cast<int>(tile_sources_.size()));
314 std::string histogram = base::StringPrintf(kOpenedItemHistogramFormat,
315 tile_sources_[index].c_str());
316 LogHistogramEvent(histogram, index, num_sites_);
[email protected]92a598822014-06-17 10:25:56317}
318
[email protected]1d1ab152014-08-21 17:14:12319void MostVisitedSites::OnStateChanged() {
320 // There have been changes to the sync state. This class cares about a few
321 // (just initialized, enabled/disabled or history sync state changed). Re-run
322 // the query code which will use the proper state.
323 QueryMostVisitedURLs();
324}
325
[email protected]7ceb4e32013-12-06 04:13:04326// static
327bool MostVisitedSites::Register(JNIEnv* env) {
328 return RegisterNativesImpl(env);
329}
330
331void MostVisitedSites::QueryMostVisitedURLs() {
[email protected]bc093e82014-03-04 21:49:11332 SuggestionsService* suggestions_service =
[email protected]d4975242014-06-02 18:16:20333 SuggestionsServiceFactory::GetForProfile(profile_);
[email protected]bc093e82014-03-04 21:49:11334 if (suggestions_service) {
335 // Suggestions service is enabled, initiate a query.
336 suggestions_service->FetchSuggestionsData(
[email protected]1d1ab152014-08-21 17:14:12337 GetSyncState(profile_),
knn6cf777fc2015-05-06 13:21:48338 base::Bind(&MostVisitedSites::OnSuggestionsProfileAvailable,
339 weak_ptr_factory_.GetWeakPtr()));
[email protected]bc093e82014-03-04 21:49:11340 } else {
341 InitiateTopSitesQuery();
342 }
343}
344
345void MostVisitedSites::InitiateTopSitesQuery() {
jitendra.ks30f03392015-01-28 09:47:18346 scoped_refptr<TopSites> top_sites = TopSitesFactory::GetForProfile(profile_);
[email protected]7ceb4e32013-12-06 04:13:04347 if (!top_sites)
348 return;
349
350 top_sites->GetMostVisitedURLs(
knn6cf777fc2015-05-06 13:21:48351 base::Bind(&MostVisitedSites::OnMostVisitedURLsAvailable,
352 weak_ptr_factory_.GetWeakPtr()),
[email protected]7ceb4e32013-12-06 04:13:04353 false);
354}
355
[email protected]d4975242014-06-02 18:16:20356void MostVisitedSites::OnMostVisitedURLsAvailable(
[email protected]d4975242014-06-02 18:16:20357 const history::MostVisitedURLList& visited_list) {
358 std::vector<base::string16> titles;
359 std::vector<std::string> urls;
treib783a3372015-08-25 16:24:39360 tile_sources_.clear();
knn6cf777fc2015-05-06 13:21:48361 int num_tiles = std::min(static_cast<int>(visited_list.size()), num_sites_);
362 for (int i = 0; i < num_tiles; ++i) {
363 const history::MostVisitedURL& visited = visited_list[i];
364 if (visited.url.is_empty()) {
365 num_tiles = i;
366 break; // This is the signal that there are no more real visited sites.
367 }
368 titles.push_back(visited.title);
369 urls.push_back(visited.url.spec());
treib783a3372015-08-25 16:24:39370 tile_sources_.push_back(kHistogramClientName);
knn6cf777fc2015-05-06 13:21:48371 }
[email protected]d4975242014-06-02 18:16:20372
treib783a3372015-08-25 16:24:39373 received_most_visited_sites_ = true;
knn6cf777fc2015-05-06 13:21:48374 mv_source_ = TOP_SITES;
treibcffa6502015-08-06 09:12:27375 AddPopularSites(&titles, &urls);
knn6cf777fc2015-05-06 13:21:48376 NotifyMostVisitedURLsObserver(titles, urls);
[email protected]d4975242014-06-02 18:16:20377}
378
[email protected]bc093e82014-03-04 21:49:11379void MostVisitedSites::OnSuggestionsProfileAvailable(
[email protected]bc093e82014-03-04 21:49:11380 const SuggestionsProfile& suggestions_profile) {
knn6cf777fc2015-05-06 13:21:48381 int num_tiles = suggestions_profile.suggestions_size();
mathpd91e8eb2015-03-24 17:37:47382 // With no server suggestions, fall back to local Most Visited.
knn6cf777fc2015-05-06 13:21:48383 if (num_tiles == 0) {
[email protected]bc093e82014-03-04 21:49:11384 InitiateTopSitesQuery();
385 return;
386 }
knn6cf777fc2015-05-06 13:21:48387 if (num_sites_ < num_tiles)
388 num_tiles = num_sites_;
[email protected]bc093e82014-03-04 21:49:11389
390 std::vector<base::string16> titles;
391 std::vector<std::string> urls;
treib783a3372015-08-25 16:24:39392 tile_sources_.clear();
knn6cf777fc2015-05-06 13:21:48393 for (int i = 0; i < num_tiles; ++i) {
[email protected]bc093e82014-03-04 21:49:11394 const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
395 titles.push_back(base::UTF8ToUTF16(suggestion.title()));
396 urls.push_back(suggestion.url());
treib783a3372015-08-25 16:24:39397 std::string tile_source;
398 if (suggestion.providers_size() > 0) {
399 tile_source =
400 base::StringPrintf(kHistogramServerFormat, suggestion.providers(0));
401 } else {
402 tile_source = kHistogramServerName;
[email protected]9ed2f5c2014-06-13 14:55:24403 }
treib783a3372015-08-25 16:24:39404 tile_sources_.push_back(tile_source);
[email protected]bc093e82014-03-04 21:49:11405 }
treib783a3372015-08-25 16:24:39406
407 received_most_visited_sites_ = true;
[email protected]d4975242014-06-02 18:16:20408 mv_source_ = SUGGESTIONS_SERVICE;
treibcffa6502015-08-06 09:12:27409 AddPopularSites(&titles, &urls);
knn6cf777fc2015-05-06 13:21:48410 NotifyMostVisitedURLsObserver(titles, urls);
411}
[email protected]d4975242014-06-02 18:16:20412
treibcffa6502015-08-06 09:12:27413void MostVisitedSites::AddPopularSites(std::vector<base::string16>* titles,
treib783a3372015-08-25 16:24:39414 std::vector<std::string>* urls) {
treibcffa6502015-08-06 09:12:27415 if (!popular_sites_)
416 return;
417
418 DCHECK_EQ(titles->size(), urls->size());
treib783a3372015-08-25 16:24:39419 DCHECK_EQ(titles->size(), tile_sources_.size());
treibcf674e62015-08-17 15:43:01420 DCHECK_LE(static_cast<int>(titles->size()), num_sites_);
treibcffa6502015-08-06 09:12:27421
treibcf674e62015-08-17 15:43:01422 // Collect all non-blacklisted popular suggestions.
treib783a3372015-08-25 16:24:39423 std::vector<base::string16> popular_titles;
424 std::vector<std::string> popular_urls;
treibfac75192015-08-17 13:21:24425 scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_));
treibcffa6502015-08-06 09:12:27426 for (const PopularSites::Site& popular_site : popular_sites_->sites()) {
treibfac75192015-08-17 13:21:24427 // Skip blacklisted sites.
428 if (top_sites && top_sites->IsBlacklisted(popular_site.url))
429 continue;
treibcffa6502015-08-06 09:12:27430
treib783a3372015-08-25 16:24:39431 popular_titles.push_back(popular_site.title);
432 popular_urls.push_back(popular_site.url.spec());
433 if (static_cast<int>(popular_titles.size()) >= num_sites_)
treibcffa6502015-08-06 09:12:27434 break;
435 }
treibcf674e62015-08-17 15:43:01436
treib783a3372015-08-25 16:24:39437 AddPopularSitesImpl(
438 num_sites_, popular_titles, popular_urls, titles, urls, &tile_sources_);
treibcf674e62015-08-17 15:43:01439}
440
441// static
442void MostVisitedSites::AddPopularSitesImpl(
443 int num_sites,
treib783a3372015-08-25 16:24:39444 const std::vector<base::string16>& popular_titles,
445 const std::vector<std::string>& popular_urls,
treibcf674e62015-08-17 15:43:01446 std::vector<base::string16>* titles,
447 std::vector<std::string>* urls,
treib783a3372015-08-25 16:24:39448 std::vector<std::string>* tile_sources) {
treibcf674e62015-08-17 15:43:01449 // Start off with the popular suggestions.
450 std::vector<base::string16> new_titles(popular_titles);
451 std::vector<std::string> new_urls(popular_urls);
treib783a3372015-08-25 16:24:39452 std::vector<std::string> new_tile_sources(new_titles.size(),
453 kHistogramPopularName);
treibcf674e62015-08-17 15:43:01454
455 // Now, go over the personalized suggestions and replace matching popular
456 // suggestions. This is so that when some of the popular suggestions become
457 // personal, they retain their absolute positions.
treibcf674e62015-08-17 15:43:01458 std::vector<base::string16> titles_to_insert;
459 std::vector<std::string> urls_to_insert;
treib783a3372015-08-25 16:24:39460 std::vector<std::string> tile_sources_to_insert;
treibcf674e62015-08-17 15:43:01461 for (size_t site_index = 0; site_index < titles->size(); site_index++) {
462 const base::string16& title = (*titles)[site_index];
463 const std::string& url = (*urls)[site_index];
treib783a3372015-08-25 16:24:39464 const std::string& tile_source = (*tile_sources)[site_index];
treibcf674e62015-08-17 15:43:01465 // See if we already have a matching popular site.
466 bool found = false;
467 for (size_t i = 0; i < new_urls.size(); i++) {
treib783a3372015-08-25 16:24:39468 if (new_tile_sources[i] == kHistogramPopularName &&
treibcf674e62015-08-17 15:43:01469 GURL(new_urls[i]).host() == GURL(url).host()) {
470 // We have a matching popular sites suggestion. Replace it with the
471 // actual URL and title.
472 new_titles[i] = title;
473 new_urls[i] = url;
treib783a3372015-08-25 16:24:39474 new_tile_sources[i] = tile_source;
treibcf674e62015-08-17 15:43:01475 found = true;
476 break;
477 }
478 }
479 if (!found) {
480 titles_to_insert.push_back(title);
481 urls_to_insert.push_back(url);
treib783a3372015-08-25 16:24:39482 tile_sources_to_insert.push_back(tile_source);
treibcf674e62015-08-17 15:43:01483 }
484 }
485
486 // Append personalized suggestions at the end if there's room.
487 size_t num_to_append =
488 std::min(static_cast<size_t>(num_sites) - new_titles.size(),
489 titles_to_insert.size());
490 new_titles.insert(new_titles.end(),
491 titles_to_insert.end() - num_to_append,
492 titles_to_insert.end());
493 new_urls.insert(new_urls.end(),
494 urls_to_insert.end() - num_to_append,
495 urls_to_insert.end());
treib783a3372015-08-25 16:24:39496 new_tile_sources.insert(new_tile_sources.end(),
497 tile_sources_to_insert.end() - num_to_append,
498 tile_sources_to_insert.end());
treibcf674e62015-08-17 15:43:01499
500 // Finally, go over the remaining personalized suggestions and evict popular
501 // suggestions to accommodate them. Do it in reverse order, so the least
502 // important popular suggestions will be evicted.
503 for (size_t i = titles_to_insert.size() - num_to_append; i > 0; --i) {
504 const base::string16& title = titles_to_insert[i - 1];
505 const std::string& url = urls_to_insert[i - 1];
treib783a3372015-08-25 16:24:39506 const std::string& tile_source = tile_sources_to_insert[i - 1];
treibcf674e62015-08-17 15:43:01507 for (size_t insert_i = new_titles.size(); insert_i > 0; --insert_i) {
508 size_t insert_index = insert_i - 1;
treib783a3372015-08-25 16:24:39509 if (new_tile_sources[insert_index] == kHistogramPopularName) {
treibcf674e62015-08-17 15:43:01510 new_titles[insert_index] = title;
511 new_urls[insert_index] = url;
treib783a3372015-08-25 16:24:39512 new_tile_sources[insert_index] = tile_source;
treibcf674e62015-08-17 15:43:01513 break;
514 }
515 }
516 }
517
518 titles->swap(new_titles);
519 urls->swap(new_urls);
treib783a3372015-08-25 16:24:39520 tile_sources->swap(new_tile_sources);
treibcffa6502015-08-06 09:12:27521}
522
knn6cf777fc2015-05-06 13:21:48523void MostVisitedSites::NotifyMostVisitedURLsObserver(
524 const std::vector<base::string16>& titles,
525 const std::vector<std::string>& urls) {
526 DCHECK_EQ(titles.size(), urls.size());
treib783a3372015-08-25 16:24:39527 if (received_most_visited_sites_ && received_popular_sites_ &&
528 !recorded_uma_) {
529 RecordImpressionUMAMetrics();
knn6cf777fc2015-05-06 13:21:48530 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumTilesHistogramName, titles.size());
treib783a3372015-08-25 16:24:39531 recorded_uma_ = true;
532 }
[email protected]bc093e82014-03-04 21:49:11533 JNIEnv* env = AttachCurrentThread();
534 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
knn6cf777fc2015-05-06 13:21:48535 env, observer_.obj(), ToJavaArrayOfStrings(env, titles).obj(),
[email protected]bc093e82014-03-04 21:49:11536 ToJavaArrayOfStrings(env, urls).obj());
537}
538
treibcffa6502015-08-06 09:12:27539void MostVisitedSites::OnPopularSitesAvailable(bool success) {
treib783a3372015-08-25 16:24:39540 received_popular_sites_ = true;
541
treibcffa6502015-08-06 09:12:27542 if (!success) {
543 LOG(WARNING) << "Download of popular sites failed";
544 return;
545 }
546
treib7f5cf872015-08-11 13:11:47547 if (observer_.is_null())
548 return;
549
550 std::vector<std::string> urls;
551 std::vector<std::string> favicon_urls;
552 for (const PopularSites::Site& popular_site : popular_sites_->sites()) {
553 urls.push_back(popular_site.url.spec());
554 favicon_urls.push_back(popular_site.favicon_url.spec());
555 }
556 JNIEnv* env = AttachCurrentThread();
557 Java_MostVisitedURLsObserver_onPopularURLsAvailable(
558 env, observer_.obj(), ToJavaArrayOfStrings(env, urls).obj(),
559 ToJavaArrayOfStrings(env, favicon_urls).obj());
560
561 QueryMostVisitedURLs();
treibcffa6502015-08-06 09:12:27562}
563
treib783a3372015-08-25 16:24:39564void MostVisitedSites::RecordThumbnailUMAMetrics() {
[email protected]32809e9e2014-06-19 01:10:58565 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumLocalThumbnailTilesHistogramName,
566 num_local_thumbs_);
[email protected]1d778e12014-06-17 02:28:51567 num_local_thumbs_ = 0;
[email protected]32809e9e2014-06-19 01:10:58568 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumEmptyTilesHistogramName, num_empty_thumbs_);
[email protected]1d778e12014-06-17 02:28:51569 num_empty_thumbs_ = 0;
[email protected]32809e9e2014-06-19 01:10:58570 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumServerTilesHistogramName, num_server_thumbs_);
[email protected]1d778e12014-06-17 02:28:51571 num_server_thumbs_ = 0;
572}
573
treib783a3372015-08-25 16:24:39574void MostVisitedSites::RecordImpressionUMAMetrics() {
575 for (size_t i = 0; i < tile_sources_.size(); i++) {
576 std::string histogram = base::StringPrintf(kImpressionHistogramFormat,
577 tile_sources_[i].c_str());
578 LogHistogramEvent(histogram, static_cast<int>(i), num_sites_);
579 }
580}
581
sdefresneedf9e01f2015-01-13 19:45:41582void MostVisitedSites::TopSitesLoaded(history::TopSites* top_sites) {
583}
584
fserbdb575112015-06-29 21:31:59585void MostVisitedSites::TopSitesChanged(history::TopSites* top_sites,
586 ChangeReason change_reason) {
sdefresneedf9e01f2015-01-13 19:45:41587 if (mv_source_ == TOP_SITES) {
588 // The displayed suggestions are invalidated.
589 QueryMostVisitedURLs();
590 }
591}
592
torne89cc5d92015-09-04 11:16:35593static jlong Init(JNIEnv* env,
594 const JavaParamRef<jobject>& obj,
595 const JavaParamRef<jobject>& jprofile) {
[email protected]7ceb4e32013-12-06 04:13:04596 MostVisitedSites* most_visited_sites =
597 new MostVisitedSites(ProfileAndroid::FromProfileAndroid(jprofile));
598 return reinterpret_cast<intptr_t>(most_visited_sites);
599}