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 "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"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/history/history_types.h"
14 #include "chrome/browser/history/top_sites.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/profiles/profile_android.h"
17 #include "chrome/browser/search/suggestions/proto/suggestions.pb.h"
18 #include "chrome/browser/search/suggestions/suggestions_service.h"
19 #include "chrome/browser/search/suggestions/suggestions_service_factory.h"
20 #include "chrome/browser/search/suggestions/suggestions_source.h"
21 #include "chrome/browser/thumbnails/thumbnail_list_source.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/notification_source.h"
24 #include "content/public/browser/url_data_source.h"
25 #include "jni/MostVisitedSites_jni.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "ui/gfx/android/java_bitmap.h"
28 #include "ui/gfx/codec/jpeg_codec.h"
30 using base::android::AttachCurrentThread;
31 using base::android::ConvertUTF8ToJavaString;
32 using base::android::ConvertJavaStringToUTF8;
33 using base::android::ScopedJavaGlobalRef;
34 using base::android::ToJavaArrayOfStrings;
35 using base::android::CheckException;
36 using content::BrowserThread;
37 using history::TopSites;
38 using suggestions::ChromeSuggestion;
39 using suggestions::SuggestionsProfile;
40 using suggestions::SuggestionsService;
41 using suggestions::SuggestionsServiceFactory;
45 void ExtractMostVisitedTitlesAndURLs(
46 const history::MostVisitedURLList& visited_list,
47 std::vector<base::string16>* titles,
48 std::vector<std::string>* urls,
50 size_t max = static_cast<size_t>(num_sites);
51 for (size_t i = 0; i < visited_list.size() && i < max; ++i) {
52 const history::MostVisitedURL& visited = visited_list[i];
54 if (visited.url.is_empty())
55 break; // This is the signal that there are no more real visited sites.
57 titles->push_back(visited.title);
58 urls->push_back(visited.url.spec());
62 void OnMostVisitedURLsAvailable(
63 ScopedJavaGlobalRef<jobject>* j_observer,
65 const history::MostVisitedURLList& visited_list) {
66 std::vector<base::string16> titles;
67 std::vector<std::string> urls;
68 ExtractMostVisitedTitlesAndURLs(visited_list, &titles, &urls, num_sites);
70 JNIEnv* env = AttachCurrentThread();
71 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
74 ToJavaArrayOfStrings(env, titles).obj(),
75 ToJavaArrayOfStrings(env, urls).obj());
78 SkBitmap ExtractThumbnail(const base::RefCountedMemory& image_data) {
79 scoped_ptr<SkBitmap> image(gfx::JPEGCodec::Decode(
82 return image.get() ? *image : SkBitmap();
85 void OnObtainedThumbnail(
86 ScopedJavaGlobalRef<jobject>* bitmap,
87 ScopedJavaGlobalRef<jobject>* j_callback) {
88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
89 JNIEnv* env = AttachCurrentThread();
90 Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
91 env, j_callback->obj(), bitmap->obj());
94 void GetUrlThumbnailTask(
95 std::string url_string,
96 scoped_refptr<TopSites> top_sites,
97 ScopedJavaGlobalRef<jobject>* j_callback) {
98 JNIEnv* env = AttachCurrentThread();
100 ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
101 new ScopedJavaGlobalRef<jobject>();
103 GURL gurl(url_string);
105 scoped_refptr<base::RefCountedMemory> data;
106 if (top_sites->GetPageThumbnail(gurl, false, &data)) {
107 SkBitmap thumbnail_bitmap = ExtractThumbnail(*data.get());
108 if (!thumbnail_bitmap.empty()) {
111 gfx::ConvertToJavaBitmap(&thumbnail_bitmap).obj());
115 // Since j_callback is owned by this callback, when the callback falls out of
116 // scope it will be deleted. We need to pass ownership to the next callback.
117 ScopedJavaGlobalRef<jobject>* j_callback_pass =
118 new ScopedJavaGlobalRef<jobject>(*j_callback);
119 BrowserThread::PostTask(
120 BrowserThread::UI, FROM_HERE,
122 &OnObtainedThumbnail,
123 base::Owned(j_bitmap_ref), base::Owned(j_callback_pass)));
128 MostVisitedSites::MostVisitedSites(Profile* profile)
129 : profile_(profile), num_sites_(0), weak_ptr_factory_(this) {
130 // Register the debugging page for the Suggestions Service and the thumbnails
132 content::URLDataSource::Add(profile_,
133 new suggestions::SuggestionsSource(profile_));
134 content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
137 MostVisitedSites::~MostVisitedSites() {
140 void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) {
144 void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv* env,
148 observer_.Reset(env, j_observer);
149 num_sites_ = num_sites;
151 QueryMostVisitedURLs();
153 history::TopSites* top_sites = profile_->GetTopSites();
155 // TopSites updates itself after a delay. To ensure up-to-date results,
156 // force an update now.
157 top_sites->SyncWithHistory();
159 // Register for notification when TopSites changes so that we can update
161 registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
162 content::Source<history::TopSites>(top_sites));
166 // May be called from any thread
167 void MostVisitedSites::GetURLThumbnail(JNIEnv* env,
170 jobject j_callback_obj) {
171 ScopedJavaGlobalRef<jobject>* j_callback =
172 new ScopedJavaGlobalRef<jobject>();
173 j_callback->Reset(env, j_callback_obj);
175 std::string url_string = ConvertJavaStringToUTF8(env, url);
176 scoped_refptr<TopSites> top_sites(profile_->GetTopSites());
177 BrowserThread::PostTask(
178 BrowserThread::DB, FROM_HERE, base::Bind(
179 &GetUrlThumbnailTask,
181 top_sites, base::Owned(j_callback)));
184 void MostVisitedSites::BlacklistUrl(JNIEnv* env,
187 TopSites* top_sites = profile_->GetTopSites();
191 std::string url_string = ConvertJavaStringToUTF8(env, j_url);
192 top_sites->AddBlacklistedURL(GURL(url_string));
195 void MostVisitedSites::Observe(int type,
196 const content::NotificationSource& source,
197 const content::NotificationDetails& details) {
198 DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED);
200 // Most visited urls changed, query again.
201 QueryMostVisitedURLs();
205 bool MostVisitedSites::Register(JNIEnv* env) {
206 return RegisterNativesImpl(env);
209 void MostVisitedSites::QueryMostVisitedURLs() {
210 SuggestionsServiceFactory* suggestions_service_factory =
211 SuggestionsServiceFactory::GetInstance();
212 SuggestionsService* suggestions_service =
213 suggestions_service_factory->GetForProfile(profile_);
214 if (suggestions_service) {
215 // Suggestions service is enabled, initiate a query.
216 suggestions_service->FetchSuggestionsData(
218 &MostVisitedSites::OnSuggestionsProfileAvailable,
219 weak_ptr_factory_.GetWeakPtr(),
220 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_))));
222 InitiateTopSitesQuery();
226 void MostVisitedSites::InitiateTopSitesQuery() {
227 TopSites* top_sites = profile_->GetTopSites();
231 top_sites->GetMostVisitedURLs(
233 &OnMostVisitedURLsAvailable,
234 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_)),
239 void MostVisitedSites::OnSuggestionsProfileAvailable(
240 ScopedJavaGlobalRef<jobject>* j_observer,
241 const SuggestionsProfile& suggestions_profile) {
242 size_t size = suggestions_profile.suggestions_size();
244 // No suggestions data available, initiate Top Sites query.
245 InitiateTopSitesQuery();
249 std::vector<base::string16> titles;
250 std::vector<std::string> urls;
251 for (size_t i = 0; i < size; ++i) {
252 const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
253 titles.push_back(base::UTF8ToUTF16(suggestion.title()));
254 urls.push_back(suggestion.url());
257 JNIEnv* env = AttachCurrentThread();
258 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
261 ToJavaArrayOfStrings(env, titles).obj(),
262 ToJavaArrayOfStrings(env, urls).obj());
265 static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
266 MostVisitedSites* most_visited_sites =
267 new MostVisitedSites(ProfileAndroid::FromProfileAndroid(jprofile));
268 return reinterpret_cast<intptr_t>(most_visited_sites);