blob: 61b17ff539ce98d388b67046e6035be86b49f6d8 [file] [log] [blame]
// Copyright 2016 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/themes/theme_service_win.h"
#include "base/bind.h"
#include "base/win/windows_version.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/win/titlebar_config.h"
#include "chrome/grit/theme_resources.h"
#include "skia/ext/skia_utils_win.h"
#include "ui/base/win/shell.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/native_theme/native_theme.h"
namespace {
SkColor GetDefaultInactiveFrameColor() {
return base::win::GetVersion() < base::win::Version::WIN10
? SkColorSetRGB(0xEB, 0xEB, 0xEB)
: SK_ColorWHITE;
}
} // namespace
ThemeServiceWin::ThemeServiceWin() {
// This just checks for Windows 8+ instead of calling DwmColorsAllowed()
// because we want to monitor the frame color even when a custom frame is in
// use, so that it will be correct if at any time the user switches to the
// native frame.
if (base::win::GetVersion() >= base::win::Version::WIN8) {
dwm_key_.reset(new base::win::RegKey(
HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\DWM", KEY_READ));
if (dwm_key_->Valid())
OnDwmKeyUpdated();
else
dwm_key_.reset();
}
}
ThemeServiceWin::~ThemeServiceWin() {
}
bool ThemeServiceWin::ShouldUseNativeFrame() const {
const bool use_native_frame_if_enabled =
ShouldCustomDrawSystemTitlebar() || !HasCustomImage(IDR_THEME_FRAME);
return use_native_frame_if_enabled && ui::win::IsAeroGlassEnabled();
}
bool ThemeServiceWin::ShouldUseIncreasedContrastThemeSupplier(
ui::NativeTheme* native_theme) const {
// On Windows the platform provides the high contrast colors, so don't use the
// IncreasedContrastThemeSupplier.
return false;
}
SkColor ThemeServiceWin::GetDefaultColor(int id, bool incognito) const {
// In high contrast mode on Windows the platform provides the color. Try to
// get that color first.
SkColor color;
if (ui::NativeTheme::GetInstanceForNativeUi()->UsesHighContrastColors() &&
GetPlatformHighContrastColor(id, &color)) {
return color;
}
if (DwmColorsAllowed()) {
if (id == ThemeProperties::COLOR_ACCENT_BORDER)
return dwm_accent_border_color_;
// When we're custom-drawing the titlebar we want to use either the colors
// we calculated in OnDwmKeyUpdated() or the default colors. When we're not
// custom-drawing the titlebar we want to match the color Windows actually
// uses because some things (like the incognito icon) use this color to
// decide whether they should draw in light or dark mode. Incognito colors
// should be the same as non-incognito in all cases here.
if (id == ThemeProperties::COLOR_FRAME) {
if (dwm_frame_color_)
return dwm_frame_color_.value();
if (!ShouldCustomDrawSystemTitlebar())
return SK_ColorWHITE;
// Fall through and use default.
}
if (id == ThemeProperties::COLOR_FRAME_INACTIVE) {
if (!ShouldCustomDrawSystemTitlebar()) {
return inactive_frame_color_from_registry_
? dwm_inactive_frame_color_.value()
: GetDefaultInactiveFrameColor();
}
if (dwm_inactive_frame_color_)
return dwm_inactive_frame_color_.value();
// Fall through and use default.
}
}
return ThemeService::GetDefaultColor(id, incognito);
}
bool ThemeServiceWin::GetPlatformHighContrastColor(int id,
SkColor* color) const {
ui::NativeTheme::SystemThemeColor system_theme_color =
ui::NativeTheme::SystemThemeColor::kNotSupported;
switch (id) {
// Window Background
case ThemeProperties::COLOR_FRAME:
case ThemeProperties::COLOR_FRAME_INCOGNITO:
case ThemeProperties::COLOR_FRAME_INACTIVE:
case ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE:
case ThemeProperties::COLOR_BACKGROUND_TAB:
case ThemeProperties::COLOR_BACKGROUND_TAB_INCOGNITO:
case ThemeProperties::COLOR_BACKGROUND_TAB_INACTIVE:
case ThemeProperties::COLOR_BACKGROUND_TAB_INCOGNITO_INACTIVE:
case ThemeProperties::COLOR_DOWNLOAD_SHELF:
case ThemeProperties::COLOR_INFOBAR:
case ThemeProperties::COLOR_TOOLBAR:
case ThemeProperties::COLOR_STATUS_BUBBLE:
system_theme_color = ui::NativeTheme::SystemThemeColor::kWindow;
break;
// Window Text
case ThemeProperties::COLOR_TOOLBAR_VERTICAL_SEPARATOR:
case ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR:
case ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR_INACTIVE:
case ThemeProperties::COLOR_LOCATION_BAR_BORDER:
system_theme_color = ui::NativeTheme::SystemThemeColor::kWindowText;
break;
// Button Background
case ThemeProperties::COLOR_OMNIBOX_BACKGROUND:
case ThemeProperties::COLOR_OMNIBOX_BACKGROUND_HOVERED:
case ThemeProperties::COLOR_OMNIBOX_RESULTS_BG:
system_theme_color = ui::NativeTheme::SystemThemeColor::kButtonFace;
break;
// Button Text Foreground
case ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON:
case ThemeProperties::COLOR_BOOKMARK_TEXT:
case ThemeProperties::COLOR_BACKGROUND_TAB_TEXT:
case ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO:
case ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INACTIVE:
case ThemeProperties::COLOR_BACKGROUND_TAB_TEXT_INCOGNITO_INACTIVE:
case ThemeProperties::COLOR_OMNIBOX_TEXT:
case ThemeProperties::COLOR_OMNIBOX_SELECTED_KEYWORD:
case ThemeProperties::COLOR_OMNIBOX_BUBBLE_OUTLINE:
case ThemeProperties::COLOR_OMNIBOX_TEXT_DIMMED:
case ThemeProperties::COLOR_OMNIBOX_RESULTS_ICON:
case ThemeProperties::COLOR_OMNIBOX_RESULTS_URL:
case ThemeProperties::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED:
case ThemeProperties::COLOR_OMNIBOX_SECURITY_CHIP_DEFAULT:
case ThemeProperties::COLOR_OMNIBOX_SECURITY_CHIP_SECURE:
case ThemeProperties::COLOR_OMNIBOX_SECURITY_CHIP_DANGEROUS:
system_theme_color = ui::NativeTheme::SystemThemeColor::kButtonText;
break;
// Highlight/Selected Background
case ThemeProperties::COLOR_OMNIBOX_RESULTS_BG_SELECTED:
case ThemeProperties::COLOR_OMNIBOX_RESULTS_BG_HOVERED:
system_theme_color = ui::NativeTheme::SystemThemeColor::kHighlight;
break;
// Highlight/Selected Text Foreground
case ThemeProperties::COLOR_TAB_TEXT:
case ThemeProperties::COLOR_OMNIBOX_RESULTS_TEXT_SELECTED:
case ThemeProperties::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED_SELECTED:
case ThemeProperties::COLOR_OMNIBOX_RESULTS_ICON_SELECTED:
case ThemeProperties::COLOR_OMNIBOX_RESULTS_URL_SELECTED:
system_theme_color = ui::NativeTheme::SystemThemeColor::kHighlightText;
break;
// Gray/Disabled Text
case ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON_INACTIVE:
system_theme_color = ui::NativeTheme::SystemThemeColor::kGrayText;
break;
default:
return false;
}
*color = ui::NativeTheme::GetInstanceForNativeUi()
->GetSystemThemeColor(system_theme_color)
.value();
return true;
}
bool ThemeServiceWin::DwmColorsAllowed() const {
return ShouldUseNativeFrame() &&
(base::win::GetVersion() >= base::win::Version::WIN8);
}
void ThemeServiceWin::OnDwmKeyUpdated() {
dwm_accent_border_color_ = GetDefaultInactiveFrameColor();
DWORD colorization_color, colorization_color_balance;
if ((dwm_key_->ReadValueDW(L"ColorizationColor", &colorization_color) ==
ERROR_SUCCESS) &&
(dwm_key_->ReadValueDW(L"ColorizationColorBalance",
&colorization_color_balance) == ERROR_SUCCESS)) {
// The accent border color is a linear blend between the colorization
// color and the neutral #d9d9d9. colorization_color_balance is the
// percentage of the colorization color in that blend.
//
// On Windows version 1611 colorization_color_balance can be 0xfffffff3 if
// the accent color is taken from the background and either the background
// is a solid color or was just changed to a slideshow. It's unclear what
// that value's supposed to mean, so change it to 80 to match Edge's
// behavior.
if (colorization_color_balance > 100)
colorization_color_balance = 80;
// colorization_color's high byte is not an alpha value, so replace it
// with 0xff to make an opaque ARGB color.
SkColor input_color = SkColorSetA(colorization_color, 0xff);
dwm_accent_border_color_ =
color_utils::AlphaBlend(input_color, SkColorSetRGB(0xd9, 0xd9, 0xd9),
colorization_color_balance / 100.0f);
}
inactive_frame_color_from_registry_ = false;
if (base::win::GetVersion() < base::win::Version::WIN10) {
dwm_frame_color_ = dwm_accent_border_color_;
} else {
DWORD accent_color, color_prevalence;
bool use_dwm_frame_color =
dwm_key_->ReadValueDW(L"AccentColor", &accent_color) == ERROR_SUCCESS &&
dwm_key_->ReadValueDW(L"ColorPrevalence", &color_prevalence) ==
ERROR_SUCCESS &&
color_prevalence == 1;
if (use_dwm_frame_color) {
dwm_frame_color_ = skia::COLORREFToSkColor(accent_color);
DWORD accent_color_inactive;
if (dwm_key_->ReadValueDW(L"AccentColorInactive",
&accent_color_inactive) == ERROR_SUCCESS) {
dwm_inactive_frame_color_ =
skia::COLORREFToSkColor(accent_color_inactive);
inactive_frame_color_from_registry_ = true;
}
} else {
dwm_frame_color_.reset();
dwm_inactive_frame_color_.reset();
}
}
if (dwm_frame_color_ && !inactive_frame_color_from_registry_) {
// Tint to create inactive color. Always use the non-incognito version of
// the tint, since the frame should look the same in both modes.
dwm_inactive_frame_color_ = color_utils::HSLShift(
dwm_frame_color_.value(),
GetTint(ThemeProperties::TINT_FRAME_INACTIVE, false));
}
// Notify native theme observers that the native theme has changed.
ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers();
// Watch for future changes.
if (!dwm_key_->StartWatching(base::Bind(
&ThemeServiceWin::OnDwmKeyUpdated, base::Unretained(this))))
dwm_key_.reset();
}