PreferenceSheet.cpp (10611B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "PreferenceSheet.h" 8 9 #include "MainThreadUtils.h" 10 #include "ServoCSSParser.h" 11 #include "mozilla/Encoding.h" 12 #include "mozilla/LookAndFeel.h" 13 #include "mozilla/Preferences.h" 14 #include "mozilla/ServoBindings.h" 15 #include "mozilla/StaticPrefs_browser.h" 16 #include "mozilla/StaticPrefs_layout.h" 17 #include "mozilla/StaticPrefs_ui.h" 18 #include "mozilla/StaticPrefs_widget.h" 19 #include "mozilla/dom/Document.h" 20 #include "mozilla/glean/AccessibleMetrics.h" 21 #include "nsContentUtils.h" 22 23 namespace mozilla { 24 25 using dom::Document; 26 27 bool PreferenceSheet::sInitialized; 28 PreferenceSheet::Prefs PreferenceSheet::sContentPrefs; 29 PreferenceSheet::Prefs PreferenceSheet::sChromePrefs; 30 PreferenceSheet::Prefs PreferenceSheet::sPrintPrefs; 31 32 static void GetColor(const char* aPrefName, ColorScheme aColorScheme, 33 nscolor& aColor) { 34 nsAutoCString darkPrefName; 35 if (aColorScheme == ColorScheme::Dark) { 36 darkPrefName.Append(aPrefName); 37 darkPrefName.AppendLiteral(".dark"); 38 aPrefName = darkPrefName.get(); 39 } 40 41 nsAutoCString value; 42 Preferences::GetCString(aPrefName, value); 43 if (value.IsEmpty() || Encoding::UTF8ValidUpTo(value) != value.Length()) { 44 return; 45 } 46 nscolor result; 47 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), value, &result)) { 48 return; 49 } 50 aColor = result; 51 } 52 53 auto PreferenceSheet::PrefsKindFor(const Document& aDoc) -> PrefsKind { 54 if (aDoc.IsInChromeDocShell()) { 55 return PrefsKind::Chrome; 56 } 57 58 if (aDoc.IsBeingUsedAsImage() && aDoc.ChromeRulesEnabled()) { 59 return PrefsKind::Chrome; 60 } 61 62 if (aDoc.IsStaticDocument()) { 63 return PrefsKind::Print; 64 } 65 66 return PrefsKind::Content; 67 } 68 69 static bool UseStandinsForNativeColors() { 70 return nsContentUtils::ShouldResistFingerprinting( 71 "we want to have consistent colors across the browser if RFP is " 72 "enabled, so we check the global preference" 73 "not excluding chrome browsers or webpages, so we call the legacy " 74 "RFP function to prevent that", 75 RFPTarget::UseStandinsForNativeColors) || 76 StaticPrefs::ui_use_standins_for_native_colors(); 77 } 78 79 void PreferenceSheet::Prefs::LoadColors(bool aIsLight) { 80 auto& colors = aIsLight ? mLightColors : mDarkColors; 81 82 if (!aIsLight) { 83 // Initialize the dark-color-scheme foreground/background colors as being 84 // the reverse of these members' default values, for ~reasonable fallback if 85 // the user configures broken pref values. 86 std::swap(colors.mDefault, colors.mDefaultBackground); 87 } 88 89 const auto scheme = aIsLight ? ColorScheme::Light : ColorScheme::Dark; 90 using ColorID = LookAndFeel::ColorID; 91 92 if (!mIsChrome && (mUseDocumentColors || mUseStandins)) { 93 // Tab content not in HCM, or we need to use standins. 94 auto GetStandinColor = [&scheme](ColorID aColorID, nscolor& aColor) { 95 aColor = LookAndFeel::Color(aColorID, scheme, 96 LookAndFeel::UseStandins::Yes, aColor); 97 }; 98 99 GetStandinColor(ColorID::Windowtext, colors.mDefault); 100 GetStandinColor(ColorID::Window, colors.mDefaultBackground); 101 GetStandinColor(ColorID::Linktext, colors.mLink); 102 GetStandinColor(ColorID::Visitedtext, colors.mVisitedLink); 103 GetStandinColor(ColorID::Activetext, colors.mActiveLink); 104 } else if (!mIsChrome && mUsePrefColors) { 105 // Tab content with explicit browser HCM, use our prefs for colors. 106 GetColor("browser.display.background_color", scheme, 107 colors.mDefaultBackground); 108 GetColor("browser.display.foreground_color", scheme, colors.mDefault); 109 GetColor("browser.anchor_color", scheme, colors.mLink); 110 GetColor("browser.active_color", scheme, colors.mActiveLink); 111 GetColor("browser.visited_color", scheme, colors.mVisitedLink); 112 } else { 113 // Browser UI or OS HCM, use system colors. 114 auto GetSystemColor = [&scheme](ColorID aColorID, nscolor& aColor) { 115 aColor = LookAndFeel::Color(aColorID, scheme, 116 LookAndFeel::UseStandins::No, aColor); 117 }; 118 119 GetSystemColor(ColorID::Windowtext, colors.mDefault); 120 GetSystemColor(ColorID::Window, colors.mDefaultBackground); 121 GetSystemColor(ColorID::Linktext, colors.mLink); 122 GetSystemColor(ColorID::Visitedtext, colors.mVisitedLink); 123 GetSystemColor(ColorID::Activetext, colors.mActiveLink); 124 } 125 126 // Wherever we got the default background color from, ensure it is opaque. 127 colors.mDefaultBackground = 128 NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF), colors.mDefaultBackground); 129 } 130 131 auto PreferenceSheet::ColorSchemeSettingForChrome() 132 -> ChromeColorSchemeSetting { 133 switch (StaticPrefs::browser_theme_toolbar_theme()) { 134 case 0: // Dark 135 return ChromeColorSchemeSetting::Dark; 136 case 1: // Light 137 return ChromeColorSchemeSetting::Light; 138 default: 139 return ChromeColorSchemeSetting::System; 140 } 141 } 142 143 ColorScheme PreferenceSheet::ThemeDerivedColorSchemeForContent() { 144 switch (StaticPrefs::browser_theme_content_theme()) { 145 case 0: // Dark 146 return ColorScheme::Dark; 147 case 1: // Light 148 return ColorScheme::Light; 149 default: 150 return LookAndFeel::SystemColorScheme(); 151 } 152 } 153 154 void PreferenceSheet::Prefs::Load(bool aIsChrome) { 155 *this = {}; 156 157 mIsChrome = aIsChrome; 158 mUseAccessibilityTheme = 159 LookAndFeel::GetInt(LookAndFeel::IntID::UseAccessibilityTheme); 160 // Chrome documents always use system colors, not stand-ins, not forced, etc. 161 if (!aIsChrome) { 162 switch (StaticPrefs::browser_display_document_color_use()) { 163 case 1: 164 // Never High Contrast 165 mUsePrefColors = false; 166 mUseDocumentColors = true; 167 break; 168 case 2: 169 // Always High Contrast 170 mUsePrefColors = true; 171 mUseDocumentColors = false; 172 break; 173 default: 174 // Only with OS HCM 175 mUsePrefColors = false; 176 mUseDocumentColors = !mUseAccessibilityTheme; 177 break; 178 } 179 mUseStandins = UseStandinsForNativeColors(); 180 } 181 182 LoadColors(true); 183 LoadColors(false); 184 185 // When forcing the pref colors, we need to forcibly use the light color-set, 186 // as those are the colors exposed to the user in the colors dialog. 187 mMustUseLightColorSet = mUsePrefColors && !mUseDocumentColors; 188 #ifdef XP_WIN 189 if (mUseAccessibilityTheme && (mIsChrome || !mUseDocumentColors)) { 190 // Windows overrides the light colors with the HCM colors when HCM is 191 // active, so make sure to always use the light system colors in that case, 192 // and also make sure that we always use the light color set for the same 193 // reason. 194 mMustUseLightSystemColors = mMustUseLightColorSet = true; 195 } 196 #endif 197 198 mColorScheme = [&] { 199 if (aIsChrome) { 200 switch (ColorSchemeSettingForChrome()) { 201 case ChromeColorSchemeSetting::Light: 202 return ColorScheme::Light; 203 case ChromeColorSchemeSetting::Dark: 204 return ColorScheme::Dark; 205 case ChromeColorSchemeSetting::System: 206 break; 207 } 208 return LookAndFeel::SystemColorScheme(); 209 } 210 if (mMustUseLightColorSet) { 211 // When forcing colors in a way such as color-scheme isn't respected, we 212 // compute a preference based on the darkness of 213 // our background. 214 return LookAndFeel::IsDarkColor(mLightColors.mDefaultBackground) 215 ? ColorScheme::Dark 216 : ColorScheme::Light; 217 } 218 switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) { 219 case 0: 220 return ColorScheme::Dark; 221 case 1: 222 return ColorScheme::Light; 223 default: 224 return ThemeDerivedColorSchemeForContent(); 225 } 226 }(); 227 } 228 229 void PreferenceSheet::Initialize() { 230 MOZ_ASSERT(NS_IsMainThread()); 231 MOZ_ASSERT(!sInitialized); 232 233 sInitialized = true; 234 235 sContentPrefs.Load(false); 236 sChromePrefs.Load(true); 237 sPrintPrefs = sContentPrefs; 238 { 239 // For printing, we always use a preferred-light color scheme. 240 sPrintPrefs.mColorScheme = ColorScheme::Light; 241 if (!sPrintPrefs.mUseDocumentColors) { 242 // When overriding document colors, we ignore the `color-scheme` property, 243 // but we still don't want to use the system colors (which might be dark, 244 // despite having made it into mLightColors), because it both wastes ink 245 // and it might interact poorly with the color adjustments we do while 246 // printing. 247 // 248 // So we override the light colors with our hardcoded default colors, and 249 // force the use of stand-ins. 250 sPrintPrefs.mLightColors = Prefs().mLightColors; 251 sPrintPrefs.mUseStandins = true; 252 } 253 } 254 255 // Telemetry for these preferences is only collected on the parent process. 256 if (!XRE_IsParentProcess()) { 257 return; 258 } 259 260 glean::a11y::ThemeLabel gleanLabel; 261 switch (StaticPrefs::browser_display_document_color_use()) { 262 case 1: 263 gleanLabel = glean::a11y::ThemeLabel::eAlways; 264 break; 265 case 2: 266 gleanLabel = glean::a11y::ThemeLabel::eNever; 267 break; 268 default: 269 gleanLabel = glean::a11y::ThemeLabel::eDefault; 270 break; 271 } 272 273 glean::a11y::theme.EnumGet(gleanLabel) 274 .Set(sContentPrefs.mUseAccessibilityTheme); 275 if (!sContentPrefs.mUseDocumentColors) { 276 // If a user has chosen to override doc colors through OS HCM or our HCM, 277 // we should log the user's current foreground (text) color and background 278 // color. Note, the document color use pref is the inverse of the HCM 279 // dropdown option in preferences. 280 // 281 // Note that we only look at light colors because that's the color set we 282 // use when forcing colors (since color-scheme is ignored when colors are 283 // forced). 284 // 285 // The light color set is the one that potentially contains the Windows HCM 286 // theme color/background (if we're using system colors and the user is 287 // using a High Contrast theme), and also the colors that as of today we 288 // allow setting in about:preferences. 289 glean::a11y::hcm_foreground.Set(sContentPrefs.mLightColors.mDefault); 290 glean::a11y::hcm_background.Set( 291 sContentPrefs.mLightColors.mDefaultBackground); 292 } 293 294 glean::a11y::backplate.Set(StaticPrefs::browser_display_permit_backplate()); 295 glean::a11y::always_underline_links.Set( 296 StaticPrefs::layout_css_always_underline_links()); 297 } 298 299 } // namespace mozilla