Skip to content

Commit

Permalink
Bug 1525107 - Make Canvas/CanvasText and Link colors color-scheme-awa…
Browse files Browse the repository at this point in the history
…re. r=dholbert

For that, add `.dark` version of the browser.display* prefs that control
the light version of these colors.

The default for background/foreground colors are taken from the
GenericDarkColors used in LookAndFeel.

The defaults for links are based on this discussion:

  whatwg/html#5426 (comment)

(So they effectively match Chrome).

Whether the dark colors should be exposed in about:preferences (like the
light colors are) is TBD.

With this patch, we pass all the tests in:

  /html/semantics/document-metadata/the-meta-element/color-scheme/

Use the colors to paint the default canvas background and the default
colors.

There are three "regressions", though they are really progressions: we
now render the reference as the test expects (before we rendered a light
canvas background even for the reference).

Apart of these iframe tests (which we should look into, I filed
https://bugzilla.mozilla.org/show_bug.cgi?id=1738380), there are three
remaining test failures.

Two of them are due to `color: initial` not changing based on the
color-scheme. Safari also fails these tests, and the thing they're
really testing is whether system colors are preserved at computed-value
time:

  w3c/csswg-drafts#3847

Regarding that change, I'm not so sure the trade-offs there are worth
it, as that not only complicates interpolation (we wouldn't be able to
use system colors in color-mix among others, see
w3c/csswg-drafts#5780) plus it changes
inheritance behavior in sorta unexpected ways, see:

  w3c/csswg-drafts#6773

Which I just filed because apparently no browser implements this
correctly. So for now will punt on those (keep matching Safari).

There's an svg-as-image test:

  https://searchfox.org/mozilla-central/rev/f8576fec48d866c5f988baaf1fa8d2f8cce2a82f/testing/web-platform/tests/css/css-color-adjust/rendering/dark-color-scheme/svg-as-image.html

Which isn't using the feature at all and I'm not sure why is it supposed
to pass (why prefers-color-scheme: dark is supposed to match that SVG
image). This test fails in all browsers apparently:

  https://wpt.fyi/results/css/css-color-adjust/rendering/dark-color-scheme/svg-as-image.html?label=master&label=experimental&aligned

I sent web-platform-tests/wpt#31407 to remove
it and hopefully get it reviewed by some Chromium folks.

Differential Revision: https://phabricator.services.mozilla.com/D129746
  • Loading branch information
emilio committed Oct 29, 2021
1 parent 2ef33cf commit 96a1a3f
Show file tree
Hide file tree
Showing 30 changed files with 216 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async function testMatchedSelectors(view, inspector) {
const numMatchedSelectors = propertyView.matchedSelectors.length;
is(
numMatchedSelectors,
6,
7,
"CssLogic returns the correct number of matched selectors for div"
);
is(
Expand Down
2 changes: 1 addition & 1 deletion devtools/server/tests/chrome/test_styles-applied.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
ok(applied[1].rule.parentStyleSheet.system, "Entry 1 should be a system style");
is(applied[1].rule.type, 1, "Entry 1 should be a rule style");

is(applied.length, 8, "Should have the expected number of rules.");
is(applied.length, 9, "Should have the expected number of rules.");
}).then(runNextTest));
});

Expand Down
4 changes: 4 additions & 0 deletions dom/base/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17621,6 +17621,10 @@ void Document::RemoveToplevelLoadingDocument(Document* aDoc) {
}
}

ColorScheme Document::DefaultColorScheme() const {
return LookAndFeel::ColorSchemeForStyle(*this, {GetColorSchemeBits()});
}

ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const {
if (aIgnoreRFP == IgnoreRFP::No &&
nsContentUtils::ShouldResistFingerprinting(this)) {
Expand Down
3 changes: 3 additions & 0 deletions dom/base/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -4022,6 +4022,9 @@ class Document : public nsINode,
// CSS prefers-color-scheme media feature for this document.
enum class IgnoreRFP { No, Yes };
ColorScheme PreferredColorScheme(IgnoreRFP = IgnoreRFP::No) const;
// Returns the initial color-scheme used for this document based on the
// color-scheme meta tag.
ColorScheme DefaultColorScheme() const;

static bool HasRecentlyStartedForegroundLoads();

Expand Down
42 changes: 21 additions & 21 deletions layout/base/PresShell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5293,29 +5293,29 @@ nscolor PresShell::GetDefaultBackgroundColorToDraw() {
return NS_RGB(255, 255, 255);
}

nscolor backgroundColor = mPresContext->DefaultBackgroundColor();
if (backgroundColor != NS_RGB(255, 255, 255)) {
// Return non-default color.
return backgroundColor;
}

// Use a dark background for top-level about:blank that is inaccessible to
// content JS.
//
// TODO(emilio): We should make this work for content-accessible documents
// based on the `<meta name=color-scheme>` meta tag.
Document* doc = GetDocument();
BrowsingContext* bc = doc->GetBrowsingContext();
if (bc && bc->IsTop() && !bc->HasOpener() && doc->GetDocumentURI() &&
NS_IsAboutBlank(doc->GetDocumentURI()) &&
doc->PreferredColorScheme(Document::IgnoreRFP::Yes) ==
ColorScheme::Dark) {
auto color = LookAndFeel::ColorID::WindowBackground;
return LookAndFeel::Color(color, ColorScheme::Dark,
LookAndFeel::ShouldUseStandins(*doc, color));
}
auto colorScheme = [&] {
// Use a dark background for top-level about:blank that is inaccessible to
// content JS.
{
BrowsingContext* bc = doc->GetBrowsingContext();
if (bc && bc->IsTop() && !bc->HasOpener() && doc->GetDocumentURI() &&
NS_IsAboutBlank(doc->GetDocumentURI())) {
return doc->PreferredColorScheme(Document::IgnoreRFP::Yes);
}
}
// Prefer the root color-scheme (since generally the default canvas
// background comes from the root element's background-color), and fall back
// to the default color-scheme if not available.
if (auto* frame = mFrameConstructor->GetRootElementStyleFrame()) {
return LookAndFeel::ColorSchemeForFrame(frame);
}
return doc->DefaultColorScheme();
}();

return backgroundColor;
return mPresContext->PrefSheetPrefs()
.ColorsFor(colorScheme)
.mDefaultBackground;
}

void PresShell::UpdateCanvasBackground() {
Expand Down
6 changes: 6 additions & 0 deletions layout/base/nsPresContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,12 @@ nsISupports* nsPresContext::GetContainerWeak() const {
return mDocument->GetDocShell();
}

nscolor nsPresContext::DefaultBackgroundColor() const {
return PrefSheetPrefs()
.ColorsFor(mDocument->DefaultColorScheme())
.mDefaultBackground;
}

nsDocShell* nsPresContext::GetDocShell() const {
return nsDocShell::Cast(mDocument->GetDocShell());
}
Expand Down
4 changes: 1 addition & 3 deletions layout/base/nsPresContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,7 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr {
const mozilla::PreferenceSheet::Prefs& PrefSheetPrefs() const {
return mozilla::PreferenceSheet::PrefsFor(*mDocument);
}
nscolor DefaultBackgroundColor() const {
return PrefSheetPrefs().mColors.mDefaultBackground;
}
nscolor DefaultBackgroundColor() const;

nsISupports* GetContainerWeak() const;

Expand Down
6 changes: 3 additions & 3 deletions layout/style/GeckoBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,8 +689,9 @@ bool Gecko_IsDocumentBody(const Element* aElement) {

nscolor Gecko_ComputeSystemColor(StyleSystemColor aColor, const Document* aDoc,
const StyleColorScheme* aStyle) {
const auto& colors = PreferenceSheet::PrefsFor(*aDoc).mColors;
// TODO: These should be color-scheme aware too.
auto colorScheme = LookAndFeel::ColorSchemeForStyle(*aDoc, aStyle->bits);

const auto& colors = PreferenceSheet::PrefsFor(*aDoc).ColorsFor(colorScheme);
switch (aColor) {
case StyleSystemColor::Canvastext:
return colors.mDefault;
Expand All @@ -706,7 +707,6 @@ nscolor Gecko_ComputeSystemColor(StyleSystemColor aColor, const Document* aDoc,
break;
}

auto colorScheme = LookAndFeel::ColorSchemeForStyle(*aDoc, aStyle->bits);
auto useStandins = LookAndFeel::ShouldUseStandins(*aDoc, aColor);

AutoWriteLock guard(*sServoFFILock);
Expand Down
2 changes: 1 addition & 1 deletion layout/style/GlobalStyleSheetCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ void GlobalStyleSheetCache::BuildPreferenceSheet(
}

if (StaticPrefs::browser_display_use_focus_colors()) {
const auto& colors = aPrefs.mColors;
const auto& colors = aPrefs.mLightColors;
nscolor focusText = colors.mFocusText;
nscolor focusBG = colors.mFocusBackground;
sheetText.AppendPrintf(
Expand Down
134 changes: 87 additions & 47 deletions layout/style/PreferenceSheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ PreferenceSheet::Prefs PreferenceSheet::sContentPrefs;
PreferenceSheet::Prefs PreferenceSheet::sChromePrefs;
PreferenceSheet::Prefs PreferenceSheet::sPrintPrefs;

static void GetColor(const char* aPrefName, nscolor& aColor) {
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()) {
Expand Down Expand Up @@ -84,82 +92,97 @@ static bool UseDocumentColors(bool aIsChrome, bool aUseAcccessibilityTheme) {
}
}

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;
}
void PreferenceSheet::Prefs::LoadColors(bool aIsLight) {
auto& colors = aIsLight ? mLightColors : mDarkColors;

void PreferenceSheet::Prefs::Load(bool aIsChrome) {
*this = {};

mIsChrome = aIsChrome;
mUseAccessibilityTheme = UseAccessibilityTheme(aIsChrome);
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 bool useStandins = nsContentUtils::UseStandinsForNativeColors();
// Users should be able to choose to use system colors or preferred colors
// when HCM is disabled, and in both OS-level HCM and FF-level HCM.
// To make this possible, we don't consider UseDocumentColors and
// mUseAccessibilityTheme when computing the following bool.
const bool usePrefColors = !useStandins && !aIsChrome &&
const bool usePrefColors = !useStandins && !mIsChrome &&
!StaticPrefs::browser_display_use_system_colors();
const auto scheme = aIsLight ? ColorScheme::Light : ColorScheme::Dark;
if (usePrefColors) {
GetColor("browser.display.background_color", mColors.mDefaultBackground);
GetColor("browser.display.foreground_color", mColors.mDefault);
GetColor("browser.anchor_color", mColors.mLink);
GetColor("browser.active_color", mColors.mActiveLink);
GetColor("browser.visited_color", mColors.mVisitedLink);
GetColor("browser.display.background_color", scheme,
colors.mDefaultBackground);
GetColor("browser.display.foreground_color", scheme, colors.mDefault);
GetColor("browser.anchor_color", scheme, colors.mLink);
GetColor("browser.active_color", scheme, colors.mActiveLink);
GetColor("browser.visited_color", scheme, colors.mVisitedLink);
} else {
using ColorID = LookAndFeel::ColorID;
const auto standins = LookAndFeel::UseStandins(useStandins);
// TODO(emilio): In the future we probably want to keep both sets of colors
// around or something.
//
// FIXME(emilio): Why do we look at a different set of colors when using
// standins vs. not?
const auto scheme = aIsChrome ? LookAndFeel::ColorSchemeForChrome()
: LookAndFeel::ColorScheme::Light;
mColors.mDefault = LookAndFeel::Color(
colors.mDefault = LookAndFeel::Color(
useStandins ? ColorID::Windowtext : ColorID::WindowForeground, scheme,
standins, mColors.mDefault);
mColors.mDefaultBackground = LookAndFeel::Color(
standins, colors.mDefault);
colors.mDefaultBackground = LookAndFeel::Color(
useStandins ? ColorID::Window : ColorID::WindowBackground, scheme,
standins, mColors.mDefaultBackground);
mColors.mLink = LookAndFeel::Color(ColorID::MozNativehyperlinktext, scheme,
standins, mColors.mLink);
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.
mColors.mVisitedLink = *color;
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.
mColors.mVisitedLink = NS_RGB(AVG2(NS_GET_R(mColors.mDefault),
NS_GET_R(mColors.mDefaultBackground)),
NS_GET_G(mColors.mDefault),
AVG2(NS_GET_B(mColors.mDefault),
NS_GET_B(mColors.mDefaultBackground)));
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) {
mColors.mActiveLink = mColors.mLink;
colors.mActiveLink = colors.mLink;
}
}

GetColor("browser.display.focus_text_color", mColors.mFocusText);
GetColor("browser.display.focus_background_color", mColors.mFocusBackground);
{
// These two are not color-scheme dependent, as we don't rebuild the
// preference sheet based on effective color scheme.
GetColor("browser.display.focus_text_color", ColorScheme::Light,
colors.mFocusText);
GetColor("browser.display.focus_background_color", ColorScheme::Light,
colors.mFocusBackground);
}

// Wherever we got the default background color from, ensure it is opaque.
colors.mDefaultBackground =
NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF), colors.mDefaultBackground);
}

// Wherever we got the default background color from, ensure it is
// opaque.
mColors.mDefaultBackground =
NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF), mColors.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;
}

void PreferenceSheet::Prefs::Load(bool aIsChrome) {
*this = {};

mIsChrome = aIsChrome;
mUseAccessibilityTheme = UseAccessibilityTheme(aIsChrome);

LoadColors(true);
LoadColors(false);
mUseDocumentColors = UseDocumentColors(aIsChrome, mUseAccessibilityTheme);
}

Expand All @@ -173,7 +196,15 @@ void PreferenceSheet::Initialize() {
sChromePrefs.Load(true);
sPrintPrefs = sContentPrefs;
if (!sPrintPrefs.mUseDocumentColors) {
sPrintPrefs.mColors = Prefs().mColors;
// For printing, we always use a preferred-light color scheme.
//
// 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.
sPrintPrefs.mLightColors = Prefs().mLightColors;
}

nsAutoString useDocumentColorPref;
Expand All @@ -193,13 +224,22 @@ void PreferenceSheet::Initialize() {
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 forground (text) color and background
// 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.mColors.mDefault);
sContentPrefs.mLightColors.mDefault);
Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_HCM_BACKGROUND,
sContentPrefs.mColors.mDefaultBackground);
sContentPrefs.mLightColors.mDefaultBackground);
}

Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_BACKPLATE,
Expand Down
10 changes: 8 additions & 2 deletions layout/style/PreferenceSheet.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define mozilla_ColorPreferences_h

#include "nsColor.h"
#include "mozilla/ColorScheme.h"

namespace mozilla {

Expand All @@ -19,7 +20,7 @@ class Document;

struct PreferenceSheet {
struct Prefs {
struct {
struct Colors {
nscolor mLink = NS_RGB(0x00, 0x00, 0xEE);
nscolor mActiveLink = NS_RGB(0xEE, 0x00, 0x00);
nscolor mVisitedLink = NS_RGB(0x55, 0x1A, 0x8B);
Expand All @@ -29,7 +30,11 @@ struct PreferenceSheet {

nscolor mFocusText = mDefault;
nscolor mFocusBackground = mDefaultBackground;
} mColors;
} mLightColors, mDarkColors;

const Colors& ColorsFor(ColorScheme aScheme) const {
return aScheme == ColorScheme::Light ? mLightColors : mDarkColors;
}

bool mIsChrome = false;
bool mUseAccessibilityTheme = false;
Expand All @@ -40,6 +45,7 @@ struct PreferenceSheet {
bool NonNativeThemeShouldBeHighContrast() const;

void Load(bool aIsChrome);
void LoadColors(bool aIsLight);
};

static void EnsureInitialized() {
Expand Down
5 changes: 3 additions & 2 deletions layout/style/nsStyleStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2880,8 +2880,9 @@ nsChangeHint nsStyleTextReset::CalcDifference(
//

static StyleRGBA DefaultColor(const Document& aDocument) {
return StyleRGBA::FromColor(
PreferenceSheet::PrefsFor(aDocument).mColors.mDefault);
return StyleRGBA::FromColor(PreferenceSheet::PrefsFor(aDocument)
.ColorsFor(aDocument.PreferredColorScheme())
.mDefault);
}

nsStyleText::nsStyleText(const Document& aDocument)
Expand Down
Loading

0 comments on commit 96a1a3f

Please sign in to comment.