Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / android / most_visited_sites.cc
blob71bc6a15be688a540df8807621fae0e71f9cdad8
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 // Server suggestion opened, no provider.
65 const char kOpenedItemServerHistogramName[] = "NewTabPage.MostVisited.server";
66 // Server suggestion opened with provider.
67 const char kOpenedItemServerProviderHistogramFormat[] =
68 "NewTabPage.MostVisited.server%d";
69 // Client impression.
70 const char kImpressionClientHistogramName[] =
71 "NewTabPage.SuggestionsImpression.client";
72 // Server suggestion impression, no provider.
73 const char kImpressionServerHistogramName[] =
74 "NewTabPage.SuggestionsImpression.server";
75 // Server suggestion impression with provider.
76 const char kImpressionServerHistogramFormat[] =
77 "NewTabPage.SuggestionsImpression.server%d";
79 void ExtractMostVisitedTitlesAndURLs(
80 const history::MostVisitedURLList& visited_list,
81 std::vector<base::string16>* titles,
82 std::vector<std::string>* urls,
83 int num_sites) {
84 size_t max = static_cast<size_t>(num_sites);
85 for (size_t i = 0; i < visited_list.size() && i < max; ++i) {
86 const history::MostVisitedURL& visited = visited_list[i];
88 if (visited.url.is_empty())
89 break; // This is the signal that there are no more real visited sites.
91 titles->push_back(visited.title);
92 urls->push_back(visited.url.spec());
96 SkBitmap ExtractThumbnail(const base::RefCountedMemory& image_data) {
97 scoped_ptr<SkBitmap> image(gfx::JPEGCodec::Decode(
98 image_data.front(),
99 image_data.size()));
100 return image.get() ? *image : SkBitmap();
103 void AddForcedURLOnUIThread(scoped_refptr<history::TopSites> top_sites,
104 const GURL& url) {
105 DCHECK_CURRENTLY_ON(BrowserThread::UI);
106 top_sites->AddForcedURL(url, base::Time::Now());
109 // Runs on the DB thread.
110 void GetUrlThumbnailTask(
111 std::string url_string,
112 scoped_refptr<TopSites> top_sites,
113 ScopedJavaGlobalRef<jobject>* j_callback,
114 MostVisitedSites::LookupSuccessCallback lookup_success_ui_callback,
115 base::Closure lookup_failed_ui_callback) {
116 JNIEnv* env = AttachCurrentThread();
118 ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
119 new ScopedJavaGlobalRef<jobject>();
121 GURL gurl(url_string);
123 scoped_refptr<base::RefCountedMemory> data;
124 if (top_sites->GetPageThumbnail(gurl, false, &data)) {
125 SkBitmap thumbnail_bitmap = ExtractThumbnail(*data.get());
126 if (!thumbnail_bitmap.empty()) {
127 j_bitmap_ref->Reset(
128 env,
129 gfx::ConvertToJavaBitmap(&thumbnail_bitmap).obj());
131 } else {
132 // A thumbnail is not locally available for |gurl|. Make sure it is put in
133 // the list to be fetched at the next visit to this site.
134 BrowserThread::PostTask(
135 BrowserThread::UI, FROM_HERE,
136 base::Bind(AddForcedURLOnUIThread, top_sites, gurl));
138 // If appropriate, return on the UI thread to execute the proper callback.
139 if (!lookup_failed_ui_callback.is_null()) {
140 BrowserThread::PostTask(
141 BrowserThread::UI, FROM_HERE, lookup_failed_ui_callback);
142 delete j_bitmap_ref;
143 return;
147 // Since j_callback is owned by this callback, when the callback falls out of
148 // scope it will be deleted. We need to pass ownership to the next callback.
149 ScopedJavaGlobalRef<jobject>* j_callback_pass =
150 new ScopedJavaGlobalRef<jobject>(*j_callback);
151 BrowserThread::PostTask(
152 BrowserThread::UI, FROM_HERE,
153 base::Bind(lookup_success_ui_callback, base::Owned(j_bitmap_ref),
154 base::Owned(j_callback_pass)));
157 // Log an event for a given |histogram| at a given element |position|. This
158 // routine exists because regular histogram macros are cached thus can't be used
159 // if the name of the histogram will change at a given call site.
160 void LogHistogramEvent(const std::string& histogram, int position,
161 int num_sites) {
162 base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
163 histogram,
165 num_sites,
166 num_sites + 1,
167 base::Histogram::kUmaTargetedHistogramFlag);
168 if (counter)
169 counter->Add(position);
172 // Return the current SyncState for use with the SuggestionsService.
173 SyncState GetSyncState(Profile* profile) {
174 ProfileSyncService* sync =
175 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
176 if (!sync)
177 return SyncState::SYNC_OR_HISTORY_SYNC_DISABLED;
178 return suggestions::GetSyncState(
179 sync->IsSyncEnabledAndLoggedIn(),
180 sync->SyncActive() && sync->ConfigurationDone(),
181 sync->GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES));
184 } // namespace
186 MostVisitedSites::MostVisitedSites(Profile* profile)
187 : profile_(profile), num_sites_(0), initial_load_done_(false),
188 num_local_thumbs_(0), num_server_thumbs_(0), num_empty_thumbs_(0),
189 scoped_observer_(this), weak_ptr_factory_(this) {
190 // Register the debugging page for the Suggestions Service and the thumbnails
191 // debugging page.
192 content::URLDataSource::Add(profile_,
193 new suggestions::SuggestionsSource(profile_));
194 content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
196 // Register this class as an observer to the sync service. It is important to
197 // be notified of changes in the sync state such as initialization, sync
198 // being enabled or disabled, etc.
199 ProfileSyncService* profile_sync_service =
200 ProfileSyncServiceFactory::GetForProfile(profile_);
201 if (profile_sync_service)
202 profile_sync_service->AddObserver(this);
205 MostVisitedSites::~MostVisitedSites() {
206 ProfileSyncService* profile_sync_service =
207 ProfileSyncServiceFactory::GetForProfile(profile_);
208 if (profile_sync_service && profile_sync_service->HasObserver(this))
209 profile_sync_service->RemoveObserver(this);
212 void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) {
213 delete this;
216 void MostVisitedSites::OnLoadingComplete(JNIEnv* env, jobject obj) {
217 RecordUMAMetrics();
220 void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv* env,
221 jobject obj,
222 jobject j_observer,
223 jint num_sites) {
224 observer_.Reset(env, j_observer);
225 num_sites_ = num_sites;
227 QueryMostVisitedURLs();
229 scoped_refptr<history::TopSites> top_sites =
230 TopSitesFactory::GetForProfile(profile_);
231 if (top_sites) {
232 // TopSites updates itself after a delay. To ensure up-to-date results,
233 // force an update now.
234 top_sites->SyncWithHistory();
236 // Register as TopSitesObserver so that we can update ourselves when the
237 // TopSites changes.
238 scoped_observer_.Add(top_sites.get());
242 // Called from the UI Thread.
243 void MostVisitedSites::GetURLThumbnail(JNIEnv* env,
244 jobject obj,
245 jstring url,
246 jobject j_callback_obj) {
247 DCHECK_CURRENTLY_ON(BrowserThread::UI);
248 ScopedJavaGlobalRef<jobject>* j_callback =
249 new ScopedJavaGlobalRef<jobject>();
250 j_callback->Reset(env, j_callback_obj);
252 std::string url_string = ConvertJavaStringToUTF8(env, url);
253 scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_));
255 // If the Suggestions service is enabled and in use, create a callback to
256 // fetch a server thumbnail from it, in case the local thumbnail is not found.
257 SuggestionsService* suggestions_service =
258 SuggestionsServiceFactory::GetForProfile(profile_);
259 bool use_suggestions_service = suggestions_service &&
260 mv_source_ == SUGGESTIONS_SERVICE;
261 base::Closure lookup_failed_callback = use_suggestions_service ?
262 base::Bind(&MostVisitedSites::GetSuggestionsThumbnailOnUIThread,
263 weak_ptr_factory_.GetWeakPtr(),
264 suggestions_service, url_string,
265 base::Owned(new ScopedJavaGlobalRef<jobject>(*j_callback))) :
266 base::Closure();
267 LookupSuccessCallback lookup_success_callback =
268 base::Bind(&MostVisitedSites::OnObtainedThumbnail,
269 weak_ptr_factory_.GetWeakPtr());
271 BrowserThread::PostTask(
272 BrowserThread::DB, FROM_HERE,
273 base::Bind(
274 &GetUrlThumbnailTask, url_string, top_sites,
275 base::Owned(j_callback), lookup_success_callback,
276 lookup_failed_callback));
279 void MostVisitedSites::BlacklistUrl(JNIEnv* env,
280 jobject obj,
281 jstring j_url) {
282 std::string url = ConvertJavaStringToUTF8(env, j_url);
284 switch (mv_source_) {
285 case TOP_SITES: {
286 scoped_refptr<TopSites> top_sites =
287 TopSitesFactory::GetForProfile(profile_);
288 DCHECK(top_sites);
289 top_sites->AddBlacklistedURL(GURL(url));
290 break;
293 case SUGGESTIONS_SERVICE: {
294 SuggestionsService* suggestions_service =
295 SuggestionsServiceFactory::GetForProfile(profile_);
296 DCHECK(suggestions_service);
297 suggestions_service->BlacklistURL(
298 GURL(url),
299 base::Bind(
300 &MostVisitedSites::OnSuggestionsProfileAvailable,
301 weak_ptr_factory_.GetWeakPtr(),
302 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_))),
303 base::Closure());
304 break;
309 void MostVisitedSites::RecordOpenedMostVisitedItem(JNIEnv* env,
310 jobject obj,
311 jint index) {
312 switch (mv_source_) {
313 case TOP_SITES: {
314 UMA_HISTOGRAM_SPARSE_SLOWLY(kOpenedItemClientHistogramName, index);
315 break;
317 case SUGGESTIONS_SERVICE: {
318 if (server_suggestions_.suggestions_size() > index) {
319 if (server_suggestions_.suggestions(index).providers_size()) {
320 std::string histogram = base::StringPrintf(
321 kOpenedItemServerProviderHistogramFormat,
322 server_suggestions_.suggestions(index).providers(0));
323 LogHistogramEvent(histogram, index, num_sites_);
324 } else {
325 UMA_HISTOGRAM_SPARSE_SLOWLY(kOpenedItemServerHistogramName, index);
328 break;
333 void MostVisitedSites::OnStateChanged() {
334 // There have been changes to the sync state. This class cares about a few
335 // (just initialized, enabled/disabled or history sync state changed). Re-run
336 // the query code which will use the proper state.
337 QueryMostVisitedURLs();
340 // static
341 bool MostVisitedSites::Register(JNIEnv* env) {
342 return RegisterNativesImpl(env);
345 void MostVisitedSites::QueryMostVisitedURLs() {
346 SuggestionsService* suggestions_service =
347 SuggestionsServiceFactory::GetForProfile(profile_);
348 if (suggestions_service) {
349 // Suggestions service is enabled, initiate a query.
350 suggestions_service->FetchSuggestionsData(
351 GetSyncState(profile_),
352 base::Bind(
353 &MostVisitedSites::OnSuggestionsProfileAvailable,
354 weak_ptr_factory_.GetWeakPtr(),
355 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_))));
356 } else {
357 InitiateTopSitesQuery();
361 void MostVisitedSites::InitiateTopSitesQuery() {
362 scoped_refptr<TopSites> top_sites = TopSitesFactory::GetForProfile(profile_);
363 if (!top_sites)
364 return;
366 top_sites->GetMostVisitedURLs(
367 base::Bind(
368 &MostVisitedSites::OnMostVisitedURLsAvailable,
369 weak_ptr_factory_.GetWeakPtr(),
370 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_)),
371 num_sites_),
372 false);
375 void MostVisitedSites::OnMostVisitedURLsAvailable(
376 ScopedJavaGlobalRef<jobject>* j_observer,
377 int num_sites,
378 const history::MostVisitedURLList& visited_list) {
379 std::vector<base::string16> titles;
380 std::vector<std::string> urls;
381 ExtractMostVisitedTitlesAndURLs(visited_list, &titles, &urls, num_sites);
383 mv_source_ = TOP_SITES;
385 // Only log impression metrics on the initial load of the NTP.
386 if (!initial_load_done_) {
387 int num_tiles = urls.size();
388 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumTilesHistogramName, num_tiles);
389 for (int i = 0; i < num_tiles; ++i) {
390 UMA_HISTOGRAM_SPARSE_SLOWLY(kImpressionClientHistogramName, i);
393 initial_load_done_ = true;
395 JNIEnv* env = AttachCurrentThread();
396 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
397 env,
398 j_observer->obj(),
399 ToJavaArrayOfStrings(env, titles).obj(),
400 ToJavaArrayOfStrings(env, urls).obj());
403 void MostVisitedSites::OnSuggestionsProfileAvailable(
404 ScopedJavaGlobalRef<jobject>* j_observer,
405 const SuggestionsProfile& suggestions_profile) {
406 int size = suggestions_profile.suggestions_size();
407 // With no server suggestions, fall back to local Most Visited.
408 if (!size) {
409 InitiateTopSitesQuery();
410 return;
413 std::vector<base::string16> titles;
414 std::vector<std::string> urls;
415 int i = 0;
416 for (; i < size && i < num_sites_; ++i) {
417 const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
418 titles.push_back(base::UTF8ToUTF16(suggestion.title()));
419 urls.push_back(suggestion.url());
420 // Only log impression metrics on the initial NTP load.
421 if (!initial_load_done_) {
422 if (suggestion.providers_size()) {
423 std::string histogram = base::StringPrintf(
424 kImpressionServerHistogramFormat, suggestion.providers(0));
425 LogHistogramEvent(histogram, i, num_sites_);
426 } else {
427 UMA_HISTOGRAM_SPARSE_SLOWLY(kImpressionServerHistogramName, i);
431 if (!initial_load_done_) {
432 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumTilesHistogramName, i);
434 initial_load_done_ = true;
436 mv_source_ = SUGGESTIONS_SERVICE;
437 // Keep a copy of the suggestions for eventual logging.
438 server_suggestions_ = suggestions_profile;
440 JNIEnv* env = AttachCurrentThread();
441 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
442 env,
443 j_observer->obj(),
444 ToJavaArrayOfStrings(env, titles).obj(),
445 ToJavaArrayOfStrings(env, urls).obj());
448 void MostVisitedSites::OnObtainedThumbnail(
449 ScopedJavaGlobalRef<jobject>* bitmap,
450 ScopedJavaGlobalRef<jobject>* j_callback) {
451 DCHECK_CURRENTLY_ON(BrowserThread::UI);
452 JNIEnv* env = AttachCurrentThread();
453 if (bitmap->obj()) {
454 num_local_thumbs_++;
455 } else {
456 num_empty_thumbs_++;
458 Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
459 env, j_callback->obj(), bitmap->obj());
462 void MostVisitedSites::GetSuggestionsThumbnailOnUIThread(
463 SuggestionsService* suggestions_service,
464 const std::string& url_string,
465 ScopedJavaGlobalRef<jobject>* j_callback) {
466 suggestions_service->GetPageThumbnail(
467 GURL(url_string),
468 base::Bind(&MostVisitedSites::OnSuggestionsThumbnailAvailable,
469 weak_ptr_factory_.GetWeakPtr(),
470 base::Owned(new ScopedJavaGlobalRef<jobject>(*j_callback))));
473 void MostVisitedSites::OnSuggestionsThumbnailAvailable(
474 ScopedJavaGlobalRef<jobject>* j_callback,
475 const GURL& url,
476 const SkBitmap* bitmap) {
477 DCHECK_CURRENTLY_ON(BrowserThread::UI);
478 JNIEnv* env = AttachCurrentThread();
480 ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
481 new ScopedJavaGlobalRef<jobject>();
482 if (bitmap) {
483 num_server_thumbs_++;
484 j_bitmap_ref->Reset(
485 env,
486 gfx::ConvertToJavaBitmap(bitmap).obj());
487 } else {
488 num_empty_thumbs_++;
491 Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
492 env, j_callback->obj(), j_bitmap_ref->obj());
495 void MostVisitedSites::RecordUMAMetrics() {
496 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumLocalThumbnailTilesHistogramName,
497 num_local_thumbs_);
498 num_local_thumbs_ = 0;
499 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumEmptyTilesHistogramName, num_empty_thumbs_);
500 num_empty_thumbs_ = 0;
501 UMA_HISTOGRAM_SPARSE_SLOWLY(kNumServerTilesHistogramName, num_server_thumbs_);
502 num_server_thumbs_ = 0;
505 void MostVisitedSites::TopSitesLoaded(history::TopSites* top_sites) {
508 void MostVisitedSites::TopSitesChanged(history::TopSites* top_sites) {
509 if (mv_source_ == TOP_SITES) {
510 // The displayed suggestions are invalidated.
511 QueryMostVisitedURLs();
515 static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
516 MostVisitedSites* most_visited_sites =
517 new MostVisitedSites(ProfileAndroid::FromProfileAndroid(jprofile));
518 return reinterpret_cast<intptr_t>(most_visited_sites);