diff --git a/android/java/apk_for_test.flags b/android/java/apk_for_test.flags index d158083e5c4..af910f45c76 100644 --- a/android/java/apk_for_test.flags +++ b/android/java/apk_for_test.flags @@ -718,6 +718,10 @@ public (...); } +-keep class org.chromium.chrome.browser.ui.favicon.FaviconUtils { + *** getIconDrawableWithFilter(...); +} + -keep class org.chromium.chrome.browser.document.ChromeLauncherActivity { public (...); } diff --git a/android/javatests/org/chromium/chrome/browser/BytecodeTest.java b/android/javatests/org/chromium/chrome/browser/BytecodeTest.java index d27cb43138f..9c972a73e30 100644 --- a/android/javatests/org/chromium/chrome/browser/BytecodeTest.java +++ b/android/javatests/org/chromium/chrome/browser/BytecodeTest.java @@ -158,6 +158,7 @@ import org.chromium.chrome.browser.ui.appmenu.AppMenuDelegate; import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate; import org.chromium.chrome.browser.ui.appmenu.BraveAppMenu; import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderCoordinator; +import org.chromium.chrome.browser.ui.favicon.FaviconHelper; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.chrome.browser.ui.native_page.NativePageHost; import org.chromium.chrome.browser.ui.system.StatusBarColorController; @@ -181,6 +182,7 @@ import org.chromium.components.browser_ui.util.motion.MotionEventInfo; import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController; import org.chromium.components.browser_ui.widget.RadioButtonWithDescription; import org.chromium.components.browser_ui.widget.RadioButtonWithEditText; +import org.chromium.components.browser_ui.widget.RoundedIconGenerator; import org.chromium.components.browser_ui.widget.displaystyle.UiConfig; import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableRecyclerViewAdapter; import org.chromium.components.browser_ui.widget.scrim.ScrimManager; @@ -362,6 +364,7 @@ public class BytecodeTest { Assert.assertTrue(classExists("org/chromium/components/cached_flags/CachedFeatureParam")); Assert.assertTrue(classExists("org/chromium/components/cached_flags/CachedFlagUtils")); Assert.assertTrue(classExists("org/chromium/chrome/browser/logo/LogoMediator")); + Assert.assertTrue(classExists("org/chromium/chrome/browser/ui/favicon/FaviconUtils")); Assert.assertTrue( classExists("org/chromium/chrome/browser/tracing/settings/DeveloperSettings")); Assert.assertTrue( @@ -795,6 +798,18 @@ public class BytecodeTest { "updateVisibility", MethodModifier.REGULAR, void.class)); + Assert.assertTrue( + methodExists( + "org/chromium/chrome/browser/ui/favicon/FaviconUtils", + "getIconDrawableWithFilter", + MethodModifier.STATIC, + Drawable.class, + Bitmap.class, + GURL.class, + RoundedIconGenerator.class, + FaviconHelper.DefaultFaviconHelper.class, + Context.class, + int.class)); Assert.assertTrue( methodExists( "org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator", diff --git a/browser/ui/android/favicon/BUILD.gn b/browser/ui/android/favicon/BUILD.gn new file mode 100644 index 00000000000..04592201ec9 --- /dev/null +++ b/browser/ui/android/favicon/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright (c) 2026 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +import("//build/config/android/rules.gni") + +android_library("java") { + sources = [ + "java/src/org/chromium/chrome/browser/ui/favicon/BraveFaviconUtils.java", + ] + + deps = [ + "//base:base_java", + "//build/android:build_java", + "//chrome/browser/ui/android/favicon:java", + "//components/browser_ui/widget/android:java", + "//url:gurl_java", + ] +} diff --git a/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/BraveFaviconUtils.java b/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/BraveFaviconUtils.java new file mode 100644 index 00000000000..5c0d146e359 --- /dev/null +++ b/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/BraveFaviconUtils.java @@ -0,0 +1,41 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package org.chromium.chrome.browser.ui.favicon; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; + +import org.chromium.build.annotations.NullUnmarked; +import org.chromium.build.annotations.Nullable; +import org.chromium.components.browser_ui.widget.RoundedIconGenerator; +import org.chromium.url.GURL; + +/** Brave's fix for FaviconUtils to handle null bitmap from icon generator. */ +public class BraveFaviconUtils { + /** + * Wrapper for {@link FaviconUtils#getIconDrawableWithFilter} that catches NullPointerException + * when RoundedIconGenerator.generateIconForUrl returns null. + */ + @NullUnmarked + public static Drawable getIconDrawableWithFilter( + @Nullable Bitmap icon, + @Nullable GURL url, + RoundedIconGenerator iconGenerator, + FaviconHelper.DefaultFaviconHelper defaultFaviconHelper, + Context context, + int iconSize) { + try { + return FaviconUtils.getIconDrawableWithFilter( + icon, url, iconGenerator, defaultFaviconHelper, context, iconSize); + } catch (NullPointerException e) { + // Fix for crash when generateIconForUrl returns null. + // Check https://github.com/brave/brave-browser/issues/52458 for more details. + // Fall back to default favicon. + return defaultFaviconHelper.getDefaultFaviconDrawable(context, url, true); + } + } +} diff --git a/build/android/bytecode/BUILD.gn b/build/android/bytecode/BUILD.gn index 6cd7bcd9252..f960f4cc0b5 100644 --- a/build/android/bytecode/BUILD.gn +++ b/build/android/bytecode/BUILD.gn @@ -56,6 +56,7 @@ java_binary("java_bytecode_rewriter") { "//brave/build/android/bytecode/java/org/brave/bytecode/BraveEditUrlSuggestionProcessorBaseClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveEditUrlSuggestionProcessorClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveExternalNavigationHandlerClassAdapter.java", + "//brave/build/android/bytecode/java/org/brave/bytecode/BraveFaviconUtilsClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveFeedSurfaceCoordinatorClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveFeedSurfaceMediatorClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveForegroundServiceUtilsClassAdapter.java", diff --git a/build/android/bytecode/bytecode_rewriter.gni b/build/android/bytecode/bytecode_rewriter.gni index b116a8061ae..db06dfe8b99 100644 --- a/build/android/bytecode/bytecode_rewriter.gni +++ b/build/android/bytecode/bytecode_rewriter.gni @@ -11,6 +11,7 @@ brave_bytecode_jars = [ "obj/brave/android/features/tab_ui/java.javac.jar", "obj/brave/android/java/org/chromium/chrome/browser/search_engines/java.javac.jar", "obj/brave/browser/notifications/java.javac.jar", + "obj/brave/browser/ui/android/favicon/java.javac.jar", "obj/brave/browser/ui/android/logo/java.javac.jar", "obj/brave/browser/ui/android/omnibox/java.javac.jar", "obj/brave/browser/ui/android/theme/java.javac.jar", diff --git a/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java b/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java index 4ae141cf22f..a538cc33926 100644 --- a/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java +++ b/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java @@ -60,6 +60,7 @@ public class BraveClassAdapter { chain = new BraveEditUrlSuggestionProcessorBaseClassAdapter(chain); chain = new BraveEditUrlSuggestionProcessorClassAdapter(chain); chain = new BraveExternalNavigationHandlerClassAdapter(chain); + chain = new BraveFaviconUtilsClassAdapter(chain); chain = new BraveFeedSurfaceCoordinatorClassAdapter(chain); chain = new BraveFeedSurfaceMediatorClassAdapter(chain); chain = new BraveForegroundServiceUtilsClassAdapter(chain); diff --git a/build/android/bytecode/java/org/brave/bytecode/BraveFaviconUtilsClassAdapter.java b/build/android/bytecode/java/org/brave/bytecode/BraveFaviconUtilsClassAdapter.java new file mode 100644 index 00000000000..8c308c038cf --- /dev/null +++ b/build/android/bytecode/java/org/brave/bytecode/BraveFaviconUtilsClassAdapter.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package org.brave.bytecode; + +import org.objectweb.asm.ClassVisitor; + +public class BraveFaviconUtilsClassAdapter extends BraveClassVisitor { + static String sFaviconUtilsClassName = "org/chromium/chrome/browser/ui/favicon/FaviconUtils"; + static String sBraveFaviconUtilsClassName = + "org/chromium/chrome/browser/ui/favicon/BraveFaviconUtils"; + + public BraveFaviconUtilsClassAdapter(ClassVisitor visitor) { + super(visitor); + + changeMethodOwner( + sFaviconUtilsClassName, "getIconDrawableWithFilter", sBraveFaviconUtilsClassName); + } +} diff --git a/build/android/config.gni b/build/android/config.gni index 01f70f97511..22e6c5bc65c 100644 --- a/build/android/config.gni +++ b/build/android/config.gni @@ -36,6 +36,7 @@ brave_chrome_java_deps = [ "//brave/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings:java", "//brave/browser/skus/android:java", "//brave/browser/tab_group_sync:java", + "//brave/browser/ui/android/favicon:java", "//brave/browser/ui/android/logo:java", "//brave/browser/ui/android/omnibox:java", "//brave/browser/ui/android/theme:java", diff --git a/test/BUILD.gn b/test/BUILD.gn index 0301a6e3a57..c7e6e22b55a 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -1466,6 +1466,7 @@ if (is_android) { "//chrome/browser/ui/android/desktop_windowing:java", "//chrome/browser/ui/android/edge_to_edge:java", "//chrome/browser/ui/android/extensions:java", + "//chrome/browser/ui/android/favicon:java", "//chrome/browser/ui/android/layouts:java", "//chrome/browser/ui/android/logo:java", "//chrome/browser/ui/android/multiwindow:java",