зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
e928053513
Коммит
e3aa78816f
|
@ -696,6 +696,12 @@ bool Gecko_IsDocumentBody(const Element* aElement) {
|
||||||
return doc && doc->GetBodyElement() == 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,
|
nscolor Gecko_ComputeSystemColor(StyleSystemColor aColor, const Document* aDoc,
|
||||||
const StyleColorScheme* aStyle) {
|
const StyleColorScheme* aStyle) {
|
||||||
auto colorScheme = LookAndFeel::ColorSchemeForStyle(*aDoc, aStyle->bits);
|
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);
|
void Gecko_StyleSheet_Release(const mozilla::StyleSheet* aSheet);
|
||||||
bool Gecko_IsDocumentBody(const mozilla::dom::Element* element);
|
bool Gecko_IsDocumentBody(const mozilla::dom::Element* element);
|
||||||
|
|
||||||
|
bool Gecko_IsDarkColorScheme(const mozilla::dom::Document*,
|
||||||
|
const mozilla::StyleColorScheme*);
|
||||||
nscolor Gecko_ComputeSystemColor(mozilla::StyleSystemColor,
|
nscolor Gecko_ComputeSystemColor(mozilla::StyleSystemColor,
|
||||||
const mozilla::dom::Document*,
|
const mozilla::dom::Document*,
|
||||||
const mozilla::StyleColorScheme*);
|
const mozilla::StyleColorScheme*);
|
||||||
|
|
|
@ -8375,6 +8375,13 @@
|
||||||
mirror: always
|
mirror: always
|
||||||
rust: true
|
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?
|
# Is support for color-mix with non-SRGB color spaces on content enabled?
|
||||||
- name: layout.css.color-mix.color-spaces.enabled
|
- name: layout.css.color-mix.color-spaces.enabled
|
||||||
type: RelaxedAtomicBool
|
type: RelaxedAtomicBool
|
||||||
|
|
|
@ -487,6 +487,11 @@ impl Device {
|
||||||
unsafe { bindings::Gecko_ComputeSystemColor(system_color, self.document(), color_scheme) }
|
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.
|
/// Returns the default background color.
|
||||||
///
|
///
|
||||||
/// This is only for forced-colors/high-contrast, so looking at light colors
|
/// 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 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() {
|
if right_percentage.is_none() {
|
||||||
right_percentage = try_parse_percentage(input);
|
right_percentage = try_parse_percentage(input);
|
||||||
|
@ -130,11 +130,53 @@ pub enum Color {
|
||||||
System(SystemColor),
|
System(SystemColor),
|
||||||
/// A color mix.
|
/// A color mix.
|
||||||
ColorMix(Box<ColorMix>),
|
ColorMix(Box<ColorMix>),
|
||||||
|
/// A light-dark() color.
|
||||||
|
LightDark(Box<LightDark>),
|
||||||
/// Quirksmode-only rule for inheriting color from the body
|
/// Quirksmode-only rule for inheriting color from the body
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
InheritFromBodyQuirk,
|
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 {
|
impl From<AbsoluteColor> for Color {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: AbsoluteColor) -> Self {
|
fn from(value: AbsoluteColor) -> Self {
|
||||||
|
@ -376,8 +418,7 @@ impl SystemColor {
|
||||||
use crate::gecko::values::convert_nscolor_to_absolute_color;
|
use crate::gecko::values::convert_nscolor_to_absolute_color;
|
||||||
use crate::gecko_bindings::bindings;
|
use crate::gecko_bindings::bindings;
|
||||||
|
|
||||||
// TODO: We should avoid cloning here most likely, though it's
|
// TODO: We should avoid cloning here most likely, though it's cheap-ish.
|
||||||
// cheap-ish.
|
|
||||||
let style_color_scheme = cx.style().get_inherited_ui().clone_color_scheme();
|
let style_color_scheme = cx.style().get_inherited_ui().clone_color_scheme();
|
||||||
let color = cx.device().system_nscolor(*self, &style_color_scheme);
|
let color = cx.device().system_nscolor(*self, &style_color_scheme);
|
||||||
if color == bindings::NS_SAME_AS_FOREGROUND_COLOR {
|
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
|
/// Whether to preserve authored colors during parsing. That's useful only if we
|
||||||
/// plan to serialize the color back.
|
/// plan to serialize the color back.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
enum PreserveAuthored {
|
enum PreserveAuthored {
|
||||||
No,
|
No,
|
||||||
Yes,
|
Yes,
|
||||||
|
@ -645,6 +687,11 @@ impl Color {
|
||||||
return Ok(Color::ColorMix(Box::new(mix)));
|
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 {
|
match e.kind {
|
||||||
ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
|
ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
|
||||||
Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(
|
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::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest),
|
||||||
Color::Absolute(ref absolute) => absolute.to_css(dest),
|
Color::Absolute(ref absolute) => absolute.to_css(dest),
|
||||||
Color::ColorMix(ref mix) => mix.to_css(dest),
|
Color::ColorMix(ref mix) => mix.to_css(dest),
|
||||||
|
Color::LightDark(ref ld) => ld.to_css(dest),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Color::System(system) => system.to_css(dest),
|
Color::System(system) => system.to_css(dest),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -732,6 +780,10 @@ impl Color {
|
||||||
Self::InheritFromBodyQuirk => false,
|
Self::InheritFromBodyQuirk => false,
|
||||||
Self::CurrentColor | Color::System(..) => true,
|
Self::CurrentColor | Color::System(..) => true,
|
||||||
Self::Absolute(ref absolute) => allow_transparent && absolute.color.alpha() == 0.0,
|
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) => {
|
Self::ColorMix(ref mix) => {
|
||||||
mix.left.honored_in_forced_colors_mode(allow_transparent) &&
|
mix.left.honored_in_forced_colors_mode(allow_transparent) &&
|
||||||
mix.right.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 {
|
Some(match *self {
|
||||||
Color::CurrentColor => ComputedColor::CurrentColor,
|
Color::CurrentColor => ComputedColor::CurrentColor,
|
||||||
Color::Absolute(ref absolute) => ComputedColor::Absolute(absolute.color),
|
Color::Absolute(ref absolute) => ComputedColor::Absolute(absolute.color),
|
||||||
|
Color::LightDark(ref ld) => ld.compute(context?),
|
||||||
Color::ColorMix(ref mix) => {
|
Color::ColorMix(ref mix) => {
|
||||||
use crate::values::computed::percentage::Percentage;
|
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>
|
Загрузка…
Ссылка в новой задаче