blob: 75c1376fb182228b09339a2f72844959833271cf [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/android/favicon_helper.h"
#include <jni.h>
#include <stddef.h>
#include <memory>
#include <vector>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/android/compose_bitmaps_helper.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/favicon/core/favicon_service.h"
#include "components/favicon/core/favicon_util.h"
#include "components/favicon/core/history_ui_favicon_request_handler.h"
#include "components/favicon_base/favicon_util.h"
#include "content/public/browser/web_contents.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "url/android/gurl_android.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/browser/ui/android/favicon/jni_headers/FaviconHelper_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF16;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;
static jlong JNI_FaviconHelper_Init(JNIEnv* env) {
return reinterpret_cast<intptr_t>(new FaviconHelper());
}
FaviconHelper::FaviconHelper() {
cancelable_task_tracker_ = std::make_unique<base::CancelableTaskTracker>();
}
void FaviconHelper::Destroy(JNIEnv* env) {
delete this;
}
jboolean FaviconHelper::GetLocalFaviconImageForURL(
JNIEnv* env,
Profile* profile,
GURL& page_url,
jint j_desired_size_in_pixel,
const JavaParamRef<jobject>& j_favicon_image_callback) {
DCHECK(profile);
if (!profile) {
return false;
}
favicon::FaviconService* favicon_service =
FaviconServiceFactory::GetForProfile(profile,
ServiceAccessType::EXPLICIT_ACCESS);
DCHECK(favicon_service);
if (!favicon_service) {
return false;
}
favicon_base::FaviconRawBitmapCallback callback_runner =
base::BindOnce(&FaviconHelper::OnFaviconBitmapResultAvailable,
weak_ptr_factory_.GetWeakPtr(),
ScopedJavaGlobalRef<jobject>(j_favicon_image_callback));
GetLocalFaviconImageForURLInternal(favicon_service, page_url,
static_cast<int>(j_desired_size_in_pixel),
std::move(callback_runner));
return true;
}
void FaviconHelper::GetLocalFaviconImageForURLInternal(
favicon::FaviconService* favicon_service,
GURL url,
int desired_size_in_pixel,
favicon_base::FaviconRawBitmapCallback callback_runner) {
DCHECK(favicon_service);
if (!favicon_service) {
return;
}
// |j_page_url| is an origin, and it may not have had a favicon associated
// with it. A trickier case is when |j_page_url| only has domain-scoped
// cookies, but visitors are redirected to HTTPS on visiting. Then
// |j_page_url| defaults to a HTTP scheme, but the favicon will be associated
// with the HTTPS URL and hence won't be found if we include the scheme in the
// lookup. Set |fallback_to_host|=true so the favicon database will fall back
// to matching only the hostname to have the best chance of finding a favicon.
const bool fallback_to_host = true;
favicon_service->GetRawFaviconForPageURL(
url,
{favicon_base::IconType::kFavicon, favicon_base::IconType::kTouchIcon,
favicon_base::IconType::kTouchPrecomposedIcon,
favicon_base::IconType::kWebManifestIcon},
desired_size_in_pixel, fallback_to_host, std::move(callback_runner),
cancelable_task_tracker_.get());
}
jboolean FaviconHelper::GetForeignFaviconImageForURL(
JNIEnv* env,
Profile* profile,
GURL& page_url,
jint j_desired_size_in_pixel,
const base::android::JavaParamRef<jobject>& j_favicon_image_callback) {
if (!profile) {
return false;
}
favicon::HistoryUiFaviconRequestHandler* history_ui_favicon_request_handler =
HistoryUiFaviconRequestHandlerFactory::GetForBrowserContext(profile);
// Can be null in tests.
if (!history_ui_favicon_request_handler) {
return false;
}
history_ui_favicon_request_handler->GetRawFaviconForPageURL(
page_url, static_cast<int>(j_desired_size_in_pixel),
/*fallback_to_host=*/true,
base::BindOnce(&FaviconHelper::OnFaviconBitmapResultAvailable,
weak_ptr_factory_.GetWeakPtr(),
ScopedJavaGlobalRef<jobject>(j_favicon_image_callback)));
return true;
}
FaviconHelper::~FaviconHelper() = default;
void FaviconHelper::OnFaviconBitmapResultAvailable(
const JavaRef<jobject>& j_favicon_image_callback,
const favicon_base::FaviconRawBitmapResult& result) {
JNIEnv* env = AttachCurrentThread();
// Convert favicon_image_result to java objects.
ScopedJavaLocalRef<jobject> j_favicon_bitmap;
if (result.is_valid()) {
SkBitmap favicon_bitmap = gfx::PNGCodec::Decode(*result.bitmap_data);
if (!favicon_bitmap.isNull()) {
j_favicon_bitmap = gfx::ConvertToJavaBitmap(favicon_bitmap);
}
}
// Call java side OnFaviconBitmapResultAvailable method.
Java_FaviconImageCallback_onFaviconAvailable(
env, j_favicon_image_callback, j_favicon_bitmap, result.icon_url);
}