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 "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 "chrome/browser/chrome_notification_types.h" 12 #include "chrome/browser/history/history_types.h" 13 #include "chrome/browser/history/top_sites.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/profiles/profile_android.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/notification_source.h" 18 #include "jni/MostVisitedSites_jni.h" 19 #include "third_party/skia/include/core/SkBitmap.h" 20 #include "ui/gfx/android/java_bitmap.h" 21 #include "ui/gfx/codec/jpeg_codec.h" 22 23 using base::android::AttachCurrentThread; 24 using base::android::ConvertUTF8ToJavaString; 25 using base::android::ConvertJavaStringToUTF8; 26 using base::android::ScopedJavaGlobalRef; 27 using base::android::ToJavaArrayOfStrings; 28 using base::android::CheckException; 29 using content::BrowserThread; 30 using history::TopSites; 31 32 namespace { 33 34 void ExtractMostVisitedTitlesAndURLs( 35 const history::MostVisitedURLList& visited_list, 36 std::vector<base::string16>* titles, 37 std::vector<std::string>* urls, 38 int num_sites) { 39 size_t max = static_cast<size_t>(num_sites); 40 for (size_t i = 0; i < visited_list.size() && i < max; ++i) { 41 const history::MostVisitedURL& visited = visited_list[i]; 42 43 if (visited.url.is_empty()) 44 break; // This is the signal that there are no more real visited sites. 45 46 titles->push_back(visited.title); 47 urls->push_back(visited.url.spec()); 48 } 49 } 50 51 void OnMostVisitedURLsAvailable( 52 ScopedJavaGlobalRef<jobject>* j_observer, 53 int num_sites, 54 const history::MostVisitedURLList& visited_list) { 55 std::vector<base::string16> titles; 56 std::vector<std::string> urls; 57 ExtractMostVisitedTitlesAndURLs(visited_list, &titles, &urls, num_sites); 58 59 JNIEnv* env = AttachCurrentThread(); 60 Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable( 61 env, 62 j_observer->obj(), 63 ToJavaArrayOfStrings(env, titles).obj(), 64 ToJavaArrayOfStrings(env, urls).obj()); 65 } 66 67 SkBitmap ExtractThumbnail(const base::RefCountedMemory& image_data) { 68 scoped_ptr<SkBitmap> image(gfx::JPEGCodec::Decode( 69 image_data.front(), 70 image_data.size())); 71 return image.get() ? *image : SkBitmap(); 72 } 73 74 void OnObtainedThumbnail( 75 ScopedJavaGlobalRef<jobject>* bitmap, 76 ScopedJavaGlobalRef<jobject>* j_callback) { 77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 78 JNIEnv* env = AttachCurrentThread(); 79 Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable( 80 env, j_callback->obj(), bitmap->obj()); 81 } 82 83 void GetUrlThumbnailTask( 84 std::string url_string, 85 scoped_refptr<TopSites> top_sites, 86 ScopedJavaGlobalRef<jobject>* j_callback) { 87 JNIEnv* env = AttachCurrentThread(); 88 89 ScopedJavaGlobalRef<jobject>* j_bitmap_ref = 90 new ScopedJavaGlobalRef<jobject>(); 91 92 GURL gurl(url_string); 93 94 scoped_refptr<base::RefCountedMemory> data; 95 if (top_sites->GetPageThumbnail(gurl, false, &data)) { 96 SkBitmap thumbnail_bitmap = ExtractThumbnail(*data.get()); 97 if (!thumbnail_bitmap.empty()) { 98 j_bitmap_ref->Reset( 99 env, 100 gfx::ConvertToJavaBitmap(&thumbnail_bitmap).obj()); 101 } 102 } 103 104 // Since j_callback is owned by this callback, when the callback falls out of 105 // scope it will be deleted. We need to pass ownership to the next callback. 106 ScopedJavaGlobalRef<jobject>* j_callback_pass = 107 new ScopedJavaGlobalRef<jobject>(*j_callback); 108 BrowserThread::PostTask( 109 BrowserThread::UI, FROM_HERE, 110 base::Bind( 111 &OnObtainedThumbnail, 112 base::Owned(j_bitmap_ref), base::Owned(j_callback_pass))); 113 } 114 115 } // namespace 116 117 MostVisitedSites::MostVisitedSites(Profile* profile) 118 : profile_(profile), num_sites_(0) { 119 } 120 121 MostVisitedSites::~MostVisitedSites() { 122 } 123 124 void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) { 125 delete this; 126 } 127 128 void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv* env, 129 jobject obj, 130 jobject j_observer, 131 jint num_sites) { 132 observer_.Reset(env, j_observer); 133 num_sites_ = num_sites; 134 135 QueryMostVisitedURLs(); 136 137 history::TopSites* top_sites = profile_->GetTopSites(); 138 if (top_sites) { 139 // TopSites updates itself after a delay. To ensure up-to-date results, 140 // force an update now. 141 top_sites->SyncWithHistory(); 142 143 // Register for notification when TopSites changes so that we can update 144 // ourself. 145 registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED, 146 content::Source<history::TopSites>(top_sites)); 147 } 148 } 149 150 // May be called from any thread 151 void MostVisitedSites::GetURLThumbnail(JNIEnv* env, 152 jobject obj, 153 jstring url, 154 jobject j_callback_obj) { 155 ScopedJavaGlobalRef<jobject>* j_callback = 156 new ScopedJavaGlobalRef<jobject>(); 157 j_callback->Reset(env, j_callback_obj); 158 159 std::string url_string = ConvertJavaStringToUTF8(env, url); 160 scoped_refptr<TopSites> top_sites(profile_->GetTopSites()); 161 BrowserThread::PostTask( 162 BrowserThread::DB, FROM_HERE, base::Bind( 163 &GetUrlThumbnailTask, 164 url_string, 165 top_sites, base::Owned(j_callback))); 166 } 167 168 void MostVisitedSites::BlacklistUrl(JNIEnv* env, 169 jobject obj, 170 jstring j_url) { 171 TopSites* top_sites = profile_->GetTopSites(); 172 if (!top_sites) 173 return; 174 175 std::string url_string = ConvertJavaStringToUTF8(env, j_url); 176 top_sites->AddBlacklistedURL(GURL(url_string)); 177 } 178 179 void MostVisitedSites::Observe(int type, 180 const content::NotificationSource& source, 181 const content::NotificationDetails& details) { 182 DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED); 183 184 // Most visited urls changed, query again. 185 QueryMostVisitedURLs(); 186 } 187 188 // static 189 bool MostVisitedSites::Register(JNIEnv* env) { 190 return RegisterNativesImpl(env); 191 } 192 193 void MostVisitedSites::QueryMostVisitedURLs() { 194 TopSites* top_sites = profile_->GetTopSites(); 195 if (!top_sites) 196 return; 197 198 top_sites->GetMostVisitedURLs( 199 base::Bind( 200 &OnMostVisitedURLsAvailable, 201 base::Owned(new ScopedJavaGlobalRef<jobject>(observer_)), 202 num_sites_), 203 false); 204 } 205 206 static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) { 207 MostVisitedSites* most_visited_sites = 208 new MostVisitedSites(ProfileAndroid::FromProfileAndroid(jprofile)); 209 return reinterpret_cast<intptr_t>(most_visited_sites); 210 } 211