Bug 1777135 - Part 2: Improve nsNativeTheme::IsDarkBackground by accounting for default bg color. r=dholbert

Since callers want just an effective color to compute whether it's dark,
we simplify the API a bit too.

This makes sure we find the dark background in plain text documents.

Differential Revision: https://phabricator.services.mozilla.com/D151018
This commit is contained in:
Emilio Cobos Álvarez 2022-07-12 09:51:47 +00:00
parent b8d58c68d0
commit 482d8239cb
7 changed files with 83 additions and 66 deletions

View File

@ -4169,29 +4169,21 @@ void nsTextPaintStyle::InitCommonColors() {
return;
}
auto bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(mFrame);
nscolor defaultBgColor = mPresContext->DefaultBackgroundColor();
nscolor bgColor = bgFrame.mFrame ? bgFrame.mFrame->GetVisitedDependentColor(
&nsStyleBackground::mBackgroundColor)
: defaultBgColor;
mFrameBackgroundColor = NS_ComposeColors(defaultBgColor, bgColor);
auto bgColor = nsCSSRendering::FindEffectiveBackgroundColor(mFrame);
mFrameBackgroundColor = bgColor.mColor;
mSystemFieldForegroundColor =
LookAndFeel::Color(LookAndFeel::ColorID::Fieldtext, mFrame);
mSystemFieldBackgroundColor =
LookAndFeel::Color(LookAndFeel::ColorID::Field, mFrame);
if (bgFrame.mIsThemed) {
if (bgColor.mIsThemed) {
// Assume a native widget has sufficient contrast always
mSufficientContrast = 0;
mInitCommonColors = true;
return;
}
NS_ASSERTION(NS_GET_A(defaultBgColor) == 255,
"default background color is not opaque");
nscolor defaultWindowBackgroundColor =
LookAndFeel::Color(LookAndFeel::ColorID::Window, mFrame);
nscolor selectionTextColor =

View File

@ -1119,34 +1119,58 @@ void nsImageRenderer::ComputeObjectAnchorPoint(const Position& aPos,
aImageSize.height, &aTopLeft->y, &aAnchorPoint->y);
}
auto nsCSSRendering::FindNonTransparentBackgroundFrame(nsIFrame* aFrame,
bool aStopAtThemed)
-> NonTransparentBackgroundFrame {
NS_ASSERTION(aFrame,
"Cannot find NonTransparentBackgroundFrame in a null frame");
auto nsCSSRendering::FindEffectiveBackgroundColor(nsIFrame* aFrame,
bool aStopAtThemed,
bool aPreferBodyToCanvas)
-> EffectiveBackgroundColor {
MOZ_ASSERT(aFrame);
auto BgColorIfNotTransparent = [](nsIFrame* aFrame) -> Maybe<nscolor> {
nscolor c =
aFrame->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
if (NS_GET_A(c) == 255) {
return Some(c);
}
if (NS_GET_A(c)) {
// TODO(emilio): We should maybe just blend with ancestor bg colors and
// such, but this is probably good enough for now, matches pre-existing
// behavior.
const nscolor defaultBg = aFrame->PresContext()->DefaultBackgroundColor();
MOZ_ASSERT(NS_GET_A(defaultBg) == 255, "PreferenceSheet guarantees this");
return Some(NS_ComposeColors(defaultBg, c));
}
return Nothing();
};
for (nsIFrame* frame = aFrame; frame;
frame = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(frame)) {
// No need to call GetVisitedDependentColor because it always uses this
// alpha component anyway.
if (NS_GET_A(frame->StyleBackground()->BackgroundColor(frame))) {
return {frame, false, false};
if (auto bg = BgColorIfNotTransparent(frame)) {
return {*bg};
}
if (aStopAtThemed && frame->IsThemed()) {
return {frame, true, false};
return {NS_TRANSPARENT, true};
}
if (IsCanvasFrame(frame)) {
nsIFrame* bgFrame = FindBackgroundFrame(frame);
if (bgFrame &&
NS_GET_A(bgFrame->StyleBackground()->BackgroundColor(bgFrame))) {
return {bgFrame, false, true};
if (aPreferBodyToCanvas) {
if (auto* body = frame->PresContext()->Document()->GetBodyElement()) {
if (nsIFrame* f = body->GetPrimaryFrame()) {
if (auto bg = BgColorIfNotTransparent(f)) {
return {*bg};
}
}
}
}
if (nsIFrame* bgFrame = FindBackgroundFrame(frame)) {
if (auto bg = BgColorIfNotTransparent(bgFrame)) {
return {*bg};
}
}
}
}
return {};
return {aFrame->PresContext()->DefaultBackgroundColor()};
}
// Returns true if aFrame is a canvas frame.

View File

@ -332,18 +332,28 @@ struct nsCSSRendering {
}
/**
* Find a frame which draws a non-transparent background, for various contrast
* checks. Note that this only accounts for background-color and might stop at
* themed frames (depending on the argument), so it might not be what you
* want.
* Find a non-transparent background color on an ancestor, for various
* contrast checks. Note that this only accounts for background-color and
* might stop at themed frames (depending on the argument), so it might not be
* what you want. Note that if we stop at themed frames we might, in fact, end
* up returning a transparent color (but then mIsThemed will be set to true).
*
* For semi-transparent colors, right now we blend with the default
* background-color rather than with all ancestor backgrounds.
*
* If aPreferBodyToCanvas is true, we prefer the background color of the
* <body> frame, even though we found a canvas background, because the body
* background color is most likely what will be visible as the background
* color of the page, even if the html element has a different background
* color which prevents that of the body frame to propagate to the viewport.
*/
struct NonTransparentBackgroundFrame {
nsIFrame* mFrame = nullptr;
struct EffectiveBackgroundColor {
nscolor mColor = 0;
bool mIsThemed = false;
bool mIsForCanvas = false;
};
static NonTransparentBackgroundFrame FindNonTransparentBackgroundFrame(
nsIFrame* aFrame, bool aStopAtThemed = true);
static EffectiveBackgroundColor FindEffectiveBackgroundColor(
nsIFrame* aFrame, bool aStopAtThemed = true,
bool aPreferBodyToCanvas = false);
/**
* Determine the background color to draw taking into account print settings.

View File

@ -0,0 +1,8 @@
<!doctype html>
<style>
:root {
color-scheme: dark;
}
</style>
Here's a tall div, to generate a scrollbar:
<div style="height:200vh"></div>

View File

@ -0,0 +1,9 @@
<!doctype html>
<style>
:root {
color-scheme: dark;
background-color: Canvas;
}
</style>
Here's a tall div, to generate a scrollbar:
<div style="height:200vh"></div>

View File

@ -2129,4 +2129,5 @@ skip-if(Android) test-pref(ui.textScaleFactor,150) test-pref(browser.display.os-
# test-pref(ui.textScaleFactor,50) test-pref(browser.display.os-zoom-behavior,1) == 1773633.html 1773633-full-zoom.html
# This works (but not on macOS for some reason).
skip-if(Android) test-pref(ui.textScaleFactor,50) test-pref(browser.display.os-zoom-behavior,1) != 1773633.html 1773633.html
pref(widget.disable-dark-scrollbar,false) == 1777135.html 1777135-ref.html
test-pref(widget.non-native-theme.use-theme-accent,false) == 1778834.html 1778834-ref.html

View File

@ -547,14 +547,6 @@ bool nsNativeTheme::IsRangeHorizontal(nsIFrame* aFrame) {
return aFrame->GetSize().width >= aFrame->GetSize().height;
}
static nsIFrame* GetBodyFrame(nsIFrame* aCanvasFrame) {
nsIContent* body = aCanvasFrame->PresContext()->Document()->GetBodyElement();
if (!body) {
return nullptr;
}
return body->GetPrimaryFrame();
}
/* static */
bool nsNativeTheme::IsDarkBackground(nsIFrame* aFrame) {
// Try to find the scrolled frame. Note that for stuff like xul <tree> there
@ -573,29 +565,10 @@ bool nsNativeTheme::IsDarkBackground(nsIFrame* aFrame) {
}
}
auto backgroundFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(
aFrame, /* aStopAtThemed = */ false);
if (!backgroundFrame.mFrame) {
return false;
}
nscolor color = backgroundFrame.mFrame->StyleBackground()->BackgroundColor(
backgroundFrame.mFrame);
if (backgroundFrame.mIsForCanvas) {
// For canvas frames, prefer to look at the body first, because the body
// background color is most likely what will be visible as the background
// color of the page, even if the html element has a different background
// color which prevents that of the body frame to propagate to the viewport.
if (nsIFrame* bodyFrame = GetBodyFrame(aFrame)) {
nscolor bodyColor =
bodyFrame->StyleBackground()->BackgroundColor(bodyFrame);
if (NS_GET_A(bodyColor)) {
color = bodyColor;
}
}
}
auto color =
nsCSSRendering::FindEffectiveBackgroundColor(
aFrame, /* aStopAtThemed = */ false, /* aPreferBodyToCanvas = */ true)
.mColor;
return LookAndFeel::IsDarkColor(color);
}