commit 6b72f0a4301abfbd2bad47524e87e22503edbdd5
parent b2cd5963c99ad6f059b2573a77701cab4da7dc9f
Author: Diego Escalante <descalante@mozilla.com>
Date: Wed, 17 Dec 2025 15:00:42 +0000
Bug 1986631 - Implement support for Type(<syntax>) and handle default as raw-string. r=firefox-style-system-reviewers,emilio
Differential Revision: https://phabricator.services.mozilla.com/D275887
Diffstat:
3 files changed, 131 insertions(+), 13 deletions(-)
diff --git a/servo/components/style/custom_properties.rs b/servo/components/style/custom_properties.rs
@@ -17,7 +17,7 @@ use crate::properties::{
};
use crate::properties_and_values::{
registry::PropertyRegistrationData,
- syntax::data_type::DependentDataTypes,
+ syntax::{data_type::DependentDataTypes, Descriptor},
value::{
AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,
SpecifiedValue as SpecifiedRegisteredValue,
@@ -473,6 +473,13 @@ enum SubstitutionFunctionKind {
Attr,
}
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem, Parse)]
+enum AttributeType {
+ None,
+ Type(Descriptor),
+ Unit,
+}
+
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
struct VariableFallback {
start: num::NonZeroUsize,
@@ -486,6 +493,7 @@ struct SubstitutionFunctionReference {
start: usize,
end: usize,
fallback: Option<VariableFallback>,
+ attribute_syntax: AttributeType,
prev_token_type: TokenSerializationType,
next_token_type: TokenSerializationType,
substitution_kind: SubstitutionFunctionKind,
@@ -831,6 +839,19 @@ fn parse_declaration_value_block<'i, 't>(
name.as_ref()
});
+ let mut attribute_syntax = AttributeType::None;
+ if substitution_kind == SubstitutionFunctionKind::Attr
+ && input
+ .try_parse(|input| input.expect_function_matching("type"))
+ .is_ok()
+ {
+ // TODO(descalante): determine what to do for `type(garbage)` bug 2006626
+ attribute_syntax = input
+ .parse_nested_block(Descriptor::from_css_parser)
+ .ok()
+ .map_or(AttributeType::None, AttributeType::Type);
+ }
+
// We want the order of the references to match source order. So we need to reserve our slot
// now, _before_ parsing our fallback. Note that we don't care if parsing fails after all, since
// if this fails we discard the whole result anyways.
@@ -845,6 +866,7 @@ fn parse_declaration_value_block<'i, 't>(
next_token_type: TokenSerializationType::Nothing,
// To be fixed up after parsing fallback.
fallback: None,
+ attribute_syntax,
substitution_kind: substitution_kind.clone(),
});
@@ -2109,6 +2131,12 @@ fn do_substitute_chunk<'a>(
Ok(Substitution::from_value(substituted))
}
+fn quoted_css_string(src: &str) -> String {
+ let mut dest = String::with_capacity(src.len() + 2);
+ cssparser::serialize_string(src, &mut dest).unwrap();
+ dest
+}
+
fn substitute_one_reference<'a>(
css: &'a str,
url_data: &UrlExtraData,
@@ -2135,12 +2163,25 @@ fn substitute_one_reference<'a>(
},
SubstitutionFunctionKind::Attr => attr_provider
.get_attr(AtomIdent::cast(&reference.name))
- .map(|attr| {
- Substitution::new(
- Cow::Owned(attr),
- TokenSerializationType::Nothing,
- TokenSerializationType::Nothing,
+ .and_then(|attr| {
+ let AttributeType::Type(syntax) = &reference.attribute_syntax else {
+ return Some(Substitution::new(
+ Cow::Owned(quoted_css_string(&attr)),
+ TokenSerializationType::Nothing,
+ TokenSerializationType::Nothing,
+ ));
+ };
+ let mut input = ParserInput::new(&attr);
+ let mut parser = Parser::new(&mut input);
+ let value = SpecifiedRegisteredValue::parse(
+ &mut parser,
+ syntax,
+ url_data,
+ AllowComputationallyDependent::Yes,
)
+ .ok()?;
+ let value = value.to_computed_value(computed_context);
+ Some(Substitution::from_value(value.to_variable_value()))
}),
};
@@ -2149,6 +2190,7 @@ fn substitute_one_reference<'a>(
.next_if(|next_ref| next_ref.end <= reference.end)
.is_some()
{}
+
return Ok(s);
}
diff --git a/servo/components/style/properties_and_values/syntax/data_type.rs b/servo/components/style/properties_and_values/syntax/data_type.rs
@@ -22,7 +22,7 @@ bitflags! {
}
/// <https://drafts.css-houdini.org/css-properties-values-api-1/#supported-names>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub enum DataType {
/// Any valid `<length>` value
Length,
diff --git a/servo/components/style/properties_and_values/syntax/mod.rs b/servo/components/style/properties_and_values/syntax/mod.rs
@@ -12,7 +12,7 @@ use std::{borrow::Cow, fmt::Write};
use crate::derives::*;
use crate::parser::{Parse, ParserContext};
use crate::values::CustomIdent;
-use cssparser::{Parser as CSSParser, ParserInput as CSSParserInput};
+use cssparser::{Parser as CSSParser, ParserInput as CSSParserInput, Token};
use style_traits::{
CssWriter, ParseError as StyleParseError, PropertySyntaxParseError as ParseError,
StyleParseErrorKind, ToCss,
@@ -24,7 +24,7 @@ mod ascii;
pub mod data_type;
/// <https://drafts.css-houdini.org/css-properties-values-api-1/#parsing-syntax>
-#[derive(Debug, Clone, Default, MallocSizeOf, PartialEq)]
+#[derive(Debug, Clone, Default, MallocSizeOf, PartialEq, ToShmem)]
pub struct Descriptor {
/// The parsed components, if any.
/// TODO: Could be a Box<[]> if that supported const construction.
@@ -54,6 +54,82 @@ impl Descriptor {
self.specified.as_deref()
}
+ /// Parse a syntax descriptor from a stream of tokens
+ /// https://drafts.csswg.org/css-values-5/#typedef-syntax
+ #[inline]
+ pub fn from_css_parser<'i>(input: &mut CSSParser<'i, '_>) -> Result<Self, StyleParseError<'i>> {
+ //TODO(bug 2006624): Should also accept <syntax-string>
+ let mut components = vec![];
+ loop {
+ let name = Self::try_parse_component_name(input).map_err(|err| {
+ input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(err))
+ })?;
+
+ let multiplier = if name.is_pre_multiplied() {
+ None
+ } else {
+ Self::try_parse_multiplier(input)
+ };
+
+ let component = Component { multiplier, name };
+ components.push(component);
+ let Ok(delim) = input.next() else { break };
+
+ if delim != &Token::Delim('|') {
+ return Err(
+ input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(
+ ParseError::ExpectedPipeBetweenComponents,
+ )),
+ );
+ }
+ }
+
+ Ok(Self {
+ components,
+ specified: None,
+ })
+ }
+
+ fn try_parse_multiplier<'i>(input: &mut CSSParser<'i, '_>) -> Option<Multiplier> {
+ input
+ .try_parse(|input| {
+ let next = input.next().map_err(|_| ())?;
+ match next {
+ Token::Delim('+') => Ok(Multiplier::Space),
+ Token::Delim('#') => Ok(Multiplier::Comma),
+ _ => Err(()),
+ }
+ })
+ .ok()
+ }
+
+ fn try_parse_component_name<'i>(
+ input: &mut CSSParser<'i, '_>,
+ ) -> Result<ComponentName, ParseError> {
+ if input.try_parse(|input| input.expect_delim('<')).is_ok() {
+ let name = Self::parse_component_data_type_name(input)?;
+ input
+ .expect_delim('>')
+ .map_err(|_| ParseError::UnclosedDataTypeName)?;
+ Ok(ComponentName::DataType(name))
+ } else {
+ input.try_parse(|input| {
+ let name = CustomIdent::parse(input, &[]).map_err(|_| ParseError::InvalidName)?;
+ Ok(ComponentName::Ident(name))
+ })
+ }
+ }
+
+ fn parse_component_data_type_name<'i>(
+ input: &mut CSSParser<'i, '_>,
+ ) -> Result<DataType, ParseError> {
+ input
+ .expect_ident()
+ .ok()
+ .and_then(|n| DataType::from_str(n))
+ .ok_or(ParseError::UnknownDataTypeName)
+ }
+
/// Parse a syntax descriptor.
/// https://drafts.css-houdini.org/css-properties-values-api-1/#consume-a-syntax-definition
pub fn from_str(css: &str, save_specified: bool) -> Result<Self, ParseError> {
@@ -88,9 +164,9 @@ impl Descriptor {
// nulls in the parser specially.
let mut components = vec![];
{
- let mut parser = Parser::new(input, &mut components);
+ let mut input = Parser::new(input, &mut components);
// 5. Repeatedly consume the next input code point from stream.
- parser.parse()?;
+ input.parse()?;
}
Ok(Self {
components,
@@ -174,7 +250,7 @@ impl ToCss for Multiplier {
}
/// <https://drafts.css-houdini.org/css-properties-values-api-1/#syntax-component>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub struct Component {
name: ComponentName,
multiplier: Option<Multiplier>,
@@ -220,7 +296,7 @@ impl ToCss for Component {
}
/// <https://drafts.css-houdini.org/css-properties-values-api-1/#syntax-component-name>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
pub enum ComponentName {
/// <https://drafts.css-houdini.org/css-properties-values-api-1/#data-type-name>
DataType(DataType),