diff --git a/app/brave_generated_resources.grd b/app/brave_generated_resources.grd index f4eb7c0ab3f2..96c78758aad3 100644 --- a/app/brave_generated_resources.grd +++ b/app/brave_generated_resources.grd @@ -353,6 +353,28 @@ By installing this extension, you are agreeing to the Google Widevine Terms of U Access Sync via + + + Light + + + Dark + + + + Same as Linux + + + + + Same as Windows + + + + + Same as MacOS + + Extensions diff --git a/browser/extensions/api/brave_theme_api.cc b/browser/extensions/api/brave_theme_api.cc index d7070f3587d7..62a99a41a4ee 100644 --- a/browser/extensions/api/brave_theme_api.cc +++ b/browser/extensions/api/brave_theme_api.cc @@ -1,62 +1,26 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ #include "brave/browser/extensions/api/brave_theme_api.h" +#include #include +#include "base/json/json_writer.h" #include "base/values.h" #include "brave/browser/themes/brave_theme_service.h" #include "brave/common/extensions/api/brave_theme.h" -#include "brave/common/pref_names.h" -#include "chrome/browser/profiles/profile.h" -#include "components/prefs/pref_service.h" - -using BTS = BraveThemeService; - -namespace { -void SetBraveThemeTypePref(Profile* profile, - BraveThemeType type) { - profile->GetPrefs()->SetInteger(kBraveThemeType, type); -} - -BraveThemeType GetBraveThemeTypeFromString( - base::StringPiece theme) { - if (theme == "Default") - return BraveThemeType::BRAVE_THEME_TYPE_DEFAULT; - - if (theme == "Light") - return BraveThemeType::BRAVE_THEME_TYPE_LIGHT; - - if (theme == "Dark") - return BraveThemeType::BRAVE_THEME_TYPE_DARK; - - NOTREACHED(); - return BraveThemeType::BRAVE_THEME_TYPE_DEFAULT; -} - -} // namespace namespace extensions { namespace api { -ExtensionFunction::ResponseAction BraveThemeSetBraveThemeTypeFunction::Run() { - std::unique_ptr params( - brave_theme::SetBraveThemeType::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - - Profile* profile = Profile::FromBrowserContext(browser_context()); - SetBraveThemeTypePref(profile, GetBraveThemeTypeFromString(params->type)); - - return RespondNow(NoArguments()); -} - -ExtensionFunction::ResponseAction BraveThemeGetBraveThemeTypeFunction::Run() { - Profile* profile = Profile::FromBrowserContext(browser_context()); - const std::string theme_type = BTS::GetStringFromBraveThemeType( - BTS::GetActiveBraveThemeType(profile)); - return RespondNow(OneArgument(std::make_unique(theme_type))); +ExtensionFunction::ResponseAction BraveThemeGetBraveThemeListFunction::Run() { + std::string json_string; + base::JSONWriter::Write(BraveThemeService::GetBraveThemeList(), + &json_string); + return RespondNow(OneArgument(std::make_unique(json_string))); } } // namespace api diff --git a/browser/extensions/api/brave_theme_api.h b/browser/extensions/api/brave_theme_api.h index b948a0bf1226..eb69150a0318 100644 --- a/browser/extensions/api/brave_theme_api.h +++ b/browser/extensions/api/brave_theme_api.h @@ -1,4 +1,5 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ @@ -10,26 +11,16 @@ namespace extensions { namespace api { -class BraveThemeSetBraveThemeTypeFunction : public UIThreadExtensionFunction { +class BraveThemeGetBraveThemeListFunction : public UIThreadExtensionFunction { public: - DECLARE_EXTENSION_FUNCTION("braveTheme.setBraveThemeType", UNKNOWN) + DECLARE_EXTENSION_FUNCTION("braveTheme.getBraveThemeList", UNKNOWN) protected: - ~BraveThemeSetBraveThemeTypeFunction() override {} + ~BraveThemeGetBraveThemeListFunction() override {} ResponseAction Run() override; }; -class BraveThemeGetBraveThemeTypeFunction : public UIThreadExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveTheme.getBraveThemeType", UNKNOWN) - - protected: - ~BraveThemeGetBraveThemeTypeFunction() override {} - - ResponseAction Run() override; -}; - } // namespace api } // namespace extensions diff --git a/browser/extensions/api/brave_theme_api_browsertest.cc b/browser/extensions/api/brave_theme_api_browsertest.cc index b22d09137f96..abd6c6b5be41 100644 --- a/browser/extensions/api/brave_theme_api_browsertest.cc +++ b/browser/extensions/api/brave_theme_api_browsertest.cc @@ -1,4 +1,5 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ @@ -18,24 +19,21 @@ #include "extensions/common/extension_builder.h" #include "testing/gmock/include/gmock/gmock.h" -using extensions::api::BraveThemeGetBraveThemeTypeFunction; -using extensions::api::BraveThemeSetBraveThemeTypeFunction; -using extension_function_test_utils::RunFunctionAndReturnSingleResult; using BTS = BraveThemeService; class BraveThemeAPIBrowserTest : public InProcessBrowserTest { - public: - void SetUpOnMainThread() override { - InProcessBrowserTest::SetUpOnMainThread(); - extension_ = extensions::ExtensionBuilder("Test").Build(); - } + public: + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + extension_ = extensions::ExtensionBuilder("Test").Build(); + } - scoped_refptr extension() { - return extension_; - } + scoped_refptr extension() { + return extension_; + } - private: - scoped_refptr extension_; + private: + scoped_refptr extension_; }; namespace { @@ -52,48 +50,6 @@ void SetBraveThemeType(Profile* profile, BraveThemeType type) { } } // namespace -IN_PROC_BROWSER_TEST_F(BraveThemeAPIBrowserTest, - BraveThemeGetBraveThemeTypeTest) { - Profile* profile = browser()->profile(); - - // Check default type is set initially. - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_DEFAULT, - BTS::GetUserPreferredBraveThemeType(profile)); - - // Change to Light type and check it from api. - SetBraveThemeType(profile, BraveThemeType::BRAVE_THEME_TYPE_LIGHT); - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_LIGHT, - BTS::GetUserPreferredBraveThemeType(profile)); - scoped_refptr get_function( - new BraveThemeGetBraveThemeTypeFunction()); - get_function->set_extension(extension().get()); - std::unique_ptr value; - value.reset(RunFunctionAndReturnSingleResult(get_function.get(), - std::string("[]"), - browser())); - EXPECT_EQ(value->GetString(), "Light"); -} - -IN_PROC_BROWSER_TEST_F(BraveThemeAPIBrowserTest, - BraveThemeSetBraveThemeTypeTest) { - Profile* profile = browser()->profile(); - - // Check default type is set initially. - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_DEFAULT, - BTS::GetUserPreferredBraveThemeType(profile)); - - // Change theme type to Light via api and check it. - scoped_refptr set_function( - new BraveThemeSetBraveThemeTypeFunction()); - set_function->set_extension(extension().get()); - RunFunctionAndReturnSingleResult(set_function.get(), - std::string("[\"Light\"]"), - browser()); - - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_LIGHT, - BTS::GetUserPreferredBraveThemeType(profile)); -} - IN_PROC_BROWSER_TEST_F(BraveThemeAPIBrowserTest, BraveThemeEventRouterTest) { Profile* profile = browser()->profile(); diff --git a/browser/extensions/api/settings_private/brave_prefs_util.cc b/browser/extensions/api/settings_private/brave_prefs_util.cc index 67ab19c6b523..b1d38e51e4e9 100644 --- a/browser/extensions/api/settings_private/brave_prefs_util.cc +++ b/browser/extensions/api/settings_private/brave_prefs_util.cc @@ -43,6 +43,8 @@ const PrefsUtil::TypedPrefMap& BravePrefsUtil::GetWhitelistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_brave_whitelist)[kHideBraveRewardsButton] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_brave_whitelist)[kBraveThemeType] = + settings_api::PrefType::PREF_TYPE_NUMBER; // WebTorrent pref (*s_brave_whitelist)[kWebTorrentEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; diff --git a/browser/extensions/brave_theme_event_router.cc b/browser/extensions/brave_theme_event_router.cc index 8ae27e7be176..712f3101eccd 100644 --- a/browser/extensions/brave_theme_event_router.cc +++ b/browser/extensions/brave_theme_event_router.cc @@ -1,16 +1,33 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ #include "brave/browser/extensions/brave_theme_event_router.h" +#include +#include + #include "brave/browser/themes/brave_theme_service.h" #include "brave/common/extensions/api/brave_theme.h" #include "chrome/browser/profiles/profile.h" #include "extensions/browser/event_router.h" #include "extensions/browser/extension_event_histogram_value.h" -using BTS = BraveThemeService; +namespace { +std::string GetStringFromBraveThemeType( + BraveThemeType type) { + switch (type) { + case BraveThemeType::BRAVE_THEME_TYPE_LIGHT: + return "Light"; + case BraveThemeType::BRAVE_THEME_TYPE_DARK: + return "Dark"; + default: + NOTREACHED(); + return "Default"; + } +} +} // namespace namespace extensions { @@ -27,8 +44,8 @@ class BraveThemeEventRouterImpl : public BraveThemeEventRouter { void BraveThemeEventRouterImpl::OnBraveThemeTypeChanged(Profile* profile) { EventRouter* event_router = EventRouter::Get(profile); - const std::string theme_type = BTS::GetStringFromBraveThemeType( - BTS::GetActiveBraveThemeType(profile)); + const std::string theme_type = GetStringFromBraveThemeType( + BraveThemeService::GetActiveBraveThemeType(profile)); auto event = std::make_unique( extensions::events::BRAVE_ON_BRAVE_THEME_TYPE_CHANGED, diff --git a/browser/resources/settings/brave_appearance_page/brave_appearance_browser_proxy.js b/browser/resources/settings/brave_appearance_page/brave_appearance_browser_proxy.js index a99aa422cee4..36c90dcc4276 100644 --- a/browser/resources/settings/brave_appearance_page/brave_appearance_browser_proxy.js +++ b/browser/resources/settings/brave_appearance_page/brave_appearance_browser_proxy.js @@ -8,11 +8,7 @@ cr.define('settings', function() { /** * @return {!Promise} */ - getBraveThemeType() {} - /** - * @param {string} theme name. - */ - setBraveThemeType(theme) {} + getBraveThemeList() {} } /** @@ -20,13 +16,8 @@ cr.define('settings', function() { */ class BraveAppearanceBrowserProxyImpl { /** @override */ - getBraveThemeType() { - return new Promise(resolve => chrome.braveTheme.getBraveThemeType(resolve)) - } - - /** @override */ - setBraveThemeType(theme) { - chrome.braveTheme.setBraveThemeType(theme); + getBraveThemeList() { + return new Promise(resolve => chrome.braveTheme.getBraveThemeList(resolve)) } } diff --git a/browser/resources/settings/brave_appearance_page/brave_appearance_page.html b/browser/resources/settings/brave_appearance_page/brave_appearance_page.html index 41a1f32a496b..3c7a2bdb1eaf 100644 --- a/browser/resources/settings/brave_appearance_page/brave_appearance_page.html +++ b/browser/resources/settings/brave_appearance_page/brave_appearance_page.html @@ -12,15 +12,11 @@
$i18n{appearanceSettingsBraveTheme}
- + +
diff --git a/browser/resources/settings/brave_appearance_page/brave_appearance_page.js b/browser/resources/settings/brave_appearance_page/brave_appearance_page.js index 8467c9221c59..c6bacd28d9ee 100644 --- a/browser/resources/settings/brave_appearance_page/brave_appearance_page.js +++ b/browser/resources/settings/brave_appearance_page/brave_appearance_page.js @@ -9,15 +9,7 @@ Polymer({ is: 'settings-brave-appearance-theme', properties: { - braveThemeTypes_: { - readOnly: true, - type: Array, - value: [ - 'Light', - 'Dark', - ], - }, - braveThemeType_: String, + braveThemeList_: [], }, /** @private {?settings.BraveAppearanceBrowserProxy} */ @@ -30,24 +22,10 @@ Polymer({ /** @override */ ready: function() { - this.browserProxy_.getBraveThemeType().then(theme => { - this.braveThemeType_ = theme; + this.browserProxy_.getBraveThemeList().then(list => { + this.braveThemeList_ = JSON.parse(list); }); }, - - /** - * @param {string} theme1 - * @param {string} theme2 - * @return {boolean} - * @private - */ - braveThemeTypeEqual_: function(theme1, theme2) { - return theme1 === theme2; - }, - - onBraveThemeTypeChange_: function() { - this.browserProxy_.setBraveThemeType(this.$.braveThemeType.value); - }, }); /** diff --git a/browser/themes/BUILD.gn b/browser/themes/BUILD.gn index 8194ec3b7715..c11f65b40160 100644 --- a/browser/themes/BUILD.gn +++ b/browser/themes/BUILD.gn @@ -4,6 +4,10 @@ source_set("themes") { "brave_theme_service.h", "brave_theme_service_win.cc", "brave_theme_service_win.h", + "brave_theme_utils.h", + "brave_theme_utils_linux.cc", + "brave_theme_utils_mac.mm", + "brave_theme_utils_win.cc", "theme_properties.cc", "theme_properties.h", ] diff --git a/browser/themes/brave_theme_service.cc b/browser/themes/brave_theme_service.cc index e21cd0a22956..2b0c72c86ade 100644 --- a/browser/themes/brave_theme_service.cc +++ b/browser/themes/brave_theme_service.cc @@ -1,33 +1,98 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ #include "brave/browser/themes/brave_theme_service.h" +#include + #include "base/command_line.h" #include "base/strings/string_util.h" #include "brave/browser/extensions/brave_theme_event_router.h" #include "brave/browser/themes/theme_properties.h" +#include "brave/browser/themes/brave_theme_utils.h" #include "brave/common/brave_switches.h" #include "brave/common/pref_names.h" +#include "brave/grit/brave_generated_resources.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/common/channel_info.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" #include "components/version_info/channel.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/ui_base_features.h" +#include "ui/base/ui_base_switches.h" #include "ui/native_theme/native_theme.h" #include "ui/native_theme/native_theme_dark_aura.h" +namespace { +BraveThemeType GetThemeTypeBasedOnChannel() { + switch (chrome::GetChannel()) { + case version_info::Channel::STABLE: + case version_info::Channel::BETA: + return BraveThemeType::BRAVE_THEME_TYPE_LIGHT; + case version_info::Channel::DEV: + case version_info::Channel::CANARY: + case version_info::Channel::UNKNOWN: + default: + return BraveThemeType::BRAVE_THEME_TYPE_DARK; + } +} +} // namespace + +// static +base::Value BraveThemeService::GetBraveThemeList() { + base::Value list(base::Value::Type::LIST); + + if (SystemThemeModeEnabled()) { + base::Value system_type(base::Value::Type::DICTIONARY); + system_type.SetKey( + "value", + base::Value(BraveThemeType::BRAVE_THEME_TYPE_DEFAULT)); + system_type.SetKey( + "name", + base::Value(l10n_util::GetStringUTF16(IDS_BRAVE_THEME_TYPE_SYSTEM))); + list.GetList().push_back(std::move(system_type)); + } + + base::Value dark_type(base::Value::Type::DICTIONARY); + dark_type.SetKey("value", base::Value(BraveThemeType::BRAVE_THEME_TYPE_DARK)); + dark_type.SetKey( + "name", + base::Value(l10n_util::GetStringUTF16(IDS_BRAVE_THEME_TYPE_DARK))); + list.GetList().push_back(std::move(dark_type)); + + base::Value light_type(base::Value::Type::DICTIONARY); + light_type.SetKey("value", + base::Value(BraveThemeType::BRAVE_THEME_TYPE_LIGHT)); + light_type.SetKey( + "name", + base::Value(l10n_util::GetStringUTF16(IDS_BRAVE_THEME_TYPE_LIGHT))); + list.GetList().push_back(std::move(light_type)); + + return list; +} + // static void BraveThemeService::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterIntegerPref(kBraveThemeType, BRAVE_THEME_TYPE_DEFAULT); + + // When this is set to true, prefs is changed from default type to + // effective type. In dtor, pref is reverted to default type if this is + // still true. With this, we can preserve the context that user didn't touch + // theme type yet. If it is changed to false, it means user changes system + // theme explicitly. + // To handle crash case, prefs is used instead of boolean flags. Recovering + // is done in BraveThemeService::Init(). + registry->RegisterBooleanPref(kUseOverriddenBraveThemeType, false); } // static -BraveThemeType BraveThemeService::GetUserPreferredBraveThemeType( - Profile* profile) { +BraveThemeType BraveThemeService::GetActiveBraveThemeType( + Profile* profile) { // allow override via cli flag const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); @@ -41,61 +106,51 @@ BraveThemeType BraveThemeService::GetUserPreferredBraveThemeType( if (requested_theme_value_lower == "dark") return BraveThemeType::BRAVE_THEME_TYPE_DARK; } - // get value from preferences - return static_cast( - profile->GetPrefs()->GetInteger(kBraveThemeType)); -} -// static -std::string BraveThemeService::GetStringFromBraveThemeType( - BraveThemeType type) { - switch (type) { - case BraveThemeType::BRAVE_THEME_TYPE_DEFAULT: - return "Default"; - case BraveThemeType::BRAVE_THEME_TYPE_LIGHT: - return "Light"; - case BraveThemeType::BRAVE_THEME_TYPE_DARK: - return "Dark"; - default: - NOTREACHED(); - } -} - -// static -BraveThemeType BraveThemeService::GetActiveBraveThemeType( - Profile* profile) { - const BraveThemeType preferred_theme = - GetUserPreferredBraveThemeType(profile); - switch (preferred_theme) { - case BraveThemeType::BRAVE_THEME_TYPE_DEFAULT: - switch (chrome::GetChannel()) { - case version_info::Channel::STABLE: - case version_info::Channel::BETA: - return BraveThemeType::BRAVE_THEME_TYPE_LIGHT; - case version_info::Channel::DEV: - case version_info::Channel::CANARY: - case version_info::Channel::UNKNOWN: - default: - return BraveThemeType::BRAVE_THEME_TYPE_DARK; - } - default: - return preferred_theme; + BraveThemeType type = static_cast( + profile->GetPrefs()->GetInteger(kBraveThemeType)); + if (type == BraveThemeType::BRAVE_THEME_TYPE_DEFAULT) { + DCHECK(SystemThemeModeEnabled()); + return ui::NativeTheme::GetInstanceForNativeUi()-> + SystemDarkModeEnabled() ? BraveThemeType::BRAVE_THEME_TYPE_DARK + : BraveThemeType::BRAVE_THEME_TYPE_LIGHT; } + return type; } BraveThemeService::BraveThemeService() {} -BraveThemeService::~BraveThemeService() {} +BraveThemeService::~BraveThemeService() { + // In test, kBraveThemeType isn't registered. + if (!profile()->GetPrefs()->FindPreference(kBraveThemeType)) + return; + + if (profile()->GetPrefs()->GetBoolean(kUseOverriddenBraveThemeType)) { + brave_theme_type_pref_.Destroy(); + profile()->GetPrefs()->SetInteger(kBraveThemeType, + BraveThemeType::BRAVE_THEME_TYPE_DEFAULT); + } +} void BraveThemeService::Init(Profile* profile) { - // In unittest, kBraveThemeType isn't registered. + // In test, kBraveThemeType isn't registered. if (profile->GetPrefs()->FindPreference(kBraveThemeType)) { + RecoverPrefStates(profile); + OverrideDefaultThemeIfNeeded(profile); + if (SystemThemeModeEnabled()) { + // Start with proper system theme to make brave theme and + // base ui components theme use same theme. + SetSystemTheme(static_cast( + profile->GetPrefs()->GetInteger(kBraveThemeType))); + } + brave_theme_type_pref_.Init( kBraveThemeType, profile->GetPrefs(), base::Bind(&BraveThemeService::OnPreferenceChanged, base::Unretained(this))); } + ThemeService::Init(profile); } @@ -117,12 +172,29 @@ SkColor BraveThemeService::GetDefaultColor(int id, bool incognito) const { void BraveThemeService::OnPreferenceChanged(const std::string& pref_name) { DCHECK(pref_name == kBraveThemeType); - // Notify dark (cross-platform) and light (platform-specific) variants - GetActiveBraveThemeType(profile()) == BraveThemeType::BRAVE_THEME_TYPE_LIGHT - ? ui::NativeThemeDarkAura::instance()->NotifyObservers() - : ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers(); - - NotifyThemeChanged(); + // Changing theme type means default theme is not overridden anymore. + profile()->GetPrefs()->SetBoolean(kUseOverriddenBraveThemeType, false); + + bool notify_theme_observer_here = true; +#if defined(OS_MACOSX) + if (SystemThemeModeEnabled()) { + // When system theme is changed, system theme changing observer notifies + // proper native theme observers. + // So, we don't need to notify again. See NotifyProperThemeObserver() + // in chromium_src/ui/native_theme/native_theme_mac.mm. + notify_theme_observer_here = false; + SetSystemTheme(static_cast( + profile()->GetPrefs()->GetInteger(kBraveThemeType))); + } +#endif + if (notify_theme_observer_here) { + // Notify dark (cross-platform) and light (platform-specific) variants + // When theme is changed from light to dark, we notify to light theme + // observer because NativeThemeObserver observes light native theme. + GetActiveBraveThemeType(profile()) == BraveThemeType::BRAVE_THEME_TYPE_LIGHT + ? ui::NativeThemeDarkAura::instance()->NotifyObservers() + : ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers(); + } if (!brave_theme_event_router_) brave_theme_event_router_ = extensions::BraveThemeEventRouter::Create(); @@ -130,8 +202,46 @@ void BraveThemeService::OnPreferenceChanged(const std::string& pref_name) { brave_theme_event_router_->OnBraveThemeTypeChanged(profile()); } +void BraveThemeService::RecoverPrefStates(Profile* profile) { + // kUseOverriddenBraveThemeType is true means pref states are not cleaned + // up properly at the last running(ex, crash). Recover them here. + if (profile->GetPrefs()->GetBoolean(kUseOverriddenBraveThemeType)) { + profile->GetPrefs()->SetInteger(kBraveThemeType, + BraveThemeType::BRAVE_THEME_TYPE_DEFAULT); + } +} + +void BraveThemeService::OverrideDefaultThemeIfNeeded(Profile* profile) { + if (!SystemThemeModeEnabled() && + profile->GetPrefs()->GetInteger(kBraveThemeType) == + BraveThemeType::BRAVE_THEME_TYPE_DEFAULT) { + profile->GetPrefs()->SetBoolean(kUseOverriddenBraveThemeType, + true); + profile->GetPrefs()->SetInteger(kBraveThemeType, + GetThemeTypeBasedOnChannel()); + } +} void BraveThemeService::SetBraveThemeEventRouterForTesting( extensions::BraveThemeEventRouter* mock_router) { brave_theme_event_router_.reset(mock_router); } + +// static +bool BraveThemeService::use_system_theme_mode_in_test_ = false; +bool BraveThemeService::is_test_ = false; + +// static +bool BraveThemeService::SystemThemeModeEnabled() { + if (is_test_) + return use_system_theme_mode_in_test_; + + if (!base::FeatureList::IsEnabled(features::kDarkMode)) + return false; + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kForceDarkMode)) + return true; + + return SystemThemeSupportDarkMode(); +} diff --git a/browser/themes/brave_theme_service.h b/browser/themes/brave_theme_service.h index 08b4b34bca79..8e440c23dcf3 100644 --- a/browser/themes/brave_theme_service.h +++ b/browser/themes/brave_theme_service.h @@ -1,4 +1,5 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ @@ -20,17 +21,22 @@ class PrefRegistrySyncable; } enum BraveThemeType { - BRAVE_THEME_TYPE_DEFAULT, // Choose theme by channel - BRAVE_THEME_TYPE_DARK, // Use dark theme regardless of channel - BRAVE_THEME_TYPE_LIGHT, // Use light theme regardless of channel + // DEFAULT type acts as two ways depends on system theme mode. + // If system theme mode is disabled, we override it with channel based + // policy. See GetThemeTypeBasedOnChannel(). In this case, user can see + // two options in theme settings(dark and light). + // Otherwise, it acts like system theme mode. In this case, user can see + // three options in theme settings(os theme, dark and light). + BRAVE_THEME_TYPE_DEFAULT, + BRAVE_THEME_TYPE_DARK, + BRAVE_THEME_TYPE_LIGHT, }; class BraveThemeService : public ThemeService { public: static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); - static BraveThemeType GetUserPreferredBraveThemeType(Profile* profile); - static std::string GetStringFromBraveThemeType(BraveThemeType type); static BraveThemeType GetActiveBraveThemeType(Profile* profile); + static base::Value GetBraveThemeList(); BraveThemeService(); ~BraveThemeService() override; @@ -43,13 +49,25 @@ class BraveThemeService : public ThemeService { SkColor GetDefaultColor(int id, bool incognito) const override; private: + friend class BraveThemeServiceTestWithoutSystemTheme; FRIEND_TEST_ALL_PREFIXES(BraveThemeAPIBrowserTest, BraveThemeEventRouterTest); + FRIEND_TEST_ALL_PREFIXES(BraveThemeServiceTest, GetBraveThemeListTest); + FRIEND_TEST_ALL_PREFIXES(BraveThemeServiceTest, SystemThemeChangeTest); + // Own |mock_router|. void SetBraveThemeEventRouterForTesting( extensions::BraveThemeEventRouter* mock_router); void OnPreferenceChanged(const std::string& pref_name); + void RecoverPrefStates(Profile* profile); + void OverrideDefaultThemeIfNeeded(Profile* profile); + + static bool SystemThemeModeEnabled(); + + static bool is_test_; + static bool use_system_theme_mode_in_test_; + IntegerPrefMember brave_theme_type_pref_; std::unique_ptr brave_theme_event_router_; diff --git a/browser/themes/brave_theme_service_browsertest.cc b/browser/themes/brave_theme_service_browsertest.cc index 775d5de26118..3b80fe432475 100644 --- a/browser/themes/brave_theme_service_browsertest.cc +++ b/browser/themes/brave_theme_service_browsertest.cc @@ -1,4 +1,5 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ @@ -9,6 +10,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_service.h" +#include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/browser.h" #include "components/prefs/pref_service.h" #include "testing/gmock/include/gmock/gmock.h" @@ -25,6 +27,10 @@ void SetBraveThemeType(Profile* profile, BraveThemeType type) { profile->GetPrefs()->SetInteger(kBraveThemeType, type); } +bool IsDefaultThemeOverridden(Profile* profile) { + return profile->GetPrefs()->GetBoolean(kUseOverriddenBraveThemeType); +} + class TestNativeThemeObserver : public ui::NativeThemeObserver { public: TestNativeThemeObserver() {} @@ -35,39 +41,57 @@ class TestNativeThemeObserver : public ui::NativeThemeObserver { } // namespace -IN_PROC_BROWSER_TEST_F(BraveThemeServiceTest, BraveThemeChangeTest) { +class BraveThemeServiceTestWithoutSystemTheme : public InProcessBrowserTest { + public: + BraveThemeServiceTestWithoutSystemTheme() { + BraveThemeService::is_test_ = true; + BraveThemeService::use_system_theme_mode_in_test_ = false; + } +}; + +IN_PROC_BROWSER_TEST_F(BraveThemeServiceTestWithoutSystemTheme, + BraveThemeChangeTest) { Profile* profile = browser()->profile(); Profile* profile_private = profile->GetOffTheRecordProfile(); - const ui::ThemeProvider& tp = ThemeService::GetThemeProviderForProfile(profile); - const ui::ThemeProvider& tp_private = ThemeService::GetThemeProviderForProfile(profile_private); + const ui::ThemeProvider& tp = + ThemeService::GetThemeProviderForProfile(profile); + const ui::ThemeProvider& tp_private = + ThemeService::GetThemeProviderForProfile(profile_private); auto test_theme_property = BraveThemeProperties::COLOR_FOR_TEST; // Check default type is set initially. - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_DEFAULT, BTS::GetUserPreferredBraveThemeType(profile)); - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_DEFAULT, BTS::GetUserPreferredBraveThemeType(profile_private)); + EXPECT_TRUE(IsDefaultThemeOverridden(profile)); + EXPECT_TRUE(IsDefaultThemeOverridden(profile_private)); // Test light theme SetBraveThemeType(profile, BraveThemeType::BRAVE_THEME_TYPE_LIGHT); - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_LIGHT, BTS::GetUserPreferredBraveThemeType(profile)); - EXPECT_EQ(BraveThemeProperties::kLightColorForTest, tp.GetColor(test_theme_property)); + EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_LIGHT, + BTS::GetActiveBraveThemeType(profile)); + EXPECT_EQ(BraveThemeProperties::kLightColorForTest, + tp.GetColor(test_theme_property)); // Test light theme private SetBraveThemeType(profile_private, BraveThemeType::BRAVE_THEME_TYPE_LIGHT); - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_LIGHT, BTS::GetUserPreferredBraveThemeType(profile_private)); - EXPECT_EQ(BraveThemeProperties::kPrivateColorForTest, tp_private.GetColor(test_theme_property)); - + EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_LIGHT, + BTS::GetActiveBraveThemeType(profile_private)); + EXPECT_EQ(BraveThemeProperties::kPrivateColorForTest, + tp_private.GetColor(test_theme_property)); // Test dark theme SetBraveThemeType(profile, BraveThemeType::BRAVE_THEME_TYPE_DARK); - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_DARK, BTS::GetUserPreferredBraveThemeType(profile)); - EXPECT_EQ(BraveThemeProperties::kDarkColorForTest, tp.GetColor(test_theme_property)); + EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_DARK, + BTS::GetActiveBraveThemeType(profile)); + EXPECT_EQ(BraveThemeProperties::kDarkColorForTest, + tp.GetColor(test_theme_property)); // Test dark theme private SetBraveThemeType(profile_private, BraveThemeType::BRAVE_THEME_TYPE_DARK); - EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_DARK, BTS::GetUserPreferredBraveThemeType(profile_private)); - EXPECT_EQ(BraveThemeProperties::kPrivateColorForTest, tp_private.GetColor(test_theme_property)); + EXPECT_EQ(BraveThemeType::BRAVE_THEME_TYPE_DARK, + BTS::GetActiveBraveThemeType(profile_private)); + EXPECT_EQ(BraveThemeProperties::kPrivateColorForTest, + tp_private.GetColor(test_theme_property)); } // Test whether appropriate native theme observer is called when brave theme is @@ -96,3 +120,33 @@ IN_PROC_BROWSER_TEST_F(BraveThemeServiceTest, NativeThemeObserverTest) { SetBraveThemeType(profile, BraveThemeType::BRAVE_THEME_TYPE_DARK); SetBraveThemeType(profile, BraveThemeType::BRAVE_THEME_TYPE_LIGHT); } + +#if defined(OS_MACOSX) +IN_PROC_BROWSER_TEST_F(BraveThemeServiceTest, SystemThemeChangeTest) { + // TODO(simonhong): Delete this when we gets dark mode enabled branch on + // MacOS. + if (!BraveThemeService::SystemThemeModeEnabled()) + return; + + const bool initial_mode = + ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled(); + Profile* profile = browser()->profile(); + + // Change to light. + SetBraveThemeType(profile, BraveThemeType::BRAVE_THEME_TYPE_LIGHT); + EXPECT_FALSE( + ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled()); + + SetBraveThemeType(profile, BraveThemeType::BRAVE_THEME_TYPE_DARK); + EXPECT_TRUE( + ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled()); + + SetBraveThemeType(profile, BraveThemeType::BRAVE_THEME_TYPE_LIGHT); + EXPECT_FALSE( + ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled()); + + SetBraveThemeType(profile, BraveThemeType::BRAVE_THEME_TYPE_DEFAULT); + EXPECT_EQ(initial_mode, + ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled()); +} +#endif diff --git a/browser/themes/brave_theme_service_unittest.cc b/browser/themes/brave_theme_service_unittest.cc new file mode 100644 index 000000000000..4be45787cd77 --- /dev/null +++ b/browser/themes/brave_theme_service_unittest.cc @@ -0,0 +1,21 @@ +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/themes/brave_theme_service.h" + +#include "testing/gtest/include/gtest/gtest.h" + +TEST(BraveThemeServiceTest, GetBraveThemeListTest) { + BraveThemeService::is_test_ = true; + + BraveThemeService::use_system_theme_mode_in_test_ = true; + + base::Value list = BraveThemeService::GetBraveThemeList(); + EXPECT_EQ(3UL, list.GetList().size()); + + BraveThemeService::use_system_theme_mode_in_test_ = false; + list = BraveThemeService::GetBraveThemeList(); + EXPECT_EQ(2UL, list.GetList().size()); +} diff --git a/browser/themes/brave_theme_utils.h b/browser/themes/brave_theme_utils.h new file mode 100644 index 000000000000..e48148a94c1a --- /dev/null +++ b/browser/themes/brave_theme_utils.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_THEMES_BRAVE_THEME_UTILS_H_ +#define BRAVE_BROWSER_THEMES_BRAVE_THEME_UTILS_H_ + +#include "brave/browser/themes/brave_theme_service.h" + +bool SystemThemeSupportDarkMode(); + +// Override system theme with |type|. With this, browser gets this theme type +// regardless of OS preference when it queries system theme type. +// If |type| is BRAVE_THEME_TYPE_DEFAULT, clear overridden theme and follow +// the theme in OS preference. +// Note: This is only implemented on MacOS for now. +void SetSystemTheme(BraveThemeType type); + +#endif // BRAVE_BROWSER_THEMES_BRAVE_THEME_UTILS_H_ diff --git a/browser/themes/brave_theme_utils_linux.cc b/browser/themes/brave_theme_utils_linux.cc new file mode 100644 index 000000000000..37cc88a28b96 --- /dev/null +++ b/browser/themes/brave_theme_utils_linux.cc @@ -0,0 +1,14 @@ +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/themes/brave_theme_utils.h" + +bool SystemThemeSupportDarkMode() { + // Linux doesn't support dark mode yet. + return false; +} + +void SetSystemTheme(BraveThemeType type) { +} diff --git a/browser/themes/brave_theme_utils_mac.mm b/browser/themes/brave_theme_utils_mac.mm new file mode 100644 index 000000000000..fb59096963e3 --- /dev/null +++ b/browser/themes/brave_theme_utils_mac.mm @@ -0,0 +1,29 @@ +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/themes/brave_theme_utils.h" + +#import + +bool SystemThemeSupportDarkMode() { + // Dark mode is supported since Mojave. + if (@available(macOS 10.14, *)) + return true; + return false; +} + +void SetSystemTheme(BraveThemeType type) { + if (type == BRAVE_THEME_TYPE_DEFAULT) { + [NSApp setAppearance:nil]; + return; + } + + if (@available(macOS 10.14, *)) { + NSAppearanceName new_appearance_name = + type == BRAVE_THEME_TYPE_DARK ? NSAppearanceNameDarkAqua + : NSAppearanceNameAqua; + [NSApp setAppearance:[NSAppearance appearanceNamed:new_appearance_name]]; + } +} diff --git a/browser/themes/brave_theme_utils_win.cc b/browser/themes/brave_theme_utils_win.cc new file mode 100644 index 000000000000..7061b89df6f6 --- /dev/null +++ b/browser/themes/brave_theme_utils_win.cc @@ -0,0 +1,28 @@ +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/themes/brave_theme_utils.h" + +#include + +#include "base/win/registry.h" + +// Copied from ctor of NativeThemeWin. +bool SystemThemeSupportDarkMode() { + // 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. + base::win::RegKey hkcu_themes_regkey; + bool key_open_succeeded = + hkcu_themes_regkey.Open( + HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\" + L"Themes\\Personalize", + KEY_READ | KEY_NOTIFY) == ERROR_SUCCESS; + return key_open_succeeded; +} + +void SetSystemTheme(BraveThemeType type) { +} diff --git a/chromium_src/ui/native_theme/native_theme_mac.mm b/chromium_src/ui/native_theme/native_theme_mac.mm new file mode 100644 index 000000000000..305144cdbb64 --- /dev/null +++ b/chromium_src/ui/native_theme/native_theme_mac.mm @@ -0,0 +1,18 @@ +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ + +#include "ui/native_theme/native_theme_dark_aura.h" + +void NotifyProperThemeObserver(); + +#include "../../../../ui/native_theme/native_theme_mac.mm" + +void NotifyProperThemeObserver() { + // When theme is changed from light to dark, we notify to light theme observer + // because NativeThemeObserver observes light native theme + ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled() + ? ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers() + : ui::NativeThemeDarkAura::instance()->NotifyObservers(); +} diff --git a/chromium_src/ui/native_theme/native_theme_win.cc b/chromium_src/ui/native_theme/native_theme_win.cc new file mode 100644 index 000000000000..1e429495a97f --- /dev/null +++ b/chromium_src/ui/native_theme/native_theme_win.cc @@ -0,0 +1,20 @@ +/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ + +#include "ui/native_theme/native_theme_dark_aura.h" + +void NotifyProperThemeObserver(); + +#include "../../../../ui/native_theme/native_theme_win.cc" // NOLINT + +// TODO(simonhong): Move this function to ui namespace to share with +// native_theme_mac.mm. +void NotifyProperThemeObserver() { + // When theme is changed from light to dark, we notify to light theme observer + // because NativeThemeObserver observes light native theme + ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled() + ? ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers() + : ui::NativeThemeDarkAura::instance()->NotifyObservers(); +} diff --git a/common/extensions/api/brave_theme.json b/common/extensions/api/brave_theme.json index c495003657bd..b786b0e1a2ba 100644 --- a/common/extensions/api/brave_theme.json +++ b/common/extensions/api/brave_theme.json @@ -25,36 +25,19 @@ ], "functions": [ { - "name": "setBraveThemeType", + "name": "getBraveThemeList", "type": "function", - "description": "Set brave theme", - "parameters": [ - { - "name": "type", - "type": "string" - }, - { - "name": "callback", - "type": "function", - "optional": true, - "parameters": [] - } - ] - }, - { - "name": "getBraveThemeType", - "type": "function", - "description": "Get brave theme", + "description": "Get available brave theme list", "parameters": [ { "name": "callback", "type": "function", - "description": "Function called when current theme type is fetched", + "description": "Function called when brave theme list is fetched", "parameters": [ { - "name": "type", + "name": "types", "type": "string", - "description": "current theme type(ex, Dark or Light)" + "description": "json stringified avalable theme list" } ] } diff --git a/common/pref_names.cc b/common/pref_names.cc index 3d6e62af74ad..4491db18e75b 100644 --- a/common/pref_names.cc +++ b/common/pref_names.cc @@ -23,6 +23,8 @@ const char kUseAlternativeSearchEngineProvider[] = const char kAlternativeSearchEngineProviderInTor[] = "brave.alternate_private_search_engine_in_tor"; const char kBraveThemeType[] = "brave.theme.type"; +const char kUseOverriddenBraveThemeType[] = + "brave.theme.use_overridden_brave_theme_type"; const char kLocationBarIsWide[] = "brave.location_bar_is_wide"; const char kReferralPromoCode[] = "brave.referral.promo_code"; const char kReferralDownloadID[] = "brave.referral.download_id"; diff --git a/common/pref_names.h b/common/pref_names.h index 598bd70f735c..6dc71a4364e1 100644 --- a/common/pref_names.h +++ b/common/pref_names.h @@ -22,6 +22,7 @@ extern const char kWidevineInstalledVersion[]; extern const char kUseAlternativeSearchEngineProvider[]; extern const char kAlternativeSearchEngineProviderInTor[]; extern const char kBraveThemeType[]; +extern const char kUseOverriddenBraveThemeType[]; extern const char kLocationBarIsWide[]; extern const char kReferralPromoCode[]; extern const char kReferralDownloadID[]; diff --git a/patches/ui-native_theme-native_theme_mac.mm.patch b/patches/ui-native_theme-native_theme_mac.mm.patch new file mode 100644 index 000000000000..aa5ae1c66b96 --- /dev/null +++ b/patches/ui-native_theme-native_theme_mac.mm.patch @@ -0,0 +1,16 @@ +diff --git a/ui/native_theme/native_theme_mac.mm b/ui/native_theme/native_theme_mac.mm +index de3389fac98a708420374851dbf7c0970cb68102..32ca3fede8784b0f519174f314c302761dd0be75 100644 +--- a/ui/native_theme/native_theme_mac.mm ++++ b/ui/native_theme/native_theme_mac.mm +@@ -55,7 +55,11 @@ + ofObject:(id)object + change:(NSDictionary*)change + context:(void*)context { ++#if defined(BRAVE_CHROMIUM_BUILD) ++ NotifyProperThemeObserver(); ++#else + ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers(); ++#endif + } + + @end diff --git a/test/BUILD.gn b/test/BUILD.gn index 3228cadfb2eb..b5cd237af22a 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -51,6 +51,7 @@ test("brave_unit_tests") { "//brave/browser/profiles/brave_profile_manager_unittest.cc", "//brave/browser/resources/settings/reset_report_uploader_unittest.cc", "//brave/browser/resources/settings/brandcode_config_fetcher_unittest.cc", + "//brave/browser/themes/brave_theme_service_unittest.cc", "//brave/chromium_src/chrome/browser/external_protocol/external_protocol_handler_unittest.cc", "//brave/chromium_src/chrome/browser/signin/account_consistency_disabled_unittest.cc", "//brave/chromium_src/chrome/browser/ui/bookmarks/brave_bookmark_context_menu_controller_unittest.cc",