blob: d03d83147480ae6bdff833e410b5197eed26211a [file] [log] [blame]
// Copyright 2012 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/favicon/favicon_utils.h"
#include "base/hash/sha1.h"
#include "build/build_config.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/monogram_utils.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/platform_locale_settings.h"
#include "components/favicon/content/content_favicon_driver.h"
#include "components/favicon/core/fallback_url_util.h"
#include "components/favicon/core/favicon_service.h"
#include "components/password_manager/content/common/web_ui_constants.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_analysis.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/favicon_size.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/native_theme/native_theme.h"
#include "ui/resources/grit/ui_resources.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
namespace favicon {
namespace {
// The color of the letter drawn for a fallback icon. Changing this may require
// changing the algorithm in ReturnRenderedIconForRequest() that guarantees
// contrast.
constexpr SkColor kFallbackIconLetterColor = SK_ColorWHITE;
// Desaturate favicon HSL shift values.
const double kDesaturateHue = -1.0;
const double kDesaturateSaturation = 0.0;
const double kDesaturateLightness = 0.6;
// Returns a color based on the hash of |icon_url|'s origin.
SkColor ComputeBackgroundColorForUrl(const GURL& icon_url) {
if (!icon_url.is_valid())
return SK_ColorGRAY;
unsigned char hash[20];
const std::string origin = icon_url.DeprecatedGetOriginAsURL().spec();
base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(origin.c_str()),
origin.size(), hash);
return SkColorSetRGB(hash[0], hash[1], hash[2]);
}
// Gets the appropriate light or dark rasterized default favicon.
gfx::Image GetDefaultFaviconForColorScheme(bool is_dark) {
const int resource_id =
is_dark ? IDR_DEFAULT_FAVICON_DARK : IDR_DEFAULT_FAVICON;
return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
resource_id);
}
} // namespace
void CreateContentFaviconDriverForWebContents(
content::WebContents* web_contents) {
DCHECK(web_contents);
if (ContentFaviconDriver::FromWebContents(web_contents))
return;
Profile* original_profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext())
->GetOriginalProfile();
return ContentFaviconDriver::CreateForWebContents(
web_contents,
FaviconServiceFactory::GetForProfile(original_profile,
ServiceAccessType::IMPLICIT_ACCESS));
}
SkBitmap GenerateMonogramFavicon(GURL url, int icon_size, int circle_size) {
SkBitmap bitmap;
bitmap.allocN32Pixels(icon_size, icon_size, false);
cc::SkiaPaintCanvas paint_canvas(bitmap);
gfx::Canvas canvas(&paint_canvas, 1.f);
std::u16string monogram = favicon::GetFallbackIconText(url);
SkColor fallback_color =
color_utils::BlendForMinContrast(ComputeBackgroundColorForUrl(url),
kFallbackIconLetterColor)
.color;
monogram::DrawMonogramInCanvas(&canvas, icon_size, circle_size, monogram,
kFallbackIconLetterColor, fallback_color);
return bitmap;
}
gfx::Image TabFaviconFromWebContents(content::WebContents* contents) {
DCHECK(contents);
favicon::FaviconDriver* favicon_driver =
favicon::ContentFaviconDriver::FromWebContents(contents);
// TODO(crbug.com/40190724): Investigate why some WebContents do not have
// an attached ContentFaviconDriver.
if (!favicon_driver) {
return gfx::Image();
}
gfx::Image favicon = favicon_driver->GetFavicon();
// Desaturate the favicon if the navigation entry contains a network error.
if (!contents->ShouldShowLoadingUI()) {
content::NavigationController& controller = contents->GetController();
content::NavigationEntry* entry = controller.GetLastCommittedEntry();
if (entry && (entry->GetPageType() == content::PAGE_TYPE_ERROR)) {
color_utils::HSL shift = {kDesaturateHue, kDesaturateSaturation,
kDesaturateLightness};
return gfx::Image(gfx::ImageSkiaOperations::CreateHSLShiftedImage(
*favicon.ToImageSkia(), shift));
}
}
return favicon;
}
gfx::Image GetDefaultFavicon() {
bool is_dark = false;
#if !BUILDFLAG(IS_ANDROID)
// Android doesn't currently implement NativeTheme::GetInstanceForNativeUi.
const ui::NativeTheme* native_theme =
ui::NativeTheme::GetInstanceForNativeUi();
is_dark = native_theme && native_theme->ShouldUseDarkColors();
#endif
return GetDefaultFaviconForColorScheme(is_dark);
}
ui::ImageModel GetDefaultFaviconModel(ui::ColorId bg_color) {
return ui::ImageModel::FromImageGenerator(
base::BindRepeating(
[](ui::ColorId bg_color, const ui::ColorProvider* provider) {
return *GetDefaultFaviconForColorScheme(
color_utils::IsDark(provider->GetColor(bg_color)))
.ToImageSkia();
},
bg_color),
gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize));
}
void SaveFaviconEvenIfInIncognito(content::WebContents* contents) {
content::NavigationEntry* entry =
contents->GetController().GetLastCommittedEntry();
if (!entry)
return;
Profile* original_profile =
Profile::FromBrowserContext(contents->GetBrowserContext())
->GetOriginalProfile();
favicon::FaviconService* favicon_service =
FaviconServiceFactory::GetForProfile(original_profile,
ServiceAccessType::EXPLICIT_ACCESS);
if (!favicon_service)
return;
// Make sure the page is in history, otherwise adding the favicon does
// nothing.
GURL page_url = entry->GetURL();
favicon_service->AddPageNoVisitForBookmark(page_url, entry->GetTitle());
const content::FaviconStatus& favicon_status = entry->GetFavicon();
if (!favicon_status.valid || favicon_status.url.is_empty() ||
favicon_status.image.IsEmpty()) {
return;
}
favicon_service->SetFavicons({page_url}, favicon_status.url,
favicon_base::IconType::kFavicon,
favicon_status.image);
}
bool ShouldThemifyFavicon(GURL url) {
if (!url.SchemeIs(content::kChromeUIScheme)) {
return false;
}
return url.host_piece() != chrome::kChromeUIAppLauncherPageHost &&
url.host_piece() != chrome::kChromeUIHelpHost &&
url.host_piece() != chrome::kChromeUIVersionHost &&
url.host_piece() != chrome::kChromeUINetExportHost &&
url.host_piece() != chrome::kChromeUINewTabHost &&
url.host_piece() != password_manager::kChromeUIPasswordManagerHost;
}
bool ShouldThemifyFaviconForEntry(content::NavigationEntry* entry) {
const GURL& virtual_url = entry->GetVirtualURL();
const GURL& actual_url = entry->GetURL();
if (ShouldThemifyFavicon(virtual_url)) {
return true;
}
// Themify favicon for the default NTP and incognito NTP.
if (actual_url.SchemeIs(content::kChromeUIScheme)) {
return actual_url.host_piece() == chrome::kChromeUINewTabPageHost ||
actual_url.host_piece() == chrome::kChromeUINewTabHost;
}
return false;
}
gfx::ImageSkia ThemeFavicon(const gfx::ImageSkia& source,
SkColor alternate_color,
SkColor active_background,
SkColor inactive_background) {
// Choose between leaving the image as-is or masking with |alternate_color|.
const SkColor original_color =
color_utils::CalculateKMeanColorOfBitmap(*source.bitmap());
// Compute the minimum contrast of each color against active and inactive
// backgrounds.
const float original_contrast = std::min(
color_utils::GetContrastRatio(original_color, active_background),
color_utils::GetContrastRatio(original_color, inactive_background));
const float alternate_contrast = std::min(
color_utils::GetContrastRatio(alternate_color, active_background),
color_utils::GetContrastRatio(alternate_color, inactive_background));
// Recolor the image if the original has low minimum contrast and recoloring
// will improve it.
return ((original_contrast < color_utils::kMinimumVisibleContrastRatio) &&
(alternate_contrast > original_contrast))
? gfx::ImageSkiaOperations::CreateColorMask(source,
alternate_color)
: source;
}
gfx::ImageSkia ThemeMonochromeFavicon(const gfx::ImageSkia& source,
SkColor background) {
return (color_utils::GetContrastRatio(gfx::kGoogleGrey900, background) >
color_utils::GetContrastRatio(SK_ColorWHITE, background))
? gfx::ImageSkiaOperations::CreateColorMask(source,
gfx::kGoogleGrey900)
: gfx::ImageSkiaOperations::CreateColorMask(source, SK_ColorWHITE);
}
} // namespace favicon