mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
Bug 1845679 - Add an internal light-dark() function to allow defining colors reacting to color-scheme. r=dshin
This implement something like what's proposed in https://github.com/w3c/csswg-drafts/issues/7561, but the simplest version possible, until issues are resolved. This will allow the front-end to experiment with it and use it (and we can update to the standard feature once there's a spec for it). Differential Revision: https://phabricator.services.mozilla.com/D184680
This commit is contained in:
parent
e928053513
commit
e3aa78816f
@ -696,6 +696,12 @@ bool Gecko_IsDocumentBody(const Element* aElement) {
|
||||
return doc && doc->GetBodyElement() == aElement;
|
||||
}
|
||||
|
||||
bool Gecko_IsDarkColorScheme(const Document* aDoc,
|
||||
const StyleColorScheme* aStyle) {
|
||||
return LookAndFeel::ColorSchemeForStyle(*aDoc, aStyle->bits) ==
|
||||
ColorScheme::Dark;
|
||||
}
|
||||
|
||||
nscolor Gecko_ComputeSystemColor(StyleSystemColor aColor, const Document* aDoc,
|
||||
const StyleColorScheme* aStyle) {
|
||||
auto colorScheme = LookAndFeel::ColorSchemeForStyle(*aDoc, aStyle->bits);
|
||||
|
@ -527,6 +527,8 @@ void Gecko_StyleSheet_AddRef(const mozilla::StyleSheet* aSheet);
|
||||
void Gecko_StyleSheet_Release(const mozilla::StyleSheet* aSheet);
|
||||
bool Gecko_IsDocumentBody(const mozilla::dom::Element* element);
|
||||
|
||||
bool Gecko_IsDarkColorScheme(const mozilla::dom::Document*,
|
||||
const mozilla::StyleColorScheme*);
|
||||
nscolor Gecko_ComputeSystemColor(mozilla::StyleSystemColor,
|
||||
const mozilla::dom::Document*,
|
||||
const mozilla::StyleColorScheme*);
|
||||
|
@ -8375,6 +8375,13 @@
|
||||
mirror: always
|
||||
rust: true
|
||||
|
||||
# Is support for light-dark() on content enabled?
|
||||
- name: layout.css.light-dark.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
rust: true
|
||||
|
||||
# Is support for color-mix with non-SRGB color spaces on content enabled?
|
||||
- name: layout.css.color-mix.color-spaces.enabled
|
||||
type: RelaxedAtomicBool
|
||||
|
@ -487,6 +487,11 @@ impl Device {
|
||||
unsafe { bindings::Gecko_ComputeSystemColor(system_color, self.document(), color_scheme) }
|
||||
}
|
||||
|
||||
/// Returns whether the used color-scheme for `color-scheme` should be dark.
|
||||
pub(crate) fn is_dark_color_scheme(&self, color_scheme: &ColorScheme) -> bool {
|
||||
unsafe { bindings::Gecko_IsDarkColorScheme(self.document(), color_scheme) }
|
||||
}
|
||||
|
||||
/// Returns the default background color.
|
||||
///
|
||||
/// This is only for forced-colors/high-contrast, so looking at light colors
|
||||
|
@ -63,7 +63,7 @@ impl ColorMix {
|
||||
|
||||
let mut right_percentage = try_parse_percentage(input);
|
||||
|
||||
let right = Color::parse(context, input)?;
|
||||
let right = Color::parse_internal(context, input, preserve_authored)?;
|
||||
|
||||
if right_percentage.is_none() {
|
||||
right_percentage = try_parse_percentage(input);
|
||||
@ -130,11 +130,53 @@ pub enum Color {
|
||||
System(SystemColor),
|
||||
/// A color mix.
|
||||
ColorMix(Box<ColorMix>),
|
||||
/// A light-dark() color.
|
||||
LightDark(Box<LightDark>),
|
||||
/// Quirksmode-only rule for inheriting color from the body
|
||||
#[cfg(feature = "gecko")]
|
||||
InheritFromBodyQuirk,
|
||||
}
|
||||
|
||||
/// A light-dark(<light-color>, <dark-color>) function.
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem, ToCss)]
|
||||
#[css(function, comma)]
|
||||
pub struct LightDark {
|
||||
light: Color,
|
||||
dark: Color,
|
||||
}
|
||||
|
||||
impl LightDark {
|
||||
fn compute(&self, cx: &Context) -> ComputedColor {
|
||||
let style_color_scheme = cx.style().get_inherited_ui().clone_color_scheme();
|
||||
let dark = cx.device().is_dark_color_scheme(&style_color_scheme);
|
||||
let used = if dark {
|
||||
&self.dark
|
||||
} else {
|
||||
&self.light
|
||||
};
|
||||
used.to_computed_value(cx)
|
||||
}
|
||||
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
preserve_authored: PreserveAuthored,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let enabled =
|
||||
context.chrome_rules_enabled() || static_prefs::pref!("layout.css.light-dark.enabled");
|
||||
if !enabled {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
input.expect_function_matching("light-dark")?;
|
||||
input.parse_nested_block(|input| {
|
||||
let light = Color::parse_internal(context, input, preserve_authored)?;
|
||||
input.expect_comma()?;
|
||||
let dark = Color::parse_internal(context, input, preserve_authored)?;
|
||||
Ok(LightDark { light, dark })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AbsoluteColor> for Color {
|
||||
#[inline]
|
||||
fn from(value: AbsoluteColor) -> Self {
|
||||
@ -376,8 +418,7 @@ impl SystemColor {
|
||||
use crate::gecko::values::convert_nscolor_to_absolute_color;
|
||||
use crate::gecko_bindings::bindings;
|
||||
|
||||
// TODO: We should avoid cloning here most likely, though it's
|
||||
// cheap-ish.
|
||||
// TODO: We should avoid cloning here most likely, though it's cheap-ish.
|
||||
let style_color_scheme = cx.style().get_inherited_ui().clone_color_scheme();
|
||||
let color = cx.device().system_nscolor(*self, &style_color_scheme);
|
||||
if color == bindings::NS_SAME_AS_FOREGROUND_COLOR {
|
||||
@ -574,6 +615,7 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorParser<'i> for ColorParser<'a, 'b> {
|
||||
|
||||
/// Whether to preserve authored colors during parsing. That's useful only if we
|
||||
/// plan to serialize the color back.
|
||||
#[derive(Copy, Clone)]
|
||||
enum PreserveAuthored {
|
||||
No,
|
||||
Yes,
|
||||
@ -645,6 +687,11 @@ impl Color {
|
||||
return Ok(Color::ColorMix(Box::new(mix)));
|
||||
}
|
||||
|
||||
if let Ok(ld) = input.try_parse(|i| LightDark::parse(context, i, preserve_authored))
|
||||
{
|
||||
return Ok(Color::LightDark(Box::new(ld)));
|
||||
}
|
||||
|
||||
match e.kind {
|
||||
ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
|
||||
Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(
|
||||
@ -717,6 +764,7 @@ impl ToCss for Color {
|
||||
Color::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest),
|
||||
Color::Absolute(ref absolute) => absolute.to_css(dest),
|
||||
Color::ColorMix(ref mix) => mix.to_css(dest),
|
||||
Color::LightDark(ref ld) => ld.to_css(dest),
|
||||
#[cfg(feature = "gecko")]
|
||||
Color::System(system) => system.to_css(dest),
|
||||
#[cfg(feature = "gecko")]
|
||||
@ -732,6 +780,10 @@ impl Color {
|
||||
Self::InheritFromBodyQuirk => false,
|
||||
Self::CurrentColor | Color::System(..) => true,
|
||||
Self::Absolute(ref absolute) => allow_transparent && absolute.color.alpha() == 0.0,
|
||||
Self::LightDark(ref ld) => {
|
||||
ld.light.honored_in_forced_colors_mode(allow_transparent) &&
|
||||
ld.dark.honored_in_forced_colors_mode(allow_transparent)
|
||||
},
|
||||
Self::ColorMix(ref mix) => {
|
||||
mix.left.honored_in_forced_colors_mode(allow_transparent) &&
|
||||
mix.right.honored_in_forced_colors_mode(allow_transparent)
|
||||
@ -854,6 +906,7 @@ impl Color {
|
||||
Some(match *self {
|
||||
Color::CurrentColor => ComputedColor::CurrentColor,
|
||||
Color::Absolute(ref absolute) => ComputedColor::Absolute(absolute.color),
|
||||
Color::LightDark(ref ld) => ld.compute(context?),
|
||||
Color::ColorMix(ref mix) => {
|
||||
use crate::values::computed::percentage::Percentage;
|
||||
|
||||
|
@ -0,0 +1,2 @@
|
||||
[light-dark.html]
|
||||
prefs: [layout.css.light-dark.enabled:true]
|
@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<title>light-dark() color-scheme propagation</title>
|
||||
<script src=/resources/testharness.js></script>
|
||||
<script src=/resources/testharnessreport.js></script>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-color-adjust/#color-scheme-effect">
|
||||
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7561">
|
||||
<div id="system"></div>
|
||||
<div id="light" style="color-scheme: light"></div>
|
||||
<div id="dark" style="color-scheme: dark"></div>
|
||||
<script>
|
||||
const system_is_dark = matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
const elements = ["system", "light", "dark"].map(document.getElementById.bind(document));
|
||||
function test_light_dark(color, expected_light, expected_dark) {
|
||||
test(() => {
|
||||
for (let element of elements) {
|
||||
let should_be_dark = element.id == "dark" || (element.id == "system" && system_is_dark);
|
||||
element.style.backgroundColor = color;
|
||||
assert_not_equals(element.style.backgroundColor, "", "Should be valid");
|
||||
assert_equals(getComputedStyle(element).backgroundColor, should_be_dark ? expected_dark : expected_light);
|
||||
}
|
||||
}, color);
|
||||
}
|
||||
|
||||
test_light_dark("light-dark(white, black)", "rgb(255, 255, 255)", "rgb(0, 0, 0)");
|
||||
test_light_dark("light-dark(light-dark(white, red), red)", "rgb(255, 255, 255)", "rgb(255, 0, 0)");
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user