Commit c79e032b authored by Leonard Grey's avatar Leonard Grey Committed by Commit Bot

Cache dark mode and high contrast status in NativeTheme

This gets called a *lot* when fetching colors while painting which assumes it's cheap.

In fact, it's responsible for about 25% of paint time on Windows and ~6% on Mac (per profiling in the field).

Bug: 950063
Change-Id: I57905fffeb51b884a0e94bac28e10b05caacd180
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1565063Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Reviewed-by: default avatarRobert Liao <robliao@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Commit-Queue: Leonard Grey <lgrey@chromium.org>
Cr-Commit-Position: refs/heads/master@{#654848}
parent 85abeb6d
......@@ -44,14 +44,32 @@ void NativeTheme::NotifyObservers() {
NativeTheme::NativeTheme()
: thumb_inactive_color_(0xeaeaea),
thumb_active_color_(0xf4f4f4),
track_color_(0xd3d3d3) {
}
track_color_(0xd3d3d3),
is_dark_mode_(IsForcedDarkMode()),
is_high_contrast_(IsForcedHighContrast()) {}
NativeTheme::~NativeTheme() {}
bool NativeTheme::SystemDarkModeEnabled() const {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceDarkMode);
return is_dark_mode_;
}
bool NativeTheme::UsesHighContrastColors() const {
return is_high_contrast_;
}
bool NativeTheme::IsForcedDarkMode() const {
static bool kIsForcedDarkMode =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceDarkMode);
return kIsForcedDarkMode;
}
bool NativeTheme::IsForcedHighContrast() const {
static bool kIsForcedHighContrast =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceHighContrast);
return kIsForcedHighContrast;
}
CaptionStyle NativeTheme::GetSystemCaptionStyle() const {
......
......@@ -415,7 +415,7 @@ class NATIVE_THEME_EXPORT NativeTheme {
// Returns whether this NativeTheme uses higher-contrast colors, controlled by
// system accessibility settings and the system theme.
virtual bool UsesHighContrastColors() const = 0;
virtual bool UsesHighContrastColors() const;
// Whether OS-level dark mode (as in macOS Mojave or Windows 10) is enabled.
virtual bool SystemDarkModeEnabled() const;
......@@ -427,6 +427,16 @@ class NATIVE_THEME_EXPORT NativeTheme {
NativeTheme();
virtual ~NativeTheme();
// Whether high contrast is forced via command-line flag.
bool IsForcedHighContrast() const;
// Whether dark mode is forced via command-line flag.
bool IsForcedDarkMode() const;
void set_dark_mode(bool is_dark_mode) { is_dark_mode_ = is_dark_mode; }
void set_high_contrast(bool is_high_contrast) {
is_high_contrast_ = is_high_contrast;
}
unsigned int thumb_inactive_color_;
unsigned int thumb_active_color_;
unsigned int track_color_;
......@@ -435,6 +445,9 @@ class NATIVE_THEME_EXPORT NativeTheme {
// Observers to notify when the native theme changes.
base::ObserverList<NativeThemeObserver>::Unchecked native_theme_observers_;
bool is_dark_mode_ = false;
bool is_high_contrast_ = false;
DISALLOW_COPY_AND_ASSIGN(NativeTheme);
};
......
......@@ -264,11 +264,6 @@ gfx::Rect NativeThemeBase::GetNinePatchAperture(Part part) const {
return gfx::Rect();
}
bool NativeThemeBase::UsesHighContrastColors() const {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceHighContrast);
}
NativeThemeBase::NativeThemeBase()
: scrollbar_width_(kDefaultScrollbarWidth),
scrollbar_button_length_(kDefaultScrollbarButtonLength) {
......
......@@ -36,7 +36,6 @@ class NATIVE_THEME_EXPORT NativeThemeBase : public NativeTheme {
bool SupportsNinePatch(Part part) const override;
gfx::Size GetNinePatchCanvasSize(Part part) const override;
gfx::Rect GetNinePatchAperture(Part part) const override;
bool UsesHighContrastColors() const override;
protected:
NativeThemeBase();
......
......@@ -56,8 +56,6 @@ class NATIVE_THEME_EXPORT NativeThemeMac : public NativeThemeBase {
State state,
const gfx::Rect& rect,
const MenuItemExtraParams& menu_item) const override;
bool UsesHighContrastColors() const override;
bool SystemDarkModeEnabled() const override;
// Paints the styled button shape used for default controls on Mac. The basic
// style is used for dialog buttons, comboboxes, and tabbed pane tabs.
......@@ -70,9 +68,6 @@ class NATIVE_THEME_EXPORT NativeThemeMac : public NativeThemeBase {
bool round_right,
bool focus);
// Updates cached dark mode status and notifies observers if it has changed.
void UpdateDarkModeStatus();
protected:
friend class NativeTheme;
friend class base::NoDestructor<NativeThemeMac>;
......@@ -91,8 +86,6 @@ class NATIVE_THEME_EXPORT NativeThemeMac : public NativeThemeBase {
appearance_observer_;
id high_contrast_notification_token_;
bool is_dark_mode_ = false;
DISALLOW_COPY_AND_ASSIGN(NativeThemeMac);
};
......
......@@ -9,6 +9,7 @@
#include "base/command_line.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_block.h"
#include "base/mac/sdk_forward_declarations.h"
#include "base/macros.h"
#import "skia/ext/skia_utils_mac.h"
......@@ -29,7 +30,15 @@ bool IsDarkMode() {
]];
return [appearance isEqual:NSAppearanceNameDarkAqua];
}
return false;
}
bool IsHighContrast() {
NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
if ([workspace respondsToSelector:@selector
(accessibilityDisplayShouldIncreaseContrast)]) {
return workspace.accessibilityDisplayShouldIncreaseContrast;
}
return false;
}
} // namespace
......@@ -45,13 +54,13 @@ bool IsDarkMode() {
@end
@implementation NativeThemeEffectiveAppearanceObserver {
ui::NativeThemeMac* owner_;
base::mac::ScopedBlock<void (^)()> handler_;
}
- (instancetype)initWithOwner:(ui::NativeThemeMac*)owner {
- (instancetype)initWithHandler:(void (^)())handler {
self = [super init];
if (self) {
owner_ = owner;
handler_.reset([handler copy]);
if (@available(macOS 10.14, *)) {
[NSApp addObserver:self
forKeyPath:@"effectiveAppearance"
......@@ -73,7 +82,7 @@ bool IsDarkMode() {
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {
owner_->UpdateDarkModeStatus();
handler_.get()();
}
@end
......@@ -267,42 +276,31 @@ void NativeThemeMac::PaintMenuItemBackground(
}
}
bool NativeThemeMac::UsesHighContrastColors() const {
if (NativeThemeBase::UsesHighContrastColors())
return true;
NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
if ([workspace respondsToSelector:@selector
(accessibilityDisplayShouldIncreaseContrast)]) {
return workspace.accessibilityDisplayShouldIncreaseContrast;
}
return false;
}
bool NativeThemeMac::SystemDarkModeEnabled() const {
if (@available(macOS 10.14, *)) {
return is_dark_mode_;
} else {
// Support "--force-dark-mode" in macOS < 10.14.
return NativeThemeBase::SystemDarkModeEnabled();
}
}
NativeThemeMac::NativeThemeMac() {
if (base::FeatureList::IsEnabled(features::kDarkMode)) {
is_dark_mode_ = IsDarkMode();
__block auto theme = this;
set_dark_mode(IsDarkMode());
appearance_observer_.reset(
[[NativeThemeEffectiveAppearanceObserver alloc] initWithOwner:this]);
[[NativeThemeEffectiveAppearanceObserver alloc] initWithHandler:^{
theme->set_dark_mode(IsDarkMode());
theme->NotifyObservers();
}]);
}
if (@available(macOS 10.10, *)) {
high_contrast_notification_token_ = [[[NSWorkspace sharedWorkspace]
notificationCenter]
addObserverForName:
NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification
object:nil
queue:nil
usingBlock:^(NSNotification* notification) {
ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers();
}];
if (!IsForcedHighContrast()) {
set_high_contrast(IsHighContrast());
__block auto theme = this;
high_contrast_notification_token_ =
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserverForName:
NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification
object:nil
queue:nil
usingBlock:^(NSNotification* notification) {
theme->set_high_contrast(IsHighContrast());
theme->NotifyObservers();
}];
}
}
}
......@@ -319,11 +317,4 @@ void NativeThemeMac::PaintSelectedMenuItem(cc::PaintCanvas* canvas,
canvas->drawRect(gfx::RectToSkRect(rect), flags);
}
void NativeThemeMac::UpdateDarkModeStatus() {
bool was_dark_mode = is_dark_mode_;
is_dark_mode_ = IsDarkMode();
if (was_dark_mode != is_dark_mode_)
NotifyObservers();
}
} // namespace ui
......@@ -238,9 +238,7 @@ NativeThemeWin::NativeThemeWin()
open_theme_(NULL),
close_theme_(NULL),
theme_dll_(LoadLibrary(L"uxtheme.dll")),
color_change_listener_(this),
is_using_high_contrast_(false),
is_using_high_contrast_valid_(false) {
color_change_listener_(this) {
if (theme_dll_) {
draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
GetProcAddress(theme_dll_, "DrawThemeBackground"));
......@@ -255,7 +253,9 @@ NativeThemeWin::NativeThemeWin()
close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
GetProcAddress(theme_dll_, "CloseThemeData"));
}
if (base::FeatureList::IsEnabled(features::kDarkMode)) {
if (!IsForcedDarkMode() && !IsForcedHighContrast() &&
base::FeatureList::IsEnabled(features::kDarkMode)) {
// Dark Mode currently targets UWP apps, which means Win32 apps need to use
// alternate, less reliable means of detecting the state. The following
// can break in future Windows versions.
......@@ -265,9 +265,13 @@ NativeThemeWin::NativeThemeWin()
L"Software\\Microsoft\\Windows\\CurrentVersion\\"
L"Themes\\Personalize",
KEY_READ | KEY_NOTIFY) == ERROR_SUCCESS;
if (key_open_succeeded)
if (key_open_succeeded) {
UpdateDarkModeStatus();
RegisterThemeRegkeyObserver();
}
}
if (!IsForcedHighContrast())
set_high_contrast(IsUsingHighContrastThemeInternal());
memset(theme_handles_, 0, sizeof(theme_handles_));
// Initialize the cached system colors.
......@@ -284,15 +288,10 @@ NativeThemeWin::~NativeThemeWin() {
}
bool NativeThemeWin::IsUsingHighContrastThemeInternal() const {
if (is_using_high_contrast_valid_)
return is_using_high_contrast_;
HIGHCONTRAST result;
result.cbSize = sizeof(HIGHCONTRAST);
is_using_high_contrast_ =
SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
(result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
is_using_high_contrast_valid_ = true;
return is_using_high_contrast_;
return SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
(result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
}
void NativeThemeWin::CloseHandlesInternal() {
......@@ -309,7 +308,8 @@ void NativeThemeWin::CloseHandlesInternal() {
void NativeThemeWin::OnSysColorChange() {
UpdateSystemColors();
is_using_high_contrast_valid_ = false;
if (!IsForcedHighContrast())
set_high_contrast(IsUsingHighContrastThemeInternal());
NotifyObservers();
}
......@@ -579,26 +579,13 @@ gfx::Rect NativeThemeWin::GetNinePatchAperture(Part part) const {
return gfx::Rect();
}
bool NativeThemeWin::UsesHighContrastColors() const {
bool force_enabled = base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceHighContrast);
return force_enabled || IsUsingHighContrastThemeInternal();
}
bool NativeThemeWin::SystemDarkModeEnabled() const {
// Windows high contrast modes are entirely different themes,
// so let them take priority over dark mode.
// ...unless --force-dark-mode was specified in which case caveat emptor.
if (UsesHighContrastColors() && !NativeTheme::SystemDarkModeEnabled())
if (UsesHighContrastColors() && !IsForcedDarkMode())
return false;
bool fDarkModeEnabled = false;
if (hkcu_themes_regkey_.Valid()) {
DWORD apps_use_light_theme = 1;
hkcu_themes_regkey_.ReadValueDW(L"AppsUseLightTheme",
&apps_use_light_theme);
fDarkModeEnabled = (apps_use_light_theme == 0);
}
return fDarkModeEnabled || NativeTheme::SystemDarkModeEnabled();
return NativeTheme::SystemDarkModeEnabled();
}
void NativeThemeWin::PaintIndirect(cc::PaintCanvas* destination_canvas,
......@@ -1933,6 +1920,7 @@ void NativeThemeWin::RegisterThemeRegkeyObserver() {
DCHECK(hkcu_themes_regkey_.Valid());
hkcu_themes_regkey_.StartWatching(base::BindOnce(
[](NativeThemeWin* native_theme) {
native_theme->UpdateDarkModeStatus();
native_theme->NotifyObservers();
// RegKey::StartWatching only provides one notification. Reregistration
// is required to get future notifications.
......@@ -1941,4 +1929,15 @@ void NativeThemeWin::RegisterThemeRegkeyObserver() {
base::Unretained(this)));
}
void NativeThemeWin::UpdateDarkModeStatus() {
bool fDarkModeEnabled = false;
if (hkcu_themes_regkey_.Valid()) {
DWORD apps_use_light_theme = 1;
hkcu_themes_regkey_.ReadValueDW(L"AppsUseLightTheme",
&apps_use_light_theme);
fDarkModeEnabled = (apps_use_light_theme == 0);
}
set_dark_mode(fDarkModeEnabled);
}
} // namespace ui
......@@ -80,7 +80,6 @@ class NATIVE_THEME_EXPORT NativeThemeWin : public NativeTheme,
bool SupportsNinePatch(Part part) const override;
gfx::Size GetNinePatchCanvasSize(Part part) const override;
gfx::Rect GetNinePatchAperture(Part part) const override;
bool UsesHighContrastColors() const override;
bool SystemDarkModeEnabled() const override;
protected:
......@@ -263,6 +262,7 @@ class NATIVE_THEME_EXPORT NativeThemeWin : public NativeTheme,
HANDLE GetThemeHandle(ThemeName theme_name) const;
void RegisterThemeRegkeyObserver();
void UpdateDarkModeStatus();
typedef HRESULT (WINAPI* DrawThemeBackgroundPtr)(HANDLE theme,
HDC hdc,
......@@ -328,12 +328,6 @@ class NATIVE_THEME_EXPORT NativeThemeWin : public NativeTheme,
gfx::ScopedSysColorChangeListener color_change_listener_;
mutable std::map<int, SkColor> system_colors_;
// Is a high contrast theme active?
mutable bool is_using_high_contrast_;
// Is |is_using_high_contrast_| valid?
mutable bool is_using_high_contrast_valid_;
DISALLOW_COPY_AND_ASSIGN(NativeThemeWin);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment