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 "ServoCSSParser.h"
10 #include "MainThreadUtils.h"
11 #include "mozilla/Encoding.h"
12 #include "mozilla/Preferences.h"
13 #include "mozilla/StaticPrefs_browser.h"
14 #include "mozilla/StaticPrefs_devtools.h"
15 #include "mozilla/StaticPrefs_widget.h"
16 #include "mozilla/StaticPrefs_ui.h"
17 #include "mozilla/Telemetry.h"
18 #include "mozilla/LookAndFeel.h"
19 #include "mozilla/ServoBindings.h"
20 #include "mozilla/dom/Document.h"
21 #include "nsContentUtils.h"
22
23 #define AVG2(a, b) (((a) + (b) + 1) >> 1)
24
25 namespace mozilla {
26
27 using dom::Document;
28
29 bool PreferenceSheet::sInitialized;
30 PreferenceSheet::Prefs PreferenceSheet::sContentPrefs;
31 PreferenceSheet::Prefs PreferenceSheet::sChromePrefs;
32 PreferenceSheet::Prefs PreferenceSheet::sPrintPrefs;
33
GetColor(const char * aPrefName,ColorScheme aColorScheme,nscolor & aColor)34 static void GetColor(const char* aPrefName, ColorScheme aColorScheme,
35 nscolor& aColor) {
36 nsAutoCString darkPrefName;
37 if (aColorScheme == ColorScheme::Dark) {
38 darkPrefName.Append(aPrefName);
39 darkPrefName.AppendLiteral(".dark");
40 aPrefName = darkPrefName.get();
41 }
42
43 nsAutoCString value;
44 Preferences::GetCString(aPrefName, value);
45 if (value.IsEmpty() || Encoding::UTF8ValidUpTo(value) != value.Length()) {
46 return;
47 }
48 nscolor result;
49 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), value, &result)) {
50 return;
51 }
52 aColor = result;
53 }
54
PrefsKindFor(const Document & aDoc)55 auto PreferenceSheet::PrefsKindFor(const Document& aDoc) -> PrefsKind {
56 // DevTools documents run in a content frame but should temporarily use
57 // chrome preferences, in particular to avoid applying High Contrast mode
58 // colors. See Bug 1575766.
59 if (aDoc.IsDevToolsDocument() &&
60 StaticPrefs::devtools_toolbox_force_chrome_prefs()) {
61 return PrefsKind::Chrome;
62 }
63
64 if (aDoc.IsInChromeDocShell()) {
65 return PrefsKind::Chrome;
66 }
67
68 if (aDoc.IsBeingUsedAsImage() && aDoc.IsDocumentURISchemeChrome()) {
69 return PrefsKind::Chrome;
70 }
71
72 if (aDoc.IsStaticDocument()) {
73 return PrefsKind::Print;
74 }
75
76 return PrefsKind::Content;
77 }
78
UseAccessibilityTheme(bool aIsChrome)79 static bool UseAccessibilityTheme(bool aIsChrome) {
80 return !aIsChrome &&
81 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseAccessibilityTheme, 0);
82 }
83
UseDocumentColors(bool aIsChrome,bool aUseAcccessibilityTheme)84 static bool UseDocumentColors(bool aIsChrome, bool aUseAcccessibilityTheme) {
85 switch (StaticPrefs::browser_display_document_color_use()) {
86 case 1:
87 return true;
88 case 2:
89 return aIsChrome;
90 default:
91 return !aUseAcccessibilityTheme;
92 }
93 }
94
LoadColors(bool aIsLight)95 void PreferenceSheet::Prefs::LoadColors(bool aIsLight) {
96 auto& colors = aIsLight ? mLightColors : mDarkColors;
97
98 if (!aIsLight) {
99 // Initialize the dark-color-scheme foreground/background colors as being
100 // the reverse of these members' default values, for ~reasonable fallback if
101 // the user configures broken pref values.
102 std::swap(colors.mDefault, colors.mDefaultBackground);
103 }
104
105 const bool useStandins = nsContentUtils::UseStandinsForNativeColors();
106 // Users should be able to choose to use system colors or preferred colors
107 // when HCM is disabled, and in both OS-level HCM and FF-level HCM.
108 // To make this possible, we don't consider UseDocumentColors and
109 // mUseAccessibilityTheme when computing the following bool.
110 const bool usePrefColors = !useStandins && !mIsChrome &&
111 !StaticPrefs::browser_display_use_system_colors();
112 const auto scheme = aIsLight ? ColorScheme::Light : ColorScheme::Dark;
113
114 // Link colors might be provided by the OS, but they might not be. If they are
115 // not, then fall back to the pref colors.
116 //
117 // In particular, we don't query active link color to the OS.
118 GetColor("browser.anchor_color", scheme, colors.mLink);
119 GetColor("browser.active_color", scheme, colors.mActiveLink);
120 GetColor("browser.visited_color", scheme, colors.mVisitedLink);
121
122 if (usePrefColors) {
123 GetColor("browser.display.background_color", scheme,
124 colors.mDefaultBackground);
125 GetColor("browser.display.foreground_color", scheme, colors.mDefault);
126 } else {
127 using ColorID = LookAndFeel::ColorID;
128 const auto standins = LookAndFeel::UseStandins(useStandins);
129 colors.mDefault = LookAndFeel::Color(ColorID::Windowtext, scheme, standins,
130 colors.mDefault);
131 colors.mDefaultBackground = LookAndFeel::Color(
132 ColorID::Window, scheme, standins, colors.mDefaultBackground);
133 colors.mLink = LookAndFeel::Color(ColorID::MozNativehyperlinktext, scheme,
134 standins, colors.mLink);
135
136 if (auto color = LookAndFeel::GetColor(
137 ColorID::MozNativevisitedhyperlinktext, scheme, standins)) {
138 // If the system provides a visited link color, we should use it.
139 colors.mVisitedLink = *color;
140 } else if (mUseAccessibilityTheme) {
141 // The fallback visited link color on HCM (if the system doesn't provide
142 // one) is produced by preserving the foreground's green and averaging the
143 // foreground and background for the red and blue. This is how IE and
144 // Edge do it too.
145 colors.mVisitedLink = NS_RGB(
146 AVG2(NS_GET_R(colors.mDefault), NS_GET_R(colors.mDefaultBackground)),
147 NS_GET_G(colors.mDefault),
148 AVG2(NS_GET_B(colors.mDefault), NS_GET_B(colors.mDefaultBackground)));
149 } else {
150 // Otherwise we keep the default visited link color
151 }
152
153 if (mUseAccessibilityTheme) {
154 colors.mActiveLink = colors.mLink;
155 }
156 }
157
158 {
159 // These two are not color-scheme dependent, as we don't rebuild the
160 // preference sheet based on effective color scheme.
161 GetColor("browser.display.focus_text_color", ColorScheme::Light,
162 colors.mFocusText);
163 GetColor("browser.display.focus_background_color", ColorScheme::Light,
164 colors.mFocusBackground);
165 }
166
167 // Wherever we got the default background color from, ensure it is opaque.
168 colors.mDefaultBackground =
169 NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF), colors.mDefaultBackground);
170 }
171
NonNativeThemeShouldBeHighContrast() const172 bool PreferenceSheet::Prefs::NonNativeThemeShouldBeHighContrast() const {
173 // We only do that if we are overriding the document colors. Otherwise it
174 // causes issues when pages only override some of the system colors,
175 // specially in dark themes mode.
176 return StaticPrefs::widget_non_native_theme_always_high_contrast() ||
177 !mUseDocumentColors;
178 }
179
Load(bool aIsChrome)180 void PreferenceSheet::Prefs::Load(bool aIsChrome) {
181 *this = {};
182
183 mIsChrome = aIsChrome;
184 mUseAccessibilityTheme = UseAccessibilityTheme(aIsChrome);
185
186 LoadColors(true);
187 LoadColors(false);
188 mUseDocumentColors = UseDocumentColors(aIsChrome, mUseAccessibilityTheme);
189 }
190
Initialize()191 void PreferenceSheet::Initialize() {
192 MOZ_ASSERT(NS_IsMainThread());
193 MOZ_ASSERT(!sInitialized);
194
195 sInitialized = true;
196
197 sContentPrefs.Load(false);
198 sChromePrefs.Load(true);
199 sPrintPrefs = sContentPrefs;
200 if (!sPrintPrefs.mUseDocumentColors) {
201 // For printing, we always use a preferred-light color scheme.
202 //
203 // When overriding document colors, we ignore the `color-scheme` property,
204 // but we still don't want to use the system colors (which might be dark,
205 // despite having made it into mLightColors), because it both wastes ink and
206 // it might interact poorly with the color adjustments we do while printing.
207 //
208 // So we override the light colors with our hardcoded default colors.
209 sPrintPrefs.mLightColors = Prefs().mLightColors;
210 }
211
212 nsAutoString useDocumentColorPref;
213 switch (StaticPrefs::browser_display_document_color_use()) {
214 case 1:
215 useDocumentColorPref.AssignLiteral("always");
216 break;
217 case 2:
218 useDocumentColorPref.AssignLiteral("never");
219 break;
220 default:
221 useDocumentColorPref.AssignLiteral("default");
222 break;
223 }
224
225 Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_THEME, useDocumentColorPref,
226 sContentPrefs.mUseAccessibilityTheme);
227 if (!sContentPrefs.mUseDocumentColors) {
228 // If a user has chosen to override doc colors through OS HCM or our HCM,
229 // we should log the user's current foreground (text) color and background
230 // color. Note, the document color use pref is the inverse of the HCM
231 // dropdown option in preferences.
232 //
233 // Note that we only look at light colors because that's the color set we
234 // use when forcing colors (since color-scheme is ignored when colors are
235 // forced).
236 //
237 // The light color set is the one that potentially contains the Windows HCM
238 // theme color/background (if we're using system colors and the user is
239 // using a High Contrast theme), and also the colors that as of today we
240 // allow setting in about:preferences.
241 Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_HCM_FOREGROUND,
242 sContentPrefs.mLightColors.mDefault);
243 Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_HCM_BACKGROUND,
244 sContentPrefs.mLightColors.mDefaultBackground);
245 }
246
247 Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_BACKPLATE,
248 StaticPrefs::browser_display_permit_backplate());
249 }
250
AffectedByPref(const nsACString & aPref)251 bool PreferenceSheet::AffectedByPref(const nsACString& aPref) {
252 const char* prefNames[] = {
253 StaticPrefs::GetPrefName_devtools_toolbox_force_chrome_prefs(),
254 StaticPrefs::GetPrefName_privacy_resistFingerprinting(),
255 StaticPrefs::GetPrefName_ui_use_standins_for_native_colors(),
256 "browser.anchor_color",
257 "browser.active_color",
258 "browser.visited_color",
259 };
260
261 if (StringBeginsWith(aPref, "browser.display."_ns)) {
262 return true;
263 }
264
265 for (const char* pref : prefNames) {
266 if (aPref.Equals(pref)) {
267 return true;
268 }
269 }
270
271 return false;
272 }
273
274 } // namespace mozilla
275
276 #undef AVG2
277