electron_api_native_theme.cc 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // Copyright (c) 2019 Slack Technologies, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/api/electron_api_native_theme.h"
  5. #include <string>
  6. #include "content/public/browser/browser_task_traits.h"
  7. #include "content/public/browser/browser_thread.h"
  8. #include "gin/handle.h"
  9. #include "shell/common/gin_converters/std_converter.h"
  10. #include "shell/common/gin_helper/dictionary.h"
  11. #include "shell/common/gin_helper/object_template_builder.h"
  12. #include "shell/common/node_includes.h"
  13. #include "ui/native_theme/native_theme.h"
  14. namespace electron::api {
  15. gin::WrapperInfo NativeTheme::kWrapperInfo = {gin::kEmbedderNativeGin};
  16. NativeTheme::NativeTheme(v8::Isolate* isolate,
  17. ui::NativeTheme* ui_theme,
  18. ui::NativeTheme* web_theme)
  19. : ui_theme_(ui_theme), web_theme_(web_theme) {
  20. ui_theme_->AddObserver(this);
  21. }
  22. NativeTheme::~NativeTheme() {
  23. ui_theme_->RemoveObserver(this);
  24. }
  25. void NativeTheme::OnNativeThemeUpdatedOnUI() {
  26. Emit("updated");
  27. }
  28. void NativeTheme::OnNativeThemeUpdated(ui::NativeTheme* theme) {
  29. content::GetUIThreadTaskRunner({})->PostTask(
  30. FROM_HERE, base::BindOnce(&NativeTheme::OnNativeThemeUpdatedOnUI,
  31. base::Unretained(this)));
  32. }
  33. void NativeTheme::SetThemeSource(ui::NativeTheme::ThemeSource override) {
  34. ui_theme_->set_theme_source(override);
  35. web_theme_->set_theme_source(override);
  36. #if BUILDFLAG(IS_MAC)
  37. // Update the macOS appearance setting for this new override value
  38. UpdateMacOSAppearanceForOverrideValue(override);
  39. #endif
  40. // TODO(MarshallOfSound): Update all existing browsers windows to use GTK dark
  41. // theme
  42. }
  43. ui::NativeTheme::ThemeSource NativeTheme::GetThemeSource() const {
  44. return ui_theme_->theme_source();
  45. }
  46. bool NativeTheme::ShouldUseDarkColors() {
  47. return ui_theme_->ShouldUseDarkColors();
  48. }
  49. bool NativeTheme::ShouldUseHighContrastColors() {
  50. return ui_theme_->UserHasContrastPreference();
  51. }
  52. bool NativeTheme::InForcedColorsMode() {
  53. return ui_theme_->InForcedColorsMode();
  54. }
  55. bool NativeTheme::GetPrefersReducedTransparency() {
  56. return ui_theme_->GetPrefersReducedTransparency();
  57. }
  58. #if BUILDFLAG(IS_MAC)
  59. const CFStringRef WhiteOnBlack = CFSTR("whiteOnBlack");
  60. const CFStringRef UniversalAccessDomain = CFSTR("com.apple.universalaccess");
  61. #endif
  62. // TODO(MarshallOfSound): Implement for Linux
  63. bool NativeTheme::ShouldUseInvertedColorScheme() {
  64. #if BUILDFLAG(IS_MAC)
  65. CFPreferencesAppSynchronize(UniversalAccessDomain);
  66. Boolean keyExistsAndHasValidFormat = false;
  67. Boolean is_inverted = CFPreferencesGetAppBooleanValue(
  68. WhiteOnBlack, UniversalAccessDomain, &keyExistsAndHasValidFormat);
  69. if (!keyExistsAndHasValidFormat)
  70. return false;
  71. return is_inverted;
  72. #else
  73. return ui_theme_->GetPlatformHighContrastColorScheme() ==
  74. ui::NativeTheme::PlatformHighContrastColorScheme::kDark;
  75. #endif
  76. }
  77. // static
  78. gin::Handle<NativeTheme> NativeTheme::Create(v8::Isolate* isolate) {
  79. ui::NativeTheme* ui_theme = ui::NativeTheme::GetInstanceForNativeUi();
  80. ui::NativeTheme* web_theme = ui::NativeTheme::GetInstanceForWeb();
  81. return gin::CreateHandle(isolate,
  82. new NativeTheme(isolate, ui_theme, web_theme));
  83. }
  84. gin::ObjectTemplateBuilder NativeTheme::GetObjectTemplateBuilder(
  85. v8::Isolate* isolate) {
  86. return gin_helper::EventEmitterMixin<NativeTheme>::GetObjectTemplateBuilder(
  87. isolate)
  88. .SetProperty("shouldUseDarkColors", &NativeTheme::ShouldUseDarkColors)
  89. .SetProperty("themeSource", &NativeTheme::GetThemeSource,
  90. &NativeTheme::SetThemeSource)
  91. .SetProperty("shouldUseHighContrastColors",
  92. &NativeTheme::ShouldUseHighContrastColors)
  93. .SetProperty("shouldUseInvertedColorScheme",
  94. &NativeTheme::ShouldUseInvertedColorScheme)
  95. .SetProperty("inForcedColorsMode", &NativeTheme::InForcedColorsMode)
  96. .SetProperty("prefersReducedTransparency",
  97. &NativeTheme::GetPrefersReducedTransparency);
  98. }
  99. const char* NativeTheme::GetTypeName() {
  100. return "NativeTheme";
  101. }
  102. } // namespace electron::api
  103. namespace {
  104. using electron::api::NativeTheme;
  105. void Initialize(v8::Local<v8::Object> exports,
  106. v8::Local<v8::Value> unused,
  107. v8::Local<v8::Context> context,
  108. void* priv) {
  109. v8::Isolate* isolate = context->GetIsolate();
  110. gin::Dictionary dict(isolate, exports);
  111. dict.Set("nativeTheme", NativeTheme::Create(isolate));
  112. }
  113. } // namespace
  114. namespace gin {
  115. v8::Local<v8::Value> Converter<ui::NativeTheme::ThemeSource>::ToV8(
  116. v8::Isolate* isolate,
  117. const ui::NativeTheme::ThemeSource& val) {
  118. switch (val) {
  119. case ui::NativeTheme::ThemeSource::kForcedDark:
  120. return ConvertToV8(isolate, "dark");
  121. case ui::NativeTheme::ThemeSource::kForcedLight:
  122. return ConvertToV8(isolate, "light");
  123. case ui::NativeTheme::ThemeSource::kSystem:
  124. default:
  125. return ConvertToV8(isolate, "system");
  126. }
  127. }
  128. bool Converter<ui::NativeTheme::ThemeSource>::FromV8(
  129. v8::Isolate* isolate,
  130. v8::Local<v8::Value> val,
  131. ui::NativeTheme::ThemeSource* out) {
  132. std::string theme_source;
  133. if (ConvertFromV8(isolate, val, &theme_source)) {
  134. if (theme_source == "dark") {
  135. *out = ui::NativeTheme::ThemeSource::kForcedDark;
  136. } else if (theme_source == "light") {
  137. *out = ui::NativeTheme::ThemeSource::kForcedLight;
  138. } else if (theme_source == "system") {
  139. *out = ui::NativeTheme::ThemeSource::kSystem;
  140. } else {
  141. return false;
  142. }
  143. return true;
  144. }
  145. return false;
  146. }
  147. } // namespace gin
  148. NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_native_theme, Initialize)