|  | // Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "chrome/browser/ui/views/chrome_typography_provider.h" | 
|  |  | 
|  | #include "chrome/browser/ui/views/chrome_typography.h" | 
|  | #include "ui/base/resource/resource_bundle.h" | 
|  | #include "ui/gfx/color_palette.h" | 
|  | #include "ui/gfx/color_utils.h" | 
|  | #include "ui/gfx/platform_font.h" | 
|  | #include "ui/native_theme/native_theme.h" | 
|  | #include "ui/views/style/typography.h" | 
|  | #include "ui/views/view.h" | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | #include "base/win/windows_version.h" | 
|  | #include "ui/gfx/platform_font_win.h" | 
|  | #include "ui/native_theme/native_theme_win.h" | 
|  | #endif | 
|  |  | 
|  | #if defined(OS_CHROMEOS) | 
|  | // gn check complains on Linux Ozone. | 
|  | #include "ash/public/cpp/ash_typography.h"  // nogncheck | 
|  | #endif | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | #if defined(OS_MACOSX) | 
|  | constexpr char kDefaultMonospacedTypeface[] = "Menlo"; | 
|  | #elif defined(OS_WIN) | 
|  | constexpr char kDefaultMonospacedTypeface[] = "Consolas"; | 
|  | #else | 
|  | constexpr char kDefaultMonospacedTypeface[] = "DejaVu Sans Mono"; | 
|  | #endif | 
|  | constexpr char kUnspecifiedTypeface[] = ""; | 
|  |  | 
|  | // If the default foreground color from the native theme isn't black, the rest | 
|  | // of the Harmony spec isn't going to work. Also skip Harmony if a Windows | 
|  | // High Contrast theme is enabled. One of the four standard High Contrast themes | 
|  | // in Windows 10 still has black text, but (since the user wants high contrast) | 
|  | // the grey text shades in Harmony should not be used. | 
|  | bool ShouldIgnoreHarmonySpec(const ui::NativeTheme& theme) { | 
|  | // Mac provides users limited ways to customize the UI, including dark and | 
|  | // high contrast modes; all these are addressed elsewhere, so there's no need | 
|  | // for Mac to try to detect non-Harmony cases as Windows and Linux need to, | 
|  | // and dark mode can interfere with the detection below. | 
|  | #if defined(OS_MACOSX) | 
|  | return false; | 
|  | #else | 
|  | if (theme.UsesHighContrastColors()) | 
|  | return true; | 
|  |  | 
|  | // TODO(pbos): Revisit this check. Both GG900 and black are considered | 
|  | // "default black" as the common theme uses GG900 as primary color. | 
|  | const SkColor test_color = | 
|  | theme.GetSystemColor(ui::NativeTheme::kColorId_LabelEnabledColor); | 
|  | const bool label_color_is_black = | 
|  | test_color == SK_ColorBLACK || test_color == gfx::kGoogleGrey900; | 
|  | return !label_color_is_black; | 
|  | #endif  // defined(OS_MACOSX) | 
|  | } | 
|  |  | 
|  | // Returns a color for a possibly inverted or high-contrast OS color theme. | 
|  | SkColor GetHarmonyTextColorForNonStandardNativeTheme( | 
|  | int context, | 
|  | int style, | 
|  | const ui::NativeTheme& theme) { | 
|  | // At the time of writing, very few UI surfaces need typography for a Chrome- | 
|  | // provided theme. Typically just incognito browser windows (when the native | 
|  | // theme is NativeThemeDarkAura). Instead, this method is consulted when the | 
|  | // actual OS theme is configured in a special way. So pick from a small number | 
|  | // of NativeTheme constants that are known to adapt properly to distinct | 
|  | // colors when configuring the OS to use a high-contrast theme. For example, | 
|  | // ::GetSysColor() on Windows has 8 text colors: BTNTEXT, CAPTIONTEXT, | 
|  | // GRAYTEXT, HIGHLIGHTTEXT, INACTIVECAPTIONTEXT, INFOTEXT (tool tips), | 
|  | // MENUTEXT, and WINDOWTEXT. There's also hyperlinks: COLOR_HOTLIGHT. | 
|  | // Diverging from these risks using a color that doesn't match user | 
|  | // expectations. | 
|  |  | 
|  | const bool inverted_scheme = color_utils::IsInvertedColorScheme(); | 
|  |  | 
|  | ui::NativeTheme::ColorId color_id = | 
|  | (context == views::style::CONTEXT_BUTTON || | 
|  | context == views::style::CONTEXT_BUTTON_MD) | 
|  | ? ui::NativeTheme::kColorId_ButtonEnabledColor | 
|  | : ui::NativeTheme::kColorId_TextfieldDefaultColor; | 
|  | switch (style) { | 
|  | case views::style::STYLE_DIALOG_BUTTON_DEFAULT: | 
|  | // This is just white in Harmony and, even in inverted themes, prominent | 
|  | // buttons have a dark background, so white will maximize contrast. | 
|  | return SK_ColorWHITE; | 
|  | case views::style::STYLE_DISABLED: | 
|  | color_id = ui::NativeTheme::kColorId_LabelDisabledColor; | 
|  | break; | 
|  | case views::style::STYLE_LINK: | 
|  | color_id = ui::NativeTheme::kColorId_LinkEnabled; | 
|  | break; | 
|  | case STYLE_RED: | 
|  | return inverted_scheme ? gfx::kGoogleRed300 : gfx::kGoogleRed600; | 
|  | case STYLE_GREEN: | 
|  | return inverted_scheme ? gfx::kGoogleGreen300 : gfx::kGoogleGreen600; | 
|  | } | 
|  | return theme.GetSystemColor(color_id); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | // static | 
|  | int ChromeTypographyProvider::GetPlatformFontHeight(int font_context) { | 
|  | const bool direct_write_enabled = | 
|  | gfx::PlatformFontWin::IsDirectWriteEnabled(); | 
|  | const bool windows_10 = base::win::GetVersion() >= base::win::VERSION_WIN10; | 
|  | switch (font_context) { | 
|  | case CONTEXT_HEADLINE: | 
|  | return windows_10 && direct_write_enabled ? 27 : 28; | 
|  | case views::style::CONTEXT_DIALOG_TITLE: | 
|  | return windows_10 || !direct_write_enabled ? 20 : 21; | 
|  | case CONTEXT_BODY_TEXT_LARGE: | 
|  | case views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT: | 
|  | return direct_write_enabled ? 18 : 17; | 
|  | case CONTEXT_BODY_TEXT_SMALL: | 
|  | return windows_10 && direct_write_enabled ? 16 : 15; | 
|  | } | 
|  | NOTREACHED(); | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | const gfx::FontList& ChromeTypographyProvider::GetFont(int context, | 
|  | int style) const { | 
|  | // "Target" font size constants. | 
|  | constexpr int kHeadlineSize = 20; | 
|  | constexpr int kTitleSize = 15; | 
|  | constexpr int kTouchableLabelSize = 14; | 
|  | constexpr int kBodyTextLargeSize = 13; | 
|  | constexpr int kDefaultSize = 12; | 
|  |  | 
|  | std::string typeface = kUnspecifiedTypeface; | 
|  | int size_delta = kDefaultSize - gfx::PlatformFont::kDefaultBaseFontSize; | 
|  | gfx::Font::Weight font_weight = gfx::Font::Weight::NORMAL; | 
|  |  | 
|  | #if defined(OS_CHROMEOS) | 
|  | ash::ApplyAshFontStyles(context, style, &size_delta, &font_weight); | 
|  | #endif | 
|  |  | 
|  | ApplyCommonFontStyles(context, style, &size_delta, &font_weight); | 
|  |  | 
|  | switch (context) { | 
|  | case views::style::CONTEXT_BUTTON_MD: | 
|  | font_weight = MediumWeightForUI(); | 
|  | break; | 
|  | case views::style::CONTEXT_DIALOG_TITLE: | 
|  | size_delta = kTitleSize - gfx::PlatformFont::kDefaultBaseFontSize; | 
|  | break; | 
|  | case views::style::CONTEXT_TOUCH_MENU: | 
|  | size_delta = | 
|  | kTouchableLabelSize - gfx::PlatformFont::kDefaultBaseFontSize; | 
|  | break; | 
|  | case CONTEXT_BODY_TEXT_LARGE: | 
|  | case views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT: | 
|  | size_delta = kBodyTextLargeSize - gfx::PlatformFont::kDefaultBaseFontSize; | 
|  | break; | 
|  | case CONTEXT_HEADLINE: | 
|  | size_delta = kHeadlineSize - gfx::PlatformFont::kDefaultBaseFontSize; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Use a bold style for emphasized text in body contexts, and ignore |style| | 
|  | // otherwise. | 
|  | if (style == STYLE_EMPHASIZED || style == STYLE_EMPHASIZED_SECONDARY) { | 
|  | switch (context) { | 
|  | case CONTEXT_BODY_TEXT_SMALL: | 
|  | case CONTEXT_BODY_TEXT_LARGE: | 
|  | case views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT: | 
|  | font_weight = gfx::Font::Weight::BOLD; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (style == STYLE_PRIMARY_MONOSPACED || | 
|  | style == STYLE_SECONDARY_MONOSPACED) { | 
|  | typeface = kDefaultMonospacedTypeface; | 
|  | } | 
|  |  | 
|  | return ui::ResourceBundle::GetSharedInstance() | 
|  | .GetFontListWithTypefaceAndDelta(typeface, size_delta, gfx::Font::NORMAL, | 
|  | font_weight); | 
|  | } | 
|  |  | 
|  | SkColor ChromeTypographyProvider::GetColor(const views::View& view, | 
|  | int context, | 
|  | int style) const { | 
|  | // TODO(lgrey): Remove anything that could be using native theme | 
|  | // colors from here after UX review of divergences. | 
|  | const ui::NativeTheme* native_theme = view.GetNativeTheme(); | 
|  | DCHECK(native_theme); | 
|  | if (ShouldIgnoreHarmonySpec(*native_theme)) { | 
|  | return GetHarmonyTextColorForNonStandardNativeTheme(context, style, | 
|  | *native_theme); | 
|  | } | 
|  |  | 
|  | if (context == views::style::CONTEXT_BUTTON_MD) { | 
|  | switch (style) { | 
|  | case views::style::STYLE_DIALOG_BUTTON_DEFAULT: | 
|  | return native_theme->SystemDarkModeEnabled() ? gfx::kGoogleGrey900 | 
|  | : SK_ColorWHITE; | 
|  | case views::style::STYLE_DISABLED: | 
|  | return gfx::kGoogleGrey600; | 
|  | default: | 
|  | return native_theme->SystemDarkModeEnabled() ? gfx::kGoogleBlue300 | 
|  | : gfx::kGoogleBlue600; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Use the secondary style instead of primary for message box body text. | 
|  | if (context == views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT) { | 
|  | if (style == views::style::STYLE_PRIMARY) { | 
|  | style = STYLE_SECONDARY; | 
|  | } else if (style == STYLE_PRIMARY_MONOSPACED) { | 
|  | style = STYLE_SECONDARY_MONOSPACED; | 
|  | } | 
|  | } | 
|  |  | 
|  | switch (style) { | 
|  | case views::style::STYLE_DIALOG_BUTTON_DEFAULT: | 
|  | return SK_ColorWHITE; | 
|  | case views::style::STYLE_DISABLED: | 
|  | return native_theme->SystemDarkModeEnabled() | 
|  | ? gfx::kGoogleGrey800 | 
|  | : SkColorSetRGB(0x9e, 0x9e, 0x9e); | 
|  | case views::style::STYLE_LINK: | 
|  | return gfx::kGoogleBlue700; | 
|  | case STYLE_SECONDARY: | 
|  | case STYLE_SECONDARY_MONOSPACED: | 
|  | case STYLE_EMPHASIZED_SECONDARY: | 
|  | case STYLE_HINT: | 
|  | return native_theme->SystemDarkModeEnabled() | 
|  | ? SkColorSetA(SK_ColorWHITE, 0x99) | 
|  | : gfx::kGoogleGrey700; | 
|  | case STYLE_RED: | 
|  | return gfx::kGoogleRed700; | 
|  | case STYLE_GREEN: | 
|  | return gfx::kGoogleGreen700; | 
|  | } | 
|  |  | 
|  | // Use default primary color for everything else. | 
|  | return native_theme->SystemDarkModeEnabled() | 
|  | ? SkColorSetA(SK_ColorWHITE, 0xDD) | 
|  | : gfx::kGoogleGrey900; | 
|  | } | 
|  |  | 
|  | int ChromeTypographyProvider::GetLineHeight(int context, int style) const { | 
|  | // "Target" line height constants from the Harmony spec. A default OS | 
|  | // configuration should use these heights. However, if the user overrides OS | 
|  | // defaults, then GetLineHeight() should return the height that would add the | 
|  | // same extra space between lines as the default configuration would have. | 
|  | constexpr int kHeadlineHeight = 32; | 
|  | constexpr int kTitleHeight = 22; | 
|  | constexpr int kBodyHeight = 20;  // For both large and small. | 
|  |  | 
|  | // Button text should always use the minimum line height for a font to avoid | 
|  | // unnecessarily influencing the height of a button. | 
|  | constexpr int kButtonAbsoluteHeight = 0; | 
|  |  | 
|  | // The platform-specific heights (i.e. gfx::Font::GetHeight()) that result when | 
|  | // asking for the target size constants in ChromeTypographyProvider::GetFont() | 
|  | // in a default OS configuration. | 
|  | #if defined(OS_MACOSX) | 
|  | constexpr int kHeadlinePlatformHeight = 25; | 
|  | constexpr int kTitlePlatformHeight = 19; | 
|  | constexpr int kBodyTextLargePlatformHeight = 16; | 
|  | constexpr int kBodyTextSmallPlatformHeight = 15; | 
|  | #elif defined(OS_WIN) | 
|  | static const int kHeadlinePlatformHeight = | 
|  | GetPlatformFontHeight(CONTEXT_HEADLINE); | 
|  | static const int kTitlePlatformHeight = | 
|  | GetPlatformFontHeight(views::style::CONTEXT_DIALOG_TITLE); | 
|  | static const int kBodyTextLargePlatformHeight = | 
|  | GetPlatformFontHeight(CONTEXT_BODY_TEXT_LARGE); | 
|  | static const int kBodyTextSmallPlatformHeight = | 
|  | GetPlatformFontHeight(CONTEXT_BODY_TEXT_SMALL); | 
|  | #else | 
|  | constexpr int kHeadlinePlatformHeight = 24; | 
|  | constexpr int kTitlePlatformHeight = 18; | 
|  | constexpr int kBodyTextLargePlatformHeight = 17; | 
|  | constexpr int kBodyTextSmallPlatformHeight = 15; | 
|  | #endif | 
|  |  | 
|  | // The style of the system font used to determine line heights. | 
|  | constexpr int kTemplateStyle = views::style::STYLE_PRIMARY; | 
|  |  | 
|  | // TODO(tapted): These statics should be cleared out when something invokes | 
|  | // ui::ResourceBundle::ReloadFonts(). Currently that only happens on ChromeOS. | 
|  | // See http://crbug.com/708943. | 
|  | static const int headline_height = | 
|  | GetFont(CONTEXT_HEADLINE, kTemplateStyle).GetHeight() - | 
|  | kHeadlinePlatformHeight + kHeadlineHeight; | 
|  | static const int title_height = | 
|  | GetFont(views::style::CONTEXT_DIALOG_TITLE, kTemplateStyle).GetHeight() - | 
|  | kTitlePlatformHeight + kTitleHeight; | 
|  | static const int body_large_height = | 
|  | GetFont(CONTEXT_BODY_TEXT_LARGE, kTemplateStyle).GetHeight() - | 
|  | kBodyTextLargePlatformHeight + kBodyHeight; | 
|  | static const int default_height = | 
|  | GetFont(CONTEXT_BODY_TEXT_SMALL, kTemplateStyle).GetHeight() - | 
|  | kBodyTextSmallPlatformHeight + kBodyHeight; | 
|  |  | 
|  | switch (context) { | 
|  | case views::style::CONTEXT_BUTTON: | 
|  | case views::style::CONTEXT_BUTTON_MD: | 
|  | case CONTEXT_TOOLBAR_BUTTON: | 
|  | return kButtonAbsoluteHeight; | 
|  | case views::style::CONTEXT_DIALOG_TITLE: | 
|  | return title_height; | 
|  | case CONTEXT_BODY_TEXT_LARGE: | 
|  | case views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT: | 
|  | case views::style::CONTEXT_TABLE_ROW: | 
|  | return body_large_height; | 
|  | case CONTEXT_HEADLINE: | 
|  | return headline_height; | 
|  | default: | 
|  | return default_height; | 
|  | } | 
|  | } |