Add more checks to investigate SupervisedUserPrefStore crash at startup.
[chromium-blink-merge.git] / chrome / browser / android / most_visited_sites.cc
blob3e01aaf85c7fb270658d92795a1b4d76d82c1e91
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.
5 #include "chrome/browser/android/most_visited_sites.h"
7 #include <string>
8 #include <vector>
10 #include "base/android/jni_android.h"
11 #include "base/android/jni_array.h"
12 #include "base/android/jni_string.h"
13 #include "base/android/scoped_java_ref.h"
14 #include "base/callback.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/sparse_histogram.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/time/time.h"
21 #include "chrome/browser/history/top_sites_factory.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/profiles/profile_android.h"
24 #include "chrome/browser/search/suggestions/suggestions_service_factory.h"
25 #include "chrome/browser/search/suggestions/suggestions_source.h"
26 #include "chrome/browser/sync/profile_sync_service.h"
27 #include "chrome/browser/sync/profile_sync_service_factory.h"
28 #include "chrome/browser/thumbnails/thumbnail_list_source.h"
29 #include "components/history/core/browser/top_sites.h"
30 #include "components/suggestions/suggestions_service.h"
31 #include "components/suggestions/suggestions_utils.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/url_data_source.h"
34 #include "jni/MostVisitedSites_jni.h"
35 #include "third_party/skia/include/core/SkBitmap.h"
36 #include "ui/gfx/android/java_bitmap.h"
37 #include "ui/gfx/codec/jpeg_codec.h"
39 using base::android::AttachCurrentThread;
40 using base::android::ConvertUTF8ToJavaString;
41 using base::android::ConvertJavaStringToUTF8;
42 using base::android::ScopedJavaGlobalRef;
43 using base::android::ToJavaArrayOfStrings;
44 using base::android::CheckException;
45 using content::BrowserThread;
46 using history::TopSites;
47 using suggestions::ChromeSuggestion;
48 using suggestions::SuggestionsProfile;
49 using suggestions::SuggestionsService;
50 using suggestions::SuggestionsServiceFactory;
51 using suggestions::SyncState;
53 namespace {
55 // Total number of tiles displayed.
56 const char kNumTilesHistogramName[] = "NewTabPage.NumberOfTiles";
57 // Tracking thumbnails.
58 const char kNumLocalThumbnailTilesHistogramName[] =
59 "NewTabPage.NumberOfThumbnailTiles";
60 const char kNumEmptyTilesHistogramName[] = "NewTabPage.NumberOfGrayTiles";
61 const char kNumServerTilesHistogramName[] = "NewTabPage.NumberOfExternalTiles";
62 // Client suggestion opened.
63 const char kOpenedItemClientHistogramName[] = "NewTabPage.MostVisited.client";
64 // Control group suggestion opened.
65 const char kOpenedItemControlHistogramName[] = "NewTabPage.MostVisited.client0";
66 // Server suggestion opened, no provider.
67 const char kOpenedItemServerHistogramName[] = "NewTabPage.MostVisited.server";
68 // Server suggestion opened with provider.
69 const char kOpenedItemServerProviderHistogramFormat[] =
70 "NewTabPage.MostVisited.server%d";
71 // Client impression.
72 const char kImpressionClientHistogramName[] =
73 "NewTabPage.SuggestionsImpression.client";
74 // Control group impression.
75 const char kImpressionControlHistogramName[] =
76 "NewTabPage.SuggestionsImpression.client0";
77 // Server suggestion impression, no provider.
78 const char kImpressionServerHistogramName[] =
79 "NewTabPage.SuggestionsImpression.server";
80 // Server suggestion impression with provider.
81 const char kImpressionServerHistogramFormat[] =
82 "NewTabPage.SuggestionsImpression.server%d";
84 void ExtractMostVisitedTitlesAndURLs(
85 const history::MostVisitedURLList& visited_list,
86 std::vector<base::string16>* titles,
87 std::vector<std::string>* urls,
88 int num_sites) {
89 size_t max = static_cast<size_t>(num_sites);
90 for (size_t i = 0; i < visited_list.size() && i < max; ++i) {
91 const history::MostVisitedURL& visited = visited_list[i];
93 if (visited.url.is_empty())
94 break; // This is the signal that there are no more real visited sites.
96 titles->push_back(visited.title);
97 urls->push_back(visited.url.spec());
101 SkBitmap ExtractThumbnail(const base::RefCountedMemory& image_data) {
102 scoped_ptr<SkBitmap> image(gfx::JPEGCodec::Decode(
103 image_data.front(),
104 image_data.size()));
105 return image.get() ? *image : SkBitmap();
108 void AddForcedURLOnUIThread(scoped_refptr<history::TopSites> top_sites,
109 const GURL& url) {
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
111 top_sites->AddForcedURL(url, base::Time::Now());
114 // Runs on the DB thread.
115 void GetUrlThumbnailTask(
116 std::string url_string,
117 scoped_refptr<TopSites> top_sites,
118 ScopedJavaGlobalRef<jobject>* j_callback,
119 MostVisitedSites::LookupSuccessCallback lookup_success_ui_callback,
120 base::Closure lookup_failed_ui_callback) {
121 JNIEnv* env = AttachCurrentThread();
123 ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
124 new ScopedJavaGlobalRef<jobject>();
126 GURL gurl(url_string);
128 scoped_refptr<base::RefCountedMemory> data;
129 if (top_sites->GetPageThumbnail(gurl, false, &data)) {
130 SkBitmap thumbnail_bitmap = ExtractThumbnail(*data.get());
131 if (!thumbnail_bitmap.empty()) {
132 j_bitmap_ref->Reset(
133 env,
134 gfx::ConvertToJavaBitmap(&thumbnail_bitmap).obj());
136 } else {
137 // A thumbnail is not locally available for |gurl|. Make sure it is put in
138 // the list to be fetched at the next visit to this site.
139 BrowserThread::PostTask(
140 BrowserThread::UI, FROM_HERE,
141 base::Bind(AddForcedURLOnUIThread, top_sites, gurl));
143 // If appropriate, return on the UI thread to execute the proper callback.
144 if (!lookup_failed_ui_callback.is_null()) {
145 BrowserThread::PostTask(
146 BrowserThread::UI, FROM_HERE, lookup_failed_ui_callback);
147 delete j_bitmap_ref;
148 return;
152 // Since j_callback is owned by this callback, when the callback falls out of
153 // scope it will be deleted. We need to pass ownership to the next callback.
154 ScopedJavaGlobalRef<jobject>* j_callback_pass =
155 new ScopedJavaGlobalRef<jobject>(*j_callback);
156 BrowserThread::PostTask(
157 BrowserThread::UI, FROM_HERE,
158 base::Bind(lookup_success_ui_callback, base::Owned(j_bitmap_ref),
159 base::Owned(j_callback_pass)));
162 // Log an event for a given |histogram| at a given element |position|. This
163 // routine exists because regular histogram macros are cached thus can't be used
164 // if the name of the histogram will change at a given call site.
165 void LogHistogramEvent(const std::string& histogram, int position,
166 int num_sites) {
167 base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
168 histogram,
170 num_sites,
171 num_sites + 1,
172 base::Histogram::kUmaTargetedHistogramFlag);
173 if (counter)
174 counter->Add(position);
177 // Return the current SyncState for use with the SuggestionsService.
178 SyncState GetSyncState(Profile* profile) {
179 ProfileSyncService* sync =
180 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
181 if (!sync)
182 return SyncState::SYNC_OR_HISTORY_SYNC_DISABLED;
183 return suggestions::GetSyncState(
184 sync->IsSyncEnabledAndLoggedIn(),
185 sync->SyncActive() && sync->ConfigurationDone(),
186 sync->GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES));
189 } // namespace
191 MostVisitedSites::MostVisitedSites(Profile* profile)
192 : profile_(profile), num_sites_(0), is_control_group_(false),
193 initial_load_done_(false), num_local_thumbs_(0), num_server_thumbs_(0),
194 num_empty_thumbs_(0), scoped_observer_(this), weak_ptr_factory_(this) {
195 // Register the debugging page for the Suggestions Service and the thumbnails
196 // debugging page.
197 content::URLDataSource::Add(profile_,
198 new suggestions::SuggestionsSource(profile_));
199 content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
201 // Register this class as an observer to the sync service. It is important to
202 // be notified of changes in the sync state such as initialization, sync
203 // being enabled or disabled, etc.
204 ProfileSyncService* profile_sync_service =
205 ProfileSyncServiceFactory::GetForProfile(profile_);
206 if (profile_sync_service)
207 profile_sync_service->AddObserver(this);
210 MostVisitedSites::~MostVisitedSites() {
211 ProfileSyncService* profile_sync_service =
212 ProfileSyncServiceFactory::GetForProfile(profile_);
213 if (profile_sync_service && profile_sync_service->HasObserver(this))
214 profile_sync_service->RemoveObserver(this);
217 void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) {
218 delete this;
221 void MostVisitedSites::OnLoadingComplete(JNIEnv* env, jobject obj) {
222 RecordUMAMetrics();
225 void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv* env,
226 jobject obj,
227 jobject j_observer,
228 jint num_sites) {
229 observer_.Reset(env, j_observer);
230 num_sites_ = num_sites;
232 QueryMostVisitedURLs();
234 scoped_refptr<history::TopSites> top_sites =
235 TopSitesFactory::GetForProfile(profile_);
236 if (top_sites) {
237 // TopSites updates itself after a delay. To ensure up-to-date results,
238 // force an update now.
239 top_sites->SyncWithHistory();
241 // Register as TopSitesObserver so that we can update ourselves when the
242 // TopSites changes.
243 scoped_observer_.Add(top_sites.get());
247 // Called from the UI Thread.
248 void MostVisitedSites::GetURLThumbnail(JNIEnv* env,
249 jobject obj,
250 jstring url,
251 jobject j_callback_obj) {
252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253 ScopedJavaGlobalRef<jobject>* j_callback =
254 new ScopedJavaGlobalRef<jobject>();
255 j_callback->Reset(env, j_callback_obj);
257 std::string url_string = ConvertJavaStringToUTF8(env, url);
258 scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_));
260 // If the Suggestions service is enabled and in use, create a callback to
261 // fetch a server thumbnail from it, in case the local thumbnail is not found.
262 SuggestionsService* suggestions_service =
263 SuggestionsServiceFactory::GetForProfile(profile_);
264 bool use_suggestions_service = suggestions_service &&
265 mv_source_ == SUGGESTIONS_SERVICE;
266 base::Closure lookup_failed_callback = use_suggestions_service ?
267 base::Bind(&MostVisitedSites::GetSuggestionsThumbnailOnUIThread,
268 weak_ptr_factory_.GetWeakPtr(),
269 suggestions_service, url_string,
270 base::Owned(new ScopedJavaGlobalRef<jobject>(*j_callback))) :
271 base::Closure();
272 LookupSuccessCallback lookup_success_callback =
273 base::Bind(&MostVisitedSites::OnObtainedThumbnail,
274 weak_ptr_factory_.GetWeakPtr());
276 BrowserThread::PostTask(
277 BrowserThread::DB, FROM_HERE,
278 base::Bind(
279 &GetUrlThumbnailTask, url_string, top_sites,
280 base::Owned(j_callback), lookup_success_callback,
281 lookup_failed_callback));
284 void MostVisitedSites::BlacklistUrl(JNIEnv* env,
285 jobject obj,
286 jstring j_url) {
287 std::string url = ConvertJavaStringToUTF8(env, j_url);
289 switch (mv_source_) {
290 case TOP_SITES: {
291 scoped_refptr<TopSites> top_sites =
292 TopSitesFactory::GetForProfile(profile_);
293 DCHECK(top_sites);
294 top_sites->AddBlacklistedURL(GURL(url));
295 break;
298 case SUGGESTIONS_SERVICE: {
299 SuggestionsService* suggestions_service =
300 SuggestionsServiceFactory::GetForProfile(profile_);
301 DCHECK(suggestions_service);
302 suggestions_service->BlacklistURL(
303 GURL(url),
304 base::Bind(
305 &MostVisitedSites::OnSuggestionsProfileAvailable,
306 weak_ptr_factory_.GetWeakPtr(),
307 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_))),
308 base::Closure());
309 break;
314 void MostVisitedSites::RecordOpenedMostVisitedItem(JNIEnv* env,
315 jobject obj,
316 jint index) {
317 switch (mv_source_) {
318 case TOP_SITES: {
319 const std::string histogram = is_control_group_ ?
320 kOpenedItemControlHistogramName : kOpenedItemClientHistogramName;
321 LogHistogramEvent(histogram, index, num_sites_);
322 break;
324 case SUGGESTIONS_SERVICE: {
325 if (server_suggestions_.suggestions_size() > index) {
326 if (server_suggestions_.suggestions(index).providers_size()) {
327 std::string histogram = base::StringPrintf(
328 kOpenedItemServerProviderHistogramFormat,
329 server_suggestions_.suggestions(index).providers(0));
330 LogHistogramEvent(histogram, index, num_sites_);
331 } else {
332 UMA_HISTOGRAM_SPARSE_SLOWLY(kOpenedItemServerHistogramName, index);
335 break;
340 void MostVisitedSites::OnStateChanged() {
341 // There have been changes to the sync state. This class cares about a few
342 // (just initialized, enabled/disabled or history sync state changed). Re-run
343 // the query code which will use the proper state.
344 QueryMostVisitedURLs();
347 // static
348 bool MostVisitedSites::Register(JNIEnv* env) {
349 return RegisterNativesImpl(env);
352 void MostVisitedSites::QueryMostVisitedURLs() {
353 SuggestionsService* suggestions_service =
354 SuggestionsServiceFactory::GetForProfile(profile_);
355 if (suggestions_service) {
356 // Suggestions service is enabled, initiate a query.
357 suggestions_service->FetchSuggestionsData(
358 GetSyncState(profile_),
359 base::Bind(
360 &MostVisitedSites::OnSuggestionsProfileAvailable,
361 weak_ptr_factory_.GetWeakPtr(),
362 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_))));
363 } else {
364 InitiateTopSitesQuery();
368 void MostVisitedSites::InitiateTopSitesQuery() {
369 scoped_refptr<TopSites> top_sites = TopSitesFactory::GetForProfile(profile_);
370 if (!top_sites)
371 return;
373 top_sites->GetMostVisitedURLs(
374 base::Bind(
375 &MostVisitedSites::OnMostVisitedURLsAvailable,
376 weak_ptr_factory_.GetWeakPtr(),
377 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_)),
378 num_sites_),
379 false);
382 void MostVisitedSites::OnMostVisitedURLsAvailable(
383 ScopedJavaGlobalRef<jobject>* j_observer,
384 int num_sites,
385 const history::MostVisitedURLList& visited_list) {
386 std::vector<base::string16> titles;
387 std::vector<std::string> urls;
388 ExtractMostVisitedTitlesAndURLs(visited_list, &titles, &urls, num_sites);
390 mv_source_ = TOP_SITES;
392 // Only log impression metrics on the initial load of the NTP.
393 if (!initial_load_done_) {
394 int num_tiles = urls.size();
395 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumTilesHistogramName, num_tiles);
396 const std::string histogram = is_control_group_ ?
397 kImpressionControlHistogramName : kImpressionClientHistogramName;
398 for (int i = 0; i < num_tiles; ++i) {
399 LogHistogramEvent(histogram, i, num_sites_);
402 initial_load_done_ = true;
404 JNIEnv* env = AttachCurrentThread();
405 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
406 env,
407 j_observer->obj(),
408 ToJavaArrayOfStrings(env, titles).obj(),
409 ToJavaArrayOfStrings(env, urls).obj());
412 void MostVisitedSites::OnSuggestionsProfileAvailable(
413 ScopedJavaGlobalRef<jobject>* j_observer,
414 const SuggestionsProfile& suggestions_profile) {
415 int size = suggestions_profile.suggestions_size();
417 // Determine if the user is in a control group (they would have received
418 // suggestions, but are in a group where they shouldn't).
419 is_control_group_ = size && SuggestionsService::IsControlGroup();
421 // If no suggestions data is available or the user is in a control group,
422 // initiate Top Sites query.
423 if (is_control_group_ || !size) {
424 InitiateTopSitesQuery();
425 return;
428 std::vector<base::string16> titles;
429 std::vector<std::string> urls;
431 int i = 0;
432 for (; i < size && i < num_sites_; ++i) {
433 const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
434 titles.push_back(base::UTF8ToUTF16(suggestion.title()));
435 urls.push_back(suggestion.url());
436 // Only log impression metrics on the initial NTP load.
437 if (!initial_load_done_) {
438 if (suggestion.providers_size()) {
439 std::string histogram = base::StringPrintf(
440 kImpressionServerHistogramFormat, suggestion.providers(0));
441 LogHistogramEvent(histogram, i, num_sites_);
442 } else {
443 UMA_HISTOGRAM_SPARSE_SLOWLY(kImpressionServerHistogramName, i);
447 if (!initial_load_done_) {
448 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumTilesHistogramName, i);
450 initial_load_done_ = true;
452 mv_source_ = SUGGESTIONS_SERVICE;
453 // Keep a copy of the suggestions for eventual logging.
454 server_suggestions_ = suggestions_profile;
456 JNIEnv* env = AttachCurrentThread();
457 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
458 env,
459 j_observer->obj(),
460 ToJavaArrayOfStrings(env, titles).obj(),
461 ToJavaArrayOfStrings(env, urls).obj());
464 void MostVisitedSites::OnObtainedThumbnail(
465 ScopedJavaGlobalRef<jobject>* bitmap,
466 ScopedJavaGlobalRef<jobject>* j_callback) {
467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
468 JNIEnv* env = AttachCurrentThread();
469 if (bitmap->obj()) {
470 num_local_thumbs_++;
471 } else {
472 num_empty_thumbs_++;
474 Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
475 env, j_callback->obj(), bitmap->obj());
478 void MostVisitedSites::GetSuggestionsThumbnailOnUIThread(
479 SuggestionsService* suggestions_service,
480 const std::string& url_string,
481 ScopedJavaGlobalRef<jobject>* j_callback) {
482 suggestions_service->GetPageThumbnail(
483 GURL(url_string),
484 base::Bind(&MostVisitedSites::OnSuggestionsThumbnailAvailable,
485 weak_ptr_factory_.GetWeakPtr(),
486 base::Owned(new ScopedJavaGlobalRef<jobject>(*j_callback))));
489 void MostVisitedSites::OnSuggestionsThumbnailAvailable(
490 ScopedJavaGlobalRef<jobject>* j_callback,
491 const GURL& url,
492 const SkBitmap* bitmap) {
493 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
494 JNIEnv* env = AttachCurrentThread();
496 ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
497 new ScopedJavaGlobalRef<jobject>();
498 if (bitmap) {
499 num_server_thumbs_++;
500 j_bitmap_ref->Reset(
501 env,
502 gfx::ConvertToJavaBitmap(bitmap).obj());
503 } else {
504 num_empty_thumbs_++;
507 Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
508 env, j_callback->obj(), j_bitmap_ref->obj());
511 void MostVisitedSites::RecordUMAMetrics() {
512 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumLocalThumbnailTilesHistogramName,
513 num_local_thumbs_);
514 num_local_thumbs_ = 0;
515 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumEmptyTilesHistogramName, num_empty_thumbs_);
516 num_empty_thumbs_ = 0;
517 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumServerTilesHistogramName, num_server_thumbs_);
518 num_server_thumbs_ = 0;
521 void MostVisitedSites::TopSitesLoaded(history::TopSites* top_sites) {
524 void MostVisitedSites::TopSitesChanged(history::TopSites* top_sites) {
525 if (mv_source_ == TOP_SITES) {
526 // The displayed suggestions are invalidated.
527 QueryMostVisitedURLs();
531 static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
532 MostVisitedSites* most_visited_sites =
533 new MostVisitedSites(ProfileAndroid::FromProfileAndroid(jprofile));
534 return reinterpret_cast<intptr_t>(most_visited_sites);