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/favicon_helper.h"
11 #include "base/android/jni_android.h"
12 #include "base/android/jni_array.h"
13 #include "base/android/jni_string.h"
14 #include "base/android/scoped_java_ref.h"
15 #include "base/bind.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/favicon/favicon_service_factory.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/profiles/profile_android.h"
21 #include "chrome/browser/sync/profile_sync_service.h"
22 #include "chrome/browser/sync/profile_sync_service_factory.h"
23 #include "components/favicon/core/favicon_service.h"
24 #include "components/favicon_base/favicon_util.h"
25 #include "components/favicon_base/select_favicon_frames.h"
26 #include "components/sync_driver/open_tabs_ui_delegate.h"
27 #include "content/public/browser/web_contents.h"
28 #include "jni/FaviconHelper_jni.h"
29 #include "third_party/skia/include/core/SkBitmap.h"
30 #include "ui/gfx/android/java_bitmap.h"
31 #include "ui/gfx/codec/png_codec.h"
32 #include "ui/gfx/color_analysis.h"
33 #include "ui/gfx/color_utils.h"
34 #include "ui/gfx/favicon_size.h"
35 #include "ui/gfx/image/image_skia.h"
37 using base::android::ScopedJavaGlobalRef
;
38 using base::android::ScopedJavaLocalRef
;
39 using base::android::AttachCurrentThread
;
40 using base::android::ConvertJavaStringToUTF16
;
41 using base::android::ConvertJavaStringToUTF8
;
42 using base::android::ConvertUTF8ToJavaString
;
46 void OnLocalFaviconAvailable(
47 ScopedJavaGlobalRef
<jobject
>* j_favicon_image_callback
,
48 const favicon_base::FaviconRawBitmapResult
& result
) {
49 JNIEnv
* env
= AttachCurrentThread();
51 // Convert favicon_image_result to java objects.
52 ScopedJavaLocalRef
<jstring
> j_icon_url
=
53 ConvertUTF8ToJavaString(env
, result
.icon_url
.spec());
54 ScopedJavaLocalRef
<jobject
> j_favicon_bitmap
;
55 if (result
.is_valid()) {
56 SkBitmap favicon_bitmap
;
57 gfx::PNGCodec::Decode(result
.bitmap_data
->front(),
58 result
.bitmap_data
->size(),
60 if (!favicon_bitmap
.isNull())
61 j_favicon_bitmap
= gfx::ConvertToJavaBitmap(&favicon_bitmap
);
64 // Call java side OnLocalFaviconAvailable method.
65 Java_FaviconImageCallback_onFaviconAvailable(env
,
66 j_favicon_image_callback
->obj(),
67 j_favicon_bitmap
.obj(),
71 void OnFaviconRawBitmapResultAvailable(
72 ScopedJavaGlobalRef
<jobject
>* j_favicon_image_callback
,
73 const favicon_base::FaviconRawBitmapResult
& favicon_bitmap_result
) {
74 JNIEnv
* env
= AttachCurrentThread();
76 // Convert favicon_image_result to java objects.
77 ScopedJavaLocalRef
<jstring
> j_icon_url
=
78 ConvertUTF8ToJavaString(env
, favicon_bitmap_result
.icon_url
.spec());
80 SkBitmap favicon_bitmap
;
81 if (favicon_bitmap_result
.is_valid()) {
82 gfx::PNGCodec::Decode(favicon_bitmap_result
.bitmap_data
->front(),
83 favicon_bitmap_result
.bitmap_data
->size(),
86 ScopedJavaLocalRef
<jobject
> j_favicon_bitmap
;
87 if (!favicon_bitmap
.isNull())
88 j_favicon_bitmap
= gfx::ConvertToJavaBitmap(&favicon_bitmap
);
90 // Call java side OnLocalFaviconAvailable method.
91 Java_FaviconImageCallback_onFaviconAvailable(env
,
92 j_favicon_image_callback
->obj(),
93 j_favicon_bitmap
.obj(),
97 void OnFaviconDownloaded(
98 const ScopedJavaGlobalRef
<jobject
>& j_availability_callback
,
100 const GURL
& page_url
,
101 int download_request_id
,
102 int http_status_code
,
103 const GURL
& image_url
,
104 const std::vector
<SkBitmap
>& bitmaps
,
105 const std::vector
<gfx::Size
>& original_sizes
) {
106 bool success
= !bitmaps
.empty();
108 gfx::Image image
= gfx::Image(CreateFaviconImageSkia(bitmaps
,
112 favicon_base::SetFaviconColorSpace(&image
);
113 favicon::FaviconService
* service
= FaviconServiceFactory::GetForProfile(
114 profile
, ServiceAccessType::IMPLICIT_ACCESS
);
115 service
->SetFavicons(page_url
, image_url
, favicon_base::FAVICON
, image
);
118 JNIEnv
* env
= AttachCurrentThread();
119 Java_FaviconAvailabilityCallback_onFaviconAvailabilityChecked(
120 env
, j_availability_callback
.obj(), success
);
123 void OnFaviconImageResultAvailable(
124 const ScopedJavaGlobalRef
<jobject
>& j_availability_callback
,
126 content::WebContents
* web_contents
,
127 const GURL
& page_url
,
128 const GURL
& favicon_url
,
129 const favicon_base::FaviconImageResult
& result
) {
130 // If there already is a favicon, return immediately.
131 if (!result
.image
.IsEmpty()) {
132 JNIEnv
* env
= AttachCurrentThread();
133 Java_FaviconAvailabilityCallback_onFaviconAvailabilityChecked(
134 env
, j_availability_callback
.obj(), false);
138 web_contents
->DownloadImage(favicon_url
, true, 0, false,
139 base::Bind(OnFaviconDownloaded
,
140 j_availability_callback
,
146 static jlong
Init(JNIEnv
* env
, jclass clazz
) {
147 return reinterpret_cast<intptr_t>(new FaviconHelper());
150 FaviconHelper::FaviconHelper() {
151 cancelable_task_tracker_
.reset(new base::CancelableTaskTracker());
154 void FaviconHelper::Destroy(JNIEnv
* env
, jobject obj
) {
158 jboolean
FaviconHelper::GetLocalFaviconImageForURL(
164 jint j_desired_size_in_pixel
,
165 jobject j_favicon_image_callback
) {
166 Profile
* profile
= ProfileAndroid::FromProfileAndroid(j_profile
);
171 favicon::FaviconService
* favicon_service
=
172 FaviconServiceFactory::GetForProfile(profile
,
173 ServiceAccessType::EXPLICIT_ACCESS
);
174 DCHECK(favicon_service
);
175 if (!favicon_service
)
178 ScopedJavaGlobalRef
<jobject
>* j_scoped_favicon_callback
=
179 new ScopedJavaGlobalRef
<jobject
>();
180 j_scoped_favicon_callback
->Reset(env
, j_favicon_image_callback
);
182 favicon_base::FaviconRawBitmapCallback callback_runner
= base::Bind(
183 &OnLocalFaviconAvailable
, base::Owned(j_scoped_favicon_callback
));
185 favicon_service
->GetRawFaviconForPageURL(
186 GURL(ConvertJavaStringToUTF16(env
, j_page_url
)),
187 static_cast<int>(j_icon_types
),
188 static_cast<int>(j_desired_size_in_pixel
),
190 cancelable_task_tracker_
.get());
195 void FaviconHelper::GetLargestRawFaviconForUrl(
200 jintArray j_icon_types
,
201 jint j_min_size_threshold_px
,
202 jobject j_favicon_image_callback
) {
203 Profile
* profile
= ProfileAndroid::FromProfileAndroid(j_profile
);
208 favicon::FaviconService
* favicon_service
=
209 FaviconServiceFactory::GetForProfile(profile
,
210 ServiceAccessType::EXPLICIT_ACCESS
);
211 DCHECK(favicon_service
);
212 if (!favicon_service
)
215 std::vector
<int> icon_types
;
216 base::android::JavaIntArrayToIntVector(env
, j_icon_types
, &icon_types
);
218 ScopedJavaGlobalRef
<jobject
>* j_scoped_favicon_callback
=
219 new ScopedJavaGlobalRef
<jobject
>();
220 j_scoped_favicon_callback
->Reset(env
, j_favicon_image_callback
);
222 favicon_base::FaviconRawBitmapCallback callback_runner
=
223 base::Bind(&OnFaviconRawBitmapResultAvailable
,
224 base::Owned(j_scoped_favicon_callback
));
225 favicon_service
->GetLargestRawFaviconForPageURL(
226 GURL(ConvertJavaStringToUTF16(env
, j_page_url
)),
228 static_cast<int>(j_min_size_threshold_px
),
230 cancelable_task_tracker_
.get());
233 ScopedJavaLocalRef
<jobject
> FaviconHelper::GetSyncedFaviconImageForURL(
237 jstring j_page_url
) {
238 Profile
* profile
= ProfileAndroid::FromProfileAndroid(jprofile
);
241 std::string page_url
= ConvertJavaStringToUTF8(env
, j_page_url
);
243 ProfileSyncService
* sync_service
=
244 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile
);
245 DCHECK(sync_service
);
247 scoped_refptr
<base::RefCountedMemory
> favicon_png
;
248 sync_driver::OpenTabsUIDelegate
* open_tabs
=
249 sync_service
->GetOpenTabsUIDelegate();
252 if (!open_tabs
->GetSyncedFaviconForPageURL(page_url
, &favicon_png
))
253 return ScopedJavaLocalRef
<jobject
>();
255 // Convert favicon_image_result to java objects.
256 gfx::Image favicon_image
= gfx::Image::CreateFrom1xPNGBytes(favicon_png
);
257 SkBitmap favicon_bitmap
= favicon_image
.AsBitmap();
259 ScopedJavaLocalRef
<jobject
> j_favicon_bitmap
;
260 if (favicon_bitmap
.isNull())
261 return ScopedJavaLocalRef
<jobject
>();
263 return gfx::ConvertToJavaBitmap(&favicon_bitmap
);
266 void FaviconHelper::EnsureFaviconIsAvailable(
270 jobject j_web_contents
,
272 jstring j_favicon_url
,
273 jobject j_availability_callback
) {
274 Profile
* profile
= ProfileAndroid::FromProfileAndroid(j_profile
);
276 content::WebContents
* web_contents
=
277 content::WebContents::FromJavaWebContents(j_web_contents
);
278 DCHECK(web_contents
);
279 GURL
page_url(ConvertJavaStringToUTF8(env
, j_page_url
));
280 GURL
favicon_url(ConvertJavaStringToUTF8(env
, j_favicon_url
));
282 // TODO(treib): Optimize this by creating a FaviconService::HasFavicon method
283 // so that we don't have to actually get the image.
284 ScopedJavaGlobalRef
<jobject
> j_scoped_callback(env
, j_availability_callback
);
285 favicon_base::FaviconImageCallback callback_runner
=
286 base::Bind(&OnFaviconImageResultAvailable
, j_scoped_callback
,
287 profile
, web_contents
, page_url
, favicon_url
);
288 favicon::FaviconService
* service
= FaviconServiceFactory::GetForProfile(
289 profile
, ServiceAccessType::IMPLICIT_ACCESS
);
290 service
->GetFaviconImageForPageURL(page_url
, callback_runner
,
291 cancelable_task_tracker_
.get());
294 FaviconHelper::~FaviconHelper() {}
296 static jint
GetDominantColorForBitmap(JNIEnv
* env
,
302 gfx::JavaBitmap
bitmap_lock(bitmap
);
303 SkBitmap skbitmap
= gfx::CreateSkBitmapFromJavaBitmap(bitmap_lock
);
304 return color_utils::CalculateKMeanColorOfBitmap(skbitmap
);
308 bool FaviconHelper::RegisterFaviconHelper(JNIEnv
* env
) {
309 return RegisterNativesImpl(env
);