summaryrefslogtreecommitdiffstats
path: root/layout/style/PreferenceSheet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/PreferenceSheet.cpp')
-rw-r--r--layout/style/PreferenceSheet.cpp341
1 files changed, 341 insertions, 0 deletions
diff --git a/layout/style/PreferenceSheet.cpp b/layout/style/PreferenceSheet.cpp
new file mode 100644
index 0000000000..a206209885
--- /dev/null
+++ b/layout/style/PreferenceSheet.cpp
@@ -0,0 +1,341 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "PreferenceSheet.h"
+
+#include "ServoCSSParser.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Encoding.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_browser.h"
+#include "mozilla/StaticPrefs_devtools.h"
+#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/StaticPrefs_widget.h"
+#include "mozilla/StaticPrefs_ui.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/ServoBindings.h"
+#include "mozilla/dom/Document.h"
+#include "nsContentUtils.h"
+
+#define AVG2(a, b) (((a) + (b) + 1) >> 1)
+
+namespace mozilla {
+
+using dom::Document;
+
+bool PreferenceSheet::sInitialized;
+PreferenceSheet::Prefs PreferenceSheet::sContentPrefs;
+PreferenceSheet::Prefs PreferenceSheet::sChromePrefs;
+PreferenceSheet::Prefs PreferenceSheet::sPrintPrefs;
+
+static void GetColor(const char* aPrefName, ColorScheme aColorScheme,
+ nscolor& aColor) {
+ nsAutoCString darkPrefName;
+ if (aColorScheme == ColorScheme::Dark) {
+ darkPrefName.Append(aPrefName);
+ darkPrefName.AppendLiteral(".dark");
+ aPrefName = darkPrefName.get();
+ }
+
+ nsAutoCString value;
+ Preferences::GetCString(aPrefName, value);
+ if (value.IsEmpty() || Encoding::UTF8ValidUpTo(value) != value.Length()) {
+ return;
+ }
+ nscolor result;
+ if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), value, &result)) {
+ return;
+ }
+ aColor = result;
+}
+
+auto PreferenceSheet::PrefsKindFor(const Document& aDoc) -> PrefsKind {
+ if (aDoc.IsInChromeDocShell()) {
+ return PrefsKind::Chrome;
+ }
+
+ if (aDoc.IsBeingUsedAsImage() && aDoc.ChromeRulesEnabled()) {
+ return PrefsKind::Chrome;
+ }
+
+ if (aDoc.IsStaticDocument()) {
+ return PrefsKind::Print;
+ }
+
+ return PrefsKind::Content;
+}
+
+static bool UseDocumentColors(bool aUseAcccessibilityTheme) {
+ switch (StaticPrefs::browser_display_document_color_use()) {
+ case 1:
+ return true;
+ case 2:
+ return false;
+ default:
+ return !aUseAcccessibilityTheme;
+ }
+}
+
+static bool UseStandinsForNativeColors() {
+ return nsContentUtils::ShouldResistFingerprinting(
+ "we want to have consistent colors across the browser if RFP is "
+ "enabled, so we check the global preference"
+ "not excluding chrome browsers or webpages, so we call the legacy "
+ "RFP function to prevent that",
+ RFPTarget::UseStandinsForNativeColors) ||
+ StaticPrefs::ui_use_standins_for_native_colors();
+}
+
+void PreferenceSheet::Prefs::LoadColors(bool aIsLight) {
+ auto& colors = aIsLight ? mLightColors : mDarkColors;
+
+ if (!aIsLight) {
+ // Initialize the dark-color-scheme foreground/background colors as being
+ // the reverse of these members' default values, for ~reasonable fallback if
+ // the user configures broken pref values.
+ std::swap(colors.mDefault, colors.mDefaultBackground);
+ }
+
+ const auto scheme = aIsLight ? ColorScheme::Light : ColorScheme::Dark;
+
+ // Link colors might be provided by the OS, but they might not be. If they are
+ // not, then fall back to the pref colors.
+ //
+ // In particular, we don't query active link color to the OS.
+ GetColor("browser.anchor_color", scheme, colors.mLink);
+ GetColor("browser.active_color", scheme, colors.mActiveLink);
+ GetColor("browser.visited_color", scheme, colors.mVisitedLink);
+
+ // Historically we've given more weight to the "use standins" setting than the
+ // "use system colors" one. In practice most users don't use standins because
+ // it's hidden behind prefs.
+ if (mUsePrefColors && !mUseStandins) {
+ GetColor("browser.display.background_color", scheme,
+ colors.mDefaultBackground);
+ GetColor("browser.display.foreground_color", scheme, colors.mDefault);
+ } else {
+ using ColorID = LookAndFeel::ColorID;
+ const auto standins = LookAndFeel::UseStandins(mUseStandins);
+ colors.mDefault = LookAndFeel::Color(ColorID::Windowtext, scheme, standins,
+ colors.mDefault);
+ colors.mDefaultBackground = LookAndFeel::Color(
+ ColorID::Window, scheme, standins, colors.mDefaultBackground);
+ colors.mLink = LookAndFeel::Color(ColorID::MozNativehyperlinktext, scheme,
+ standins, colors.mLink);
+
+ if (auto color = LookAndFeel::GetColor(
+ ColorID::MozNativevisitedhyperlinktext, scheme, standins)) {
+ // If the system provides a visited link color, we should use it.
+ colors.mVisitedLink = *color;
+ } else if (mUseAccessibilityTheme) {
+ // The fallback visited link color on HCM (if the system doesn't provide
+ // one) is produced by preserving the foreground's green and averaging the
+ // foreground and background for the red and blue. This is how IE and
+ // Edge do it too.
+ colors.mVisitedLink = NS_RGB(
+ AVG2(NS_GET_R(colors.mDefault), NS_GET_R(colors.mDefaultBackground)),
+ NS_GET_G(colors.mDefault),
+ AVG2(NS_GET_B(colors.mDefault), NS_GET_B(colors.mDefaultBackground)));
+ } else {
+ // Otherwise we keep the default visited link color
+ }
+
+ if (mUseAccessibilityTheme) {
+ colors.mActiveLink = colors.mLink;
+ }
+ }
+
+ // Wherever we got the default background color from, ensure it is opaque.
+ colors.mDefaultBackground =
+ NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF), colors.mDefaultBackground);
+}
+
+bool PreferenceSheet::Prefs::NonNativeThemeShouldBeHighContrast() const {
+ // We only do that if we are overriding the document colors. Otherwise it
+ // causes issues when pages only override some of the system colors,
+ // specially in dark themes mode.
+ return StaticPrefs::widget_non_native_theme_always_high_contrast() ||
+ !mUseDocumentColors;
+}
+
+auto PreferenceSheet::ColorSchemeSettingForChrome()
+ -> ChromeColorSchemeSetting {
+ switch (StaticPrefs::browser_theme_toolbar_theme()) {
+ case 0: // Dark
+ return ChromeColorSchemeSetting::Dark;
+ case 1: // Light
+ return ChromeColorSchemeSetting::Light;
+ default:
+ return ChromeColorSchemeSetting::System;
+ }
+}
+
+ColorScheme PreferenceSheet::ThemeDerivedColorSchemeForContent() {
+ switch (StaticPrefs::browser_theme_content_theme()) {
+ case 0: // Dark
+ return ColorScheme::Dark;
+ case 1: // Light
+ return ColorScheme::Light;
+ default:
+ return LookAndFeel::SystemColorScheme();
+ }
+}
+
+void PreferenceSheet::Prefs::Load(bool aIsChrome) {
+ *this = {};
+
+ mIsChrome = aIsChrome;
+ mUseAccessibilityTheme =
+ LookAndFeel::GetInt(LookAndFeel::IntID::UseAccessibilityTheme);
+ // Chrome documents always use system colors, not stand-ins, not forced, etc.
+ if (!aIsChrome) {
+ mUseDocumentColors = UseDocumentColors(mUseAccessibilityTheme);
+ mUsePrefColors = !StaticPrefs::browser_display_use_system_colors();
+ mUseStandins = UseStandinsForNativeColors();
+ }
+
+ LoadColors(true);
+ LoadColors(false);
+
+ // When forcing the pref colors, we need to forcibly use the light color-set,
+ // as those are the colors exposed to the user in the colors dialog.
+ mMustUseLightColorSet = mUsePrefColors && !mUseDocumentColors;
+#ifdef XP_WIN
+ if (mUseAccessibilityTheme) {
+ // Windows overrides the light colors with the HCM colors when HCM is
+ // active, so make sure to always use the light system colors in that case,
+ // and also make sure that we always use the light color set for the same
+ // reason.
+ mMustUseLightSystemColors = mMustUseLightColorSet = true;
+ }
+#endif
+
+ mColorScheme = [&] {
+ if (aIsChrome) {
+ switch (ColorSchemeSettingForChrome()) {
+ case ChromeColorSchemeSetting::Light:
+ return ColorScheme::Light;
+ case ChromeColorSchemeSetting::Dark:
+ return ColorScheme::Dark;
+ case ChromeColorSchemeSetting::System:
+ break;
+ }
+ return LookAndFeel::SystemColorScheme();
+ }
+ if (mMustUseLightColorSet) {
+ // When forcing colors in a way such as color-scheme isn't respected, we
+ // compute a preference based on the darkness of
+ // our background.
+ return LookAndFeel::IsDarkColor(mLightColors.mDefaultBackground)
+ ? ColorScheme::Dark
+ : ColorScheme::Light;
+ }
+ switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
+ case 0:
+ return ColorScheme::Dark;
+ case 1:
+ return ColorScheme::Light;
+ default:
+ return ThemeDerivedColorSchemeForContent();
+ }
+ }();
+}
+
+void PreferenceSheet::Initialize() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sInitialized);
+
+ sInitialized = true;
+
+ sContentPrefs.Load(false);
+ sChromePrefs.Load(true);
+ sPrintPrefs = sContentPrefs;
+ {
+ // For printing, we always use a preferred-light color scheme.
+ sPrintPrefs.mColorScheme = ColorScheme::Light;
+ if (!sPrintPrefs.mUseDocumentColors) {
+ // When overriding document colors, we ignore the `color-scheme` property,
+ // but we still don't want to use the system colors (which might be dark,
+ // despite having made it into mLightColors), because it both wastes ink
+ // and it might interact poorly with the color adjustments we do while
+ // printing.
+ //
+ // So we override the light colors with our hardcoded default colors, and
+ // force the use of stand-ins.
+ sPrintPrefs.mLightColors = Prefs().mLightColors;
+ sPrintPrefs.mUseStandins = true;
+ }
+ }
+
+ nsAutoString useDocumentColorPref;
+ switch (StaticPrefs::browser_display_document_color_use()) {
+ case 1:
+ useDocumentColorPref.AssignLiteral("always");
+ break;
+ case 2:
+ useDocumentColorPref.AssignLiteral("never");
+ break;
+ default:
+ useDocumentColorPref.AssignLiteral("default");
+ break;
+ }
+
+ Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_THEME, useDocumentColorPref,
+ sContentPrefs.mUseAccessibilityTheme);
+ if (!sContentPrefs.mUseDocumentColors) {
+ // If a user has chosen to override doc colors through OS HCM or our HCM,
+ // we should log the user's current foreground (text) color and background
+ // color. Note, the document color use pref is the inverse of the HCM
+ // dropdown option in preferences.
+ //
+ // Note that we only look at light colors because that's the color set we
+ // use when forcing colors (since color-scheme is ignored when colors are
+ // forced).
+ //
+ // The light color set is the one that potentially contains the Windows HCM
+ // theme color/background (if we're using system colors and the user is
+ // using a High Contrast theme), and also the colors that as of today we
+ // allow setting in about:preferences.
+ Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_HCM_FOREGROUND,
+ sContentPrefs.mLightColors.mDefault);
+ Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_HCM_BACKGROUND,
+ sContentPrefs.mLightColors.mDefaultBackground);
+ }
+
+ Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_BACKPLATE,
+ StaticPrefs::browser_display_permit_backplate());
+ Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_USE_SYSTEM_COLORS,
+ StaticPrefs::browser_display_use_system_colors());
+ Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_ALWAYS_UNDERLINE_LINKS,
+ StaticPrefs::layout_css_always_underline_links());
+}
+
+bool PreferenceSheet::AffectedByPref(const nsACString& aPref) {
+ const char* prefNames[] = {
+ StaticPrefs::GetPrefName_privacy_resistFingerprinting(),
+ StaticPrefs::GetPrefName_ui_use_standins_for_native_colors(),
+ "browser.anchor_color",
+ "browser.active_color",
+ "browser.visited_color",
+ };
+
+ if (StringBeginsWith(aPref, "browser.display."_ns)) {
+ return true;
+ }
+
+ for (const char* pref : prefNames) {
+ if (aPref.Equals(pref)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace mozilla
+
+#undef AVG2