aw_dark_mode.cc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "android_webview/browser/aw_dark_mode.h"
  5. #include "android_webview/browser/aw_contents.h"
  6. #include "android_webview/browser_jni_headers/AwDarkMode_jni.h"
  7. #include "android_webview/common/aw_features.h"
  8. #include "base/android/scoped_java_ref.h"
  9. #include "base/memory/ptr_util.h"
  10. #include "base/metrics/histogram_macros.h"
  11. #include "content/public/browser/navigation_details.h"
  12. #include "content/public/browser/web_contents.h"
  13. #include "third_party/blink/public/common/web_preferences/web_preferences.h"
  14. #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom.h"
  15. using base::android::JavaParamRef;
  16. using base::android::ScopedJavaLocalRef;
  17. namespace android_webview {
  18. namespace {
  19. const void* const kAwDarkModeUserDataKey = &kAwDarkModeUserDataKey;
  20. bool sShouldEnableSimplifiedDarkMode = false;
  21. bool IsForceDarkEnabled(content::WebContents* web_contents) {
  22. AwContents* contents = AwContents::FromWebContents(web_contents);
  23. return contents && contents->GetViewTreeForceDarkState();
  24. }
  25. } // namespace
  26. // static
  27. jlong JNI_AwDarkMode_Init(JNIEnv* env,
  28. const JavaParamRef<jobject>& caller,
  29. const JavaParamRef<jobject>& java_web_contents) {
  30. content::WebContents* web_contents =
  31. content::WebContents::FromJavaWebContents(java_web_contents);
  32. DCHECK(web_contents);
  33. return reinterpret_cast<intptr_t>(new AwDarkMode(env, caller, web_contents));
  34. }
  35. void JNI_AwDarkMode_EnableSimplifiedDarkMode(JNIEnv* env) {
  36. sShouldEnableSimplifiedDarkMode = true;
  37. }
  38. AwDarkMode* AwDarkMode::FromWebContents(content::WebContents* contents) {
  39. return static_cast<AwDarkMode*>(
  40. contents->GetUserData(kAwDarkModeUserDataKey));
  41. }
  42. AwDarkMode::AwDarkMode(JNIEnv* env,
  43. jobject obj,
  44. content::WebContents* web_contents)
  45. : content::WebContentsObserver(web_contents), jobj_(env, obj) {
  46. web_contents->SetUserData(kAwDarkModeUserDataKey, base::WrapUnique(this));
  47. }
  48. AwDarkMode::~AwDarkMode() {
  49. JNIEnv* env = base::android::AttachCurrentThread();
  50. ScopedJavaLocalRef<jobject> scoped_obj = jobj_.get(env);
  51. if (scoped_obj)
  52. Java_AwDarkMode_onNativeObjectDestroyed(env, scoped_obj);
  53. }
  54. void AwDarkMode::PopulateWebPreferences(
  55. blink::web_pref::WebPreferences* web_prefs,
  56. int force_dark_mode,
  57. int force_dark_behavior,
  58. bool algorithmic_darkening_allowed) {
  59. if (!sShouldEnableSimplifiedDarkMode) {
  60. PopulateWebPreferencesForPreT(web_prefs, force_dark_mode,
  61. force_dark_behavior);
  62. return;
  63. }
  64. prefers_dark_from_theme_ = IsAppUsingDarkTheme();
  65. web_prefs->preferred_color_scheme =
  66. prefers_dark_from_theme_ ? blink::mojom::PreferredColorScheme::kDark
  67. : blink::mojom::PreferredColorScheme::kLight;
  68. web_prefs->force_dark_mode_enabled = false;
  69. is_force_dark_applied_ = false;
  70. if (IsForceDarkEnabled(web_contents())) {
  71. is_force_dark_applied_ = true;
  72. web_prefs->force_dark_mode_enabled = true;
  73. web_prefs->preferred_color_scheme =
  74. blink::mojom::PreferredColorScheme::kDark;
  75. } else if (prefers_dark_from_theme_) {
  76. is_force_dark_applied_ = algorithmic_darkening_allowed;
  77. web_prefs->force_dark_mode_enabled = algorithmic_darkening_allowed;
  78. }
  79. }
  80. void AwDarkMode::PopulateWebPreferencesForPreT(
  81. blink::web_pref::WebPreferences* web_prefs,
  82. int force_dark_mode,
  83. int force_dark_behavior) {
  84. prefers_dark_from_theme_ = false;
  85. switch (force_dark_mode) {
  86. case AwSettings::ForceDarkMode::FORCE_DARK_OFF:
  87. is_force_dark_applied_ = false;
  88. break;
  89. case AwSettings::ForceDarkMode::FORCE_DARK_ON:
  90. is_force_dark_applied_ = true;
  91. break;
  92. case AwSettings::ForceDarkMode::FORCE_DARK_AUTO: {
  93. is_force_dark_applied_ = IsForceDarkEnabled(web_contents());
  94. if (!is_force_dark_applied_)
  95. prefers_dark_from_theme_ = IsAppUsingDarkTheme();
  96. break;
  97. }
  98. }
  99. web_prefs->preferred_color_scheme =
  100. is_force_dark_applied_ ? blink::mojom::PreferredColorScheme::kDark
  101. : blink::mojom::PreferredColorScheme::kLight;
  102. if (is_force_dark_applied_) {
  103. switch (force_dark_behavior) {
  104. case AwSettings::ForceDarkBehavior::FORCE_DARK_ONLY: {
  105. web_prefs->preferred_color_scheme =
  106. blink::mojom::PreferredColorScheme::kLight;
  107. web_prefs->force_dark_mode_enabled = true;
  108. break;
  109. }
  110. case AwSettings::ForceDarkBehavior::MEDIA_QUERY_ONLY: {
  111. web_prefs->preferred_color_scheme =
  112. blink::mojom::PreferredColorScheme::kDark;
  113. web_prefs->force_dark_mode_enabled = false;
  114. break;
  115. }
  116. // Blink's behavior is that if the preferred color scheme matches the
  117. // supported color scheme, then force dark will be disabled, otherwise
  118. // the preferred color scheme will be reset to 'light'. Therefore
  119. // when enabling force dark, we also set the preferred color scheme to
  120. // dark so that dark themed content will be preferred over force
  121. // darkening.
  122. case AwSettings::ForceDarkBehavior::PREFER_MEDIA_QUERY_OVER_FORCE_DARK: {
  123. web_prefs->preferred_color_scheme =
  124. blink::mojom::PreferredColorScheme::kDark;
  125. web_prefs->force_dark_mode_enabled = true;
  126. break;
  127. }
  128. }
  129. } else if (prefers_dark_from_theme_) {
  130. web_prefs->preferred_color_scheme =
  131. blink::mojom::PreferredColorScheme::kDark;
  132. if (base::FeatureList::IsEnabled(
  133. android_webview::features::kWebViewForceDarkModeMatchTheme)) {
  134. web_prefs->force_dark_mode_enabled = true;
  135. is_force_dark_applied_ = true;
  136. }
  137. } else {
  138. web_prefs->preferred_color_scheme =
  139. blink::mojom::PreferredColorScheme::kLight;
  140. web_prefs->force_dark_mode_enabled = false;
  141. }
  142. }
  143. bool AwDarkMode::IsAppUsingDarkTheme() {
  144. JNIEnv* env = base::android::AttachCurrentThread();
  145. ScopedJavaLocalRef<jobject> scoped_obj = jobj_.get(env);
  146. if (!scoped_obj)
  147. return false;
  148. return Java_AwDarkMode_isAppUsingDarkTheme(env, scoped_obj);
  149. }
  150. void AwDarkMode::DetachFromJavaObject(JNIEnv* env,
  151. const JavaParamRef<jobject>& jcaller) {
  152. jobj_.reset();
  153. }
  154. void AwDarkMode::NavigationEntryCommitted(
  155. const content::LoadCommittedDetails& load_details) {
  156. if (!load_details.is_main_frame)
  157. return;
  158. UMA_HISTOGRAM_BOOLEAN("Android.WebView.DarkMode.PrefersDarkFromTheme",
  159. prefers_dark_from_theme_);
  160. }
  161. void AwDarkMode::InferredColorSchemeUpdated(
  162. absl::optional<blink::mojom::PreferredColorScheme> color_scheme) {
  163. if (prefers_dark_from_theme_ && color_scheme.has_value()) {
  164. UMA_HISTOGRAM_BOOLEAN(
  165. "Android.WebView.DarkMode.PageDarkenedAccordingToAppTheme",
  166. color_scheme.value() == blink::mojom::PreferredColorScheme::kDark);
  167. }
  168. }
  169. } // namespace android_webview