Bug 1851370 - Vendor cssparser-color into Gecko r=emilio

To increase the integration between color and calc it is nescessary to
vendor the current cssparser-color library into Gecko where all the calc
functionality lives.

Differential Revision: https://phabricator.services.mozilla.com/D188216
This commit is contained in:
Tiaan Louw 2023-09-14 10:53:03 +00:00
Родитель d16a6ad83e
Коммит b52ec06463
11 изменённых файлов: 14 добавлений и 465 удалений

10
Cargo.lock сгенерированный
Просмотреть файл

@ -1036,15 +1036,6 @@ dependencies = [
"smallvec",
]
[[package]]
name = "cssparser-color"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f"
dependencies = [
"cssparser",
]
[[package]]
name = "cssparser-macros"
version = "0.6.1"
@ -5191,7 +5182,6 @@ dependencies = [
"bitflags 1.3.2",
"byteorder",
"cssparser",
"cssparser-color",
"derive_more 0.99.999",
"dom",
"euclid",

Просмотреть файл

@ -33,7 +33,6 @@ atomic_refcell = "0.1"
bitflags = "1.0"
byteorder = "1.0"
cssparser = "0.33"
cssparser-color = "0.1"
derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign", "deref", "deref_mut", "from"] }
dom = { path = "../../../dom/base/rust" }
new_debug_unreachable = "1.0"

Просмотреть файл

@ -7,6 +7,7 @@
/// cbindgen:ignore
pub mod convert;
pub mod mix;
pub mod parsing;
use cssparser::color::PredefinedColorSpace;
use std::fmt::{self, Write};
@ -512,7 +513,7 @@ impl ToCss for AbsoluteColor {
ColorSpace::Srgb if self.flags.contains(ColorFlags::IS_LEGACY_SRGB) => {
// The "none" keyword is not supported in the rgb/rgba legacy syntax.
cssparser::ToCss::to_css(
&cssparser_color::RgbaLegacy::from_floats(
&parsing::RgbaLegacy::from_floats(
self.components.0,
self.components.1,
self.components.2,
@ -523,19 +524,19 @@ impl ToCss for AbsoluteColor {
},
ColorSpace::Hsl | ColorSpace::Hwb => self.into_srgb_legacy().to_css(dest),
ColorSpace::Lab => cssparser::ToCss::to_css(
&cssparser_color::Lab::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
&parsing::Lab::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
dest,
),
ColorSpace::Lch => cssparser::ToCss::to_css(
&cssparser_color::Lch::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
&parsing::Lch::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
dest,
),
ColorSpace::Oklab => cssparser::ToCss::to_css(
&cssparser_color::Oklab::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
&parsing::Oklab::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
dest,
),
ColorSpace::Oklch => cssparser::ToCss::to_css(
&cssparser_color::Oklch::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
&parsing::Oklch::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
dest,
),
_ => {
@ -560,14 +561,14 @@ impl ToCss for AbsoluteColor {
},
};
let color_function = cssparser_color::ColorFunction {
let color_function = parsing::ColorFunction {
color_space,
c1: maybe_c1,
c2: maybe_c2,
c3: maybe_c3,
alpha: maybe_alpha,
};
let color = cssparser_color::Color::ColorFunction(color_function);
let color = parsing::Color::ColorFunction(color_function);
cssparser::ToCss::to_css(&color, dest)
},
}

Просмотреть файл

@ -8,9 +8,6 @@
//! Relative colors, color-mix, system colors, and other such things require better calc() support
//! and integration.
#[cfg(test)]
mod tests;
use cssparser::color::{
clamp_floor_256_f32, clamp_unit_f32, parse_hash_color, serialize_color_alpha,
PredefinedColorSpace, OPAQUE,

Просмотреть файл

@ -10,7 +10,7 @@ use crate::values::computed::percentage::Percentage;
use crate::values::generics::color::{
GenericCaretColor, GenericColor, GenericColorMix, GenericColorOrAuto,
};
use cssparser_color::Color as CSSParserColor;
use crate::color::parsing::Color as CSSParserColor;
use std::fmt;
use style_traits::{CssWriter, ToCss};

Просмотреть файл

@ -16,7 +16,7 @@ use crate::values::specified::length::{ContainerRelativeLength, ViewportPercenta
use crate::values::specified::{self, Angle, Resolution, Time};
use crate::values::{serialize_number, serialize_percentage, CSSFloat, CSSInteger};
use cssparser::{CowRcStr, Parser, Token};
use cssparser_color::{AngleOrNumber, NumberOrPercentage};
use crate::color::parsing::{AngleOrNumber, NumberOrPercentage};
use smallvec::SmallVec;
use std::cmp;
use std::fmt::{self, Write};

Просмотреть файл

@ -15,7 +15,7 @@ use crate::values::generics::color::{
use crate::values::specified::calc::CalcNode;
use crate::values::specified::Percentage;
use crate::values::{normalize, CustomIdent};
use cssparser_color::{AngleOrNumber, Color as CSSParserColor, NumberOrPercentage, FromParsedColor};
use crate::color::parsing::{AngleOrNumber, Color as CSSParserColor, NumberOrPercentage, FromParsedColor, self};
use cssparser::{Parser, Token, BasicParseErrorKind, ParseErrorKind, color::PredefinedColorSpace};
use itoa;
use std::fmt::{self, Write};
@ -500,7 +500,7 @@ impl FromParsedColor for Color {
}
struct ColorParser<'a, 'b: 'a>(&'a ParserContext<'b>);
impl<'a, 'b: 'a, 'i: 'a> cssparser_color::ColorParser<'i> for ColorParser<'a, 'b> {
impl<'a, 'b: 'a, 'i: 'a> parsing::ColorParser<'i> for ColorParser<'a, 'b> {
type Output = Color;
type Error = StyleParseErrorKind<'i>;
@ -601,7 +601,7 @@ impl Color {
};
let color_parser = ColorParser(&*context);
match input.try_parse(|i| cssparser_color::parse_color_with(&color_parser, i)) {
match input.try_parse(|i| parsing::parse_color_with(&color_parser, i)) {
Ok(mut color) => {
if let Color::Absolute(ref mut absolute) = color {
let enabled = {

Просмотреть файл

@ -38,7 +38,7 @@ includes = ["mozilla/ServoStyleConstsForwards.h", "mozilla/ServoStyleSet.h"]
[parse]
parse_deps = true
extra_bindings = ["style"]
include = ["style", "app_units", "cssparser", "cssparser_color", "style_traits", "servo_arc"]
include = ["style", "app_units", "cssparser", "style_traits", "servo_arc"]
[struct]
associated_constants_in_body = true

Просмотреть файл

@ -1 +0,0 @@
{"files":{"Cargo.toml":"4d81f7fec4a83149e1b7365eaac98a7b6e910db62c5e82f0e69f80f2718d81f1","lib.rs":"3756b36c919d2150007a933ee7e01dab68620656ce4f8a14463de00f4ced98ba","tests.rs":"685edeec1ee7f3df2bdf6bd8ae01766bbeaad6c1feb52de55791d89f8dcc7989"},"package":"556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f"}

36
third_party/rust/cssparser-color/Cargo.toml поставляемый
Просмотреть файл

@ -1,36 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "cssparser-color"
version = "0.1.0"
authors = ["Emilio Cobos Álvarez <emilio@crisal.io>"]
description = "Color implementation based on cssparser"
documentation = "https://docs.rs/cssparser-color/"
license = "MPL-2.0"
repository = "https://github.com/servo/rust-cssparser"
resolver = "1"
[lib]
path = "lib.rs"
[dependencies.cssparser]
version = "0.33"
[dev-dependencies.difference]
version = "2.0"
[dev-dependencies.encoding_rs]
version = "0.8"
[dev-dependencies.serde_json]
version = "1.0"

401
third_party/rust/cssparser-color/tests.rs поставляемый
Просмотреть файл

@ -1,401 +0,0 @@
/* 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/. */
use super::*;
use crate::{ColorParser, PredefinedColorSpace, Color, RgbaLegacy};
use cssparser::{Parser, ParserInput};
use serde_json::{self, json, Value};
fn almost_equals(a: &Value, b: &Value) -> bool {
match (a, b) {
(&Value::Number(ref a), &Value::Number(ref b)) => {
let a = a.as_f64().unwrap();
let b = b.as_f64().unwrap();
(a - b).abs() <= a.abs() * 1e-6
}
(&Value::Bool(a), &Value::Bool(b)) => a == b,
(&Value::String(ref a), &Value::String(ref b)) => a == b,
(&Value::Array(ref a), &Value::Array(ref b)) => {
a.len() == b.len()
&& a.iter()
.zip(b.iter())
.all(|(ref a, ref b)| almost_equals(*a, *b))
}
(&Value::Object(_), &Value::Object(_)) => panic!("Not implemented"),
(&Value::Null, &Value::Null) => true,
_ => false,
}
}
fn assert_json_eq(results: Value, expected: Value, message: &str) {
if !almost_equals(&results, &expected) {
println!(
"{}",
::difference::Changeset::new(
&serde_json::to_string_pretty(&results).unwrap(),
&serde_json::to_string_pretty(&expected).unwrap(),
"\n",
)
);
panic!("{}", message)
}
}
fn run_raw_json_tests<F: Fn(Value, Value) -> ()>(json_data: &str, run: F) {
let items = match serde_json::from_str(json_data) {
Ok(Value::Array(items)) => items,
other => panic!("Invalid JSON: {:?}", other),
};
assert!(items.len() % 2 == 0);
let mut input = None;
for item in items.into_iter() {
match (&input, item) {
(&None, json_obj) => input = Some(json_obj),
(&Some(_), expected) => {
let input = input.take().unwrap();
run(input, expected)
}
};
}
}
fn run_json_tests<F: Fn(&mut Parser) -> Value>(json_data: &str, parse: F) {
run_raw_json_tests(json_data, |input, expected| match input {
Value::String(input) => {
let mut parse_input = ParserInput::new(&input);
let result = parse(&mut Parser::new(&mut parse_input));
assert_json_eq(result, expected, &input);
}
_ => panic!("Unexpected JSON"),
});
}
fn run_color_tests<F: Fn(Result<Color, ()>) -> Value>(json_data: &str, to_json: F) {
run_json_tests(json_data, |input| {
let result: Result<_, ParseError<()>> =
input.parse_entirely(|i| Color::parse(i).map_err(Into::into));
to_json(result.map_err(|_| ()))
});
}
#[test]
fn color3() {
run_color_tests(include_str!("../src/css-parsing-tests/color3.json"), |c| {
c.ok()
.map(|v| v.to_css_string().to_json())
.unwrap_or(Value::Null)
})
}
#[cfg_attr(all(miri, feature = "skip_long_tests"), ignore)]
#[test]
fn color3_hsl() {
run_color_tests(include_str!("../src/css-parsing-tests/color3_hsl.json"), |c| {
c.ok()
.map(|v| v.to_css_string().to_json())
.unwrap_or(Value::Null)
})
}
/// color3_keywords.json is different: R, G and B are in 0..255 rather than 0..1
#[test]
fn color3_keywords() {
run_color_tests(
include_str!("../src/css-parsing-tests/color3_keywords.json"),
|c| {
c.ok()
.map(|v| v.to_css_string().to_json())
.unwrap_or(Value::Null)
},
)
}
#[cfg_attr(all(miri, feature = "skip_long_tests"), ignore)]
#[test]
fn color4_hwb() {
run_color_tests(include_str!("../src/css-parsing-tests/color4_hwb.json"), |c| {
c.ok()
.map(|v| v.to_css_string().to_json())
.unwrap_or(Value::Null)
})
}
#[cfg_attr(all(miri, feature = "skip_long_tests"), ignore)]
#[test]
fn color4_lab_lch_oklab_oklch() {
run_color_tests(
include_str!("../src/css-parsing-tests/color4_lab_lch_oklab_oklch.json"),
|c| {
c.ok()
.map(|v| v.to_css_string().to_json())
.unwrap_or(Value::Null)
},
)
}
#[test]
fn color4_color_function() {
run_color_tests(
include_str!("../src/css-parsing-tests/color4_color_function.json"),
|c| {
c.ok()
.map(|v| v.to_css_string().to_json())
.unwrap_or(Value::Null)
},
)
}
macro_rules! parse_single_color {
($i:expr) => {{
let input = $i;
let mut input = ParserInput::new(input);
let mut input = Parser::new(&mut input);
Color::parse(&mut input).map_err(Into::<ParseError<()>>::into)
}};
}
#[test]
fn color4_invalid_color_space() {
let result = parse_single_color!("color(invalid 1 1 1)");
assert!(result.is_err());
}
#[test]
fn serialize_current_color() {
let c = Color::CurrentColor;
assert!(c.to_css_string() == "currentcolor");
}
#[test]
fn serialize_rgb_full_alpha() {
let c = Color::Rgba(RgbaLegacy::new(255, 230, 204, 1.0));
assert_eq!(c.to_css_string(), "rgb(255, 230, 204)");
}
#[test]
fn serialize_rgba() {
let c = Color::Rgba(RgbaLegacy::new(26, 51, 77, 0.125));
assert_eq!(c.to_css_string(), "rgba(26, 51, 77, 0.125)");
}
#[test]
fn serialize_rgba_two_digit_float_if_roundtrips() {
let c = Color::Rgba(RgbaLegacy::from_floats(0., 0., 0., 0.5));
assert_eq!(c.to_css_string(), "rgba(0, 0, 0, 0.5)");
}
trait ToJson {
fn to_json(&self) -> Value;
}
impl<T> ToJson for T
where
T: Clone,
Value: From<T>,
{
fn to_json(&self) -> Value {
Value::from(self.clone())
}
}
impl ToJson for Color {
fn to_json(&self) -> Value {
match *self {
Color::CurrentColor => "currentcolor".to_json(),
Color::Rgba(ref rgba) => {
json!([rgba.red, rgba.green, rgba.blue, rgba.alpha])
}
Color::Hsl(ref c) => json!([c.hue, c.saturation, c.lightness, c.alpha]),
Color::Hwb(ref c) => json!([c.hue, c.whiteness, c.blackness, c.alpha]),
Color::Lab(ref c) => json!([c.lightness, c.a, c.b, c.alpha]),
Color::Lch(ref c) => json!([c.lightness, c.chroma, c.hue, c.alpha]),
Color::Oklab(ref c) => json!([c.lightness, c.a, c.b, c.alpha]),
Color::Oklch(ref c) => json!([c.lightness, c.chroma, c.hue, c.alpha]),
Color::ColorFunction(ref c) => {
json!([c.color_space.as_str(), c.c1, c.c2, c.c3, c.alpha])
}
}
}
}
#[test]
fn generic_parser() {
#[derive(Debug, PartialEq)]
enum OutputType {
CurrentColor,
Rgba(u8, u8, u8, f32),
Hsl(Option<f32>, Option<f32>, Option<f32>, Option<f32>),
Hwb(Option<f32>, Option<f32>, Option<f32>, Option<f32>),
Lab(Option<f32>, Option<f32>, Option<f32>, Option<f32>),
Lch(Option<f32>, Option<f32>, Option<f32>, Option<f32>),
Oklab(Option<f32>, Option<f32>, Option<f32>, Option<f32>),
Oklch(Option<f32>, Option<f32>, Option<f32>, Option<f32>),
ColorFunction(
PredefinedColorSpace,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
),
}
impl FromParsedColor for OutputType {
fn from_current_color() -> Self {
OutputType::CurrentColor
}
fn from_rgba(red: u8, green: u8, blue: u8, alpha: f32) -> Self {
OutputType::Rgba(red, green, blue, alpha)
}
fn from_hsl(
hue: Option<f32>,
saturation: Option<f32>,
lightness: Option<f32>,
alpha: Option<f32>,
) -> Self {
OutputType::Hsl(hue, saturation, lightness, alpha)
}
fn from_hwb(
hue: Option<f32>,
blackness: Option<f32>,
whiteness: Option<f32>,
alpha: Option<f32>,
) -> Self {
OutputType::Hwb(hue, blackness, whiteness, alpha)
}
fn from_lab(
lightness: Option<f32>,
a: Option<f32>,
b: Option<f32>,
alpha: Option<f32>,
) -> Self {
OutputType::Lab(lightness, a, b, alpha)
}
fn from_lch(
lightness: Option<f32>,
chroma: Option<f32>,
hue: Option<f32>,
alpha: Option<f32>,
) -> Self {
OutputType::Lch(lightness, chroma, hue, alpha)
}
fn from_oklab(
lightness: Option<f32>,
a: Option<f32>,
b: Option<f32>,
alpha: Option<f32>,
) -> Self {
OutputType::Oklab(lightness, a, b, alpha)
}
fn from_oklch(
lightness: Option<f32>,
chroma: Option<f32>,
hue: Option<f32>,
alpha: Option<f32>,
) -> Self {
OutputType::Oklch(lightness, chroma, hue, alpha)
}
fn from_color_function(
color_space: PredefinedColorSpace,
c1: Option<f32>,
c2: Option<f32>,
c3: Option<f32>,
alpha: Option<f32>,
) -> Self {
OutputType::ColorFunction(color_space, c1, c2, c3, alpha)
}
}
struct TestColorParser;
impl<'i> ColorParser<'i> for TestColorParser {
type Output = OutputType;
type Error = ();
}
#[rustfmt::skip]
const TESTS: &[(&str, OutputType)] = &[
("currentColor", OutputType::CurrentColor),
("rgb(1, 2, 3)", OutputType::Rgba(1, 2, 3, 1.0)),
("rgba(1, 2, 3, 0.4)", OutputType::Rgba(1, 2, 3, 0.4)),
("rgb(none none none / none)", OutputType::Rgba(0, 0, 0, 0.0)),
("rgb(1 none 3 / none)", OutputType::Rgba(1, 0, 3, 0.0)),
("hsla(45deg, 20%, 30%, 0.4)", OutputType::Hsl(Some(45.0), Some(0.2), Some(0.3), Some(0.4))),
("hsl(45deg none none)", OutputType::Hsl(Some(45.0), None, None, Some(1.0))),
("hsl(none 10% none / none)", OutputType::Hsl(None, Some(0.1), None, None)),
("hsl(120 100.0% 50.0%)", OutputType::Hsl(Some(120.0), Some(1.0), Some(0.5), Some(1.0))),
("hwb(45deg 20% 30% / 0.4)", OutputType::Hwb(Some(45.0), Some(0.2), Some(0.3), Some(0.4))),
("lab(100 20 30 / 0.4)", OutputType::Lab(Some(100.0), Some(20.0), Some(30.0), Some(0.4))),
("lch(100 20 30 / 0.4)", OutputType::Lch(Some(100.0), Some(20.0), Some(30.0), Some(0.4))),
("oklab(100 20 30 / 0.4)", OutputType::Oklab(Some(100.0), Some(20.0), Some(30.0), Some(0.4))),
("oklch(100 20 30 / 0.4)", OutputType::Oklch(Some(100.0), Some(20.0), Some(30.0), Some(0.4))),
("color(srgb 0.1 0.2 0.3 / 0.4)", OutputType::ColorFunction(PredefinedColorSpace::Srgb, Some(0.1), Some(0.2), Some(0.3), Some(0.4))),
("color(srgb none none none)", OutputType::ColorFunction(PredefinedColorSpace::Srgb, None, None, None, Some(1.0))),
("color(srgb none none none / none)", OutputType::ColorFunction(PredefinedColorSpace::Srgb, None, None, None, None)),
("color(srgb-linear 0.1 0.2 0.3 / 0.4)", OutputType::ColorFunction(PredefinedColorSpace::SrgbLinear, Some(0.1), Some(0.2), Some(0.3), Some(0.4))),
("color(display-p3 0.1 0.2 0.3 / 0.4)", OutputType::ColorFunction(PredefinedColorSpace::DisplayP3, Some(0.1), Some(0.2), Some(0.3), Some(0.4))),
("color(a98-rgb 0.1 0.2 0.3 / 0.4)", OutputType::ColorFunction(PredefinedColorSpace::A98Rgb, Some(0.1), Some(0.2), Some(0.3), Some(0.4))),
("color(prophoto-rgb 0.1 0.2 0.3 / 0.4)", OutputType::ColorFunction(PredefinedColorSpace::ProphotoRgb, Some(0.1), Some(0.2), Some(0.3), Some(0.4))),
("color(rec2020 0.1 0.2 0.3 / 0.4)", OutputType::ColorFunction(PredefinedColorSpace::Rec2020, Some(0.1), Some(0.2), Some(0.3), Some(0.4))),
("color(xyz-d50 0.1 0.2 0.3 / 0.4)", OutputType::ColorFunction(PredefinedColorSpace::XyzD50, Some(0.1), Some(0.2), Some(0.3), Some(0.4))),
("color(xyz-d65 0.1 0.2 0.3 / 0.4)", OutputType::ColorFunction(PredefinedColorSpace::XyzD65, Some(0.1), Some(0.2), Some(0.3), Some(0.4))),
];
for (input, expected) in TESTS {
let mut input = ParserInput::new(*input);
let mut input = Parser::new(&mut input);
let actual: OutputType = parse_color_with(&TestColorParser, &mut input).unwrap();
assert_eq!(actual, *expected);
}
}
#[test]
fn serialize_modern_components() {
// None.
assert_eq!(ModernComponent(&None).to_css_string(), "none".to_string());
// Finite values.
assert_eq!(
ModernComponent(&Some(10.0)).to_css_string(),
"10".to_string()
);
assert_eq!(
ModernComponent(&Some(-10.0)).to_css_string(),
"-10".to_string()
);
assert_eq!(ModernComponent(&Some(0.0)).to_css_string(), "0".to_string());
assert_eq!(
ModernComponent(&Some(-0.0)).to_css_string(),
"0".to_string()
);
// Infinite values.
assert_eq!(
ModernComponent(&Some(f32::INFINITY)).to_css_string(),
"calc(infinity)".to_string()
);
assert_eq!(
ModernComponent(&Some(f32::NEG_INFINITY)).to_css_string(),
"calc(-infinity)".to_string()
);
// NaN.
assert_eq!(
ModernComponent(&Some(f32::NAN)).to_css_string(),
"calc(NaN)".to_string()
);
}