style/properties_and_values/
value.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Parsing for registered custom properties.
6
7use std::fmt::{self, Write};
8
9use super::{
10    registry::PropertyRegistrationData,
11    syntax::{
12        data_type::DataType, Component as SyntaxComponent, ComponentName, Descriptor, Multiplier,
13    },
14};
15use crate::custom_properties::ComputedValue as ComputedPropertyValue;
16use crate::parser::{Parse, ParserContext};
17use crate::properties;
18use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
19use crate::values::{
20    animated::{self, Animate, Procedure},
21    computed::{self, ToComputedValue},
22    specified, CustomIdent,
23};
24use cssparser::{BasicParseErrorKind, ParseErrorKind, Parser as CSSParser, TokenSerializationType};
25use selectors::matching::QuirksMode;
26use servo_arc::Arc;
27use smallvec::SmallVec;
28use style_traits::{
29    owned_str::OwnedStr, CssWriter, ParseError as StyleParseError, ParsingMode,
30    PropertySyntaxParseError, StyleParseErrorKind, ToCss,
31};
32
33/// A single component of the computed value.
34pub type ComputedValueComponent = GenericValueComponent<
35    computed::Length,
36    computed::Number,
37    computed::Percentage,
38    computed::LengthPercentage,
39    computed::Color,
40    computed::Image,
41    computed::url::ComputedUrl,
42    computed::Integer,
43    computed::Angle,
44    computed::Time,
45    computed::Resolution,
46    computed::Transform,
47>;
48
49/// A single component of the specified value.
50pub type SpecifiedValueComponent = GenericValueComponent<
51    specified::Length,
52    specified::Number,
53    specified::Percentage,
54    specified::LengthPercentage,
55    specified::Color,
56    specified::Image,
57    specified::url::SpecifiedUrl,
58    specified::Integer,
59    specified::Angle,
60    specified::Time,
61    specified::Resolution,
62    specified::Transform,
63>;
64
65impl<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>
66    GenericValueComponent<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>
67{
68    fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) {
69        let first_token_type = match self {
70            Self::Length(_) | Self::Angle(_) | Self::Time(_) | Self::Resolution(_) => {
71                TokenSerializationType::Dimension
72            },
73            Self::Number(_) | Self::Integer(_) => TokenSerializationType::Number,
74            Self::Percentage(_) | Self::LengthPercentage(_) => TokenSerializationType::Percentage,
75            Self::Color(_)
76            | Self::Image(_)
77            | Self::Url(_)
78            | Self::TransformFunction(_)
79            | Self::TransformList(_) => TokenSerializationType::Function,
80            Self::CustomIdent(_) => TokenSerializationType::Ident,
81            Self::String(_) => TokenSerializationType::Other,
82        };
83        let last_token_type = if first_token_type == TokenSerializationType::Function {
84            TokenSerializationType::Other
85        } else {
86            first_token_type
87        };
88        (first_token_type, last_token_type)
89    }
90}
91
92/// A generic enum used for both specified value components and computed value components.
93#[derive(
94    Animate, Clone, ToCss, ToComputedValue, ToResolvedValue, Debug, MallocSizeOf, PartialEq, ToShmem,
95)]
96#[animation(no_bound(Image, Url))]
97pub enum GenericValueComponent<
98    Length,
99    Number,
100    Percentage,
101    LengthPercentage,
102    Color,
103    Image,
104    Url,
105    Integer,
106    Angle,
107    Time,
108    Resolution,
109    TransformFunction,
110> {
111    /// A <length> value
112    Length(Length),
113    /// A <number> value
114    Number(Number),
115    /// A <percentage> value
116    Percentage(Percentage),
117    /// A <length-percentage> value
118    LengthPercentage(LengthPercentage),
119    /// A <color> value
120    Color(Color),
121    /// An <image> value
122    #[animation(error)]
123    Image(Image),
124    /// A <url> value
125    #[animation(error)]
126    Url(Url),
127    /// An <integer> value
128    Integer(Integer),
129    /// An <angle> value
130    Angle(Angle),
131    /// A <time> value
132    Time(Time),
133    /// A <resolution> value
134    Resolution(Resolution),
135    /// A <transform-function> value
136    /// TODO(bug 1884606): <transform-function> `none` should not interpolate.
137    TransformFunction(TransformFunction),
138    /// A <custom-ident> value
139    #[animation(error)]
140    CustomIdent(CustomIdent),
141    /// A <transform-list> value, equivalent to <transform-function>+
142    /// TODO(bug 1884606): <transform-list> `none` should not interpolate.
143    TransformList(ComponentList<Self>),
144    /// A <string> value
145    #[animation(error)]
146    String(OwnedStr),
147}
148
149/// A list of component values, including the list's multiplier.
150#[derive(Clone, ToComputedValue, ToResolvedValue, Debug, MallocSizeOf, PartialEq, ToShmem)]
151pub struct ComponentList<Component> {
152    /// Multiplier
153    pub multiplier: Multiplier,
154    /// The list of components contained.
155    pub components: crate::OwnedSlice<Component>,
156}
157
158impl<Component: Animate> Animate for ComponentList<Component> {
159    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
160        if self.multiplier != other.multiplier {
161            return Err(());
162        }
163        let components = animated::lists::by_computed_value::animate(
164            &self.components,
165            &other.components,
166            procedure,
167        )?;
168        Ok(Self {
169            multiplier: self.multiplier,
170            components,
171        })
172    }
173}
174
175impl<Component: ToCss> ToCss for ComponentList<Component> {
176    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
177    where
178        W: Write,
179    {
180        let mut iter = self.components.iter();
181        let Some(first) = iter.next() else {
182            return Ok(());
183        };
184        first.to_css(dest)?;
185
186        // The separator implied by the multiplier for this list.
187        let separator = match self.multiplier {
188            // <https://drafts.csswg.org/cssom-1/#serialize-a-whitespace-separated-list>
189            Multiplier::Space => " ",
190            // <https://drafts.csswg.org/cssom-1/#serialize-a-comma-separated-list>
191            Multiplier::Comma => ", ",
192        };
193        for component in iter {
194            dest.write_str(separator)?;
195            component.to_css(dest)?;
196        }
197        Ok(())
198    }
199}
200
201/// A struct for a single specified registered custom property value that includes its original URL
202// data so the value can be uncomputed later.
203#[derive(
204    Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
205)]
206pub struct Value<Component> {
207    /// The registered custom property value.
208    pub(crate) v: ValueInner<Component>,
209    /// The URL data of the registered custom property from before it was computed. This is
210    /// necessary to uncompute registered custom properties.
211    #[css(skip)]
212    url_data: UrlExtraData,
213}
214
215impl<Component: Animate> Animate for Value<Component> {
216    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
217        let v = self.v.animate(&other.v, procedure)?;
218        Ok(Value {
219            v,
220            url_data: self.url_data.clone(),
221        })
222    }
223}
224
225impl<Component> Value<Component> {
226    /// Creates a new registered custom property value.
227    pub fn new(v: ValueInner<Component>, url_data: UrlExtraData) -> Self {
228        Self { v, url_data }
229    }
230
231    /// Creates a new registered custom property value presumed to have universal syntax.
232    pub fn universal(var: Arc<ComputedPropertyValue>) -> Self {
233        let url_data = var.url_data.clone();
234        let v = ValueInner::Universal(var);
235        Self { v, url_data }
236    }
237}
238
239/// A specified registered custom property value.
240#[derive(
241    Animate, ToComputedValue, ToResolvedValue, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToShmem,
242)]
243pub enum ValueInner<Component> {
244    /// A single specified component value whose syntax descriptor component did not have a
245    /// multiplier.
246    Component(Component),
247    /// A specified value whose syntax descriptor was the universal syntax definition.
248    #[animation(error)]
249    Universal(#[ignore_malloc_size_of = "Arc"] Arc<ComputedPropertyValue>),
250    /// A list of specified component values whose syntax descriptor component had a multiplier.
251    List(#[animation(field_bound)] ComponentList<Component>),
252}
253
254/// Specified custom property value.
255pub type SpecifiedValue = Value<SpecifiedValueComponent>;
256
257/// Computed custom property value.
258pub type ComputedValue = Value<ComputedValueComponent>;
259
260impl SpecifiedValue {
261    /// Convert a registered custom property to a Computed custom property value, given input and a
262    /// property registration.
263    pub fn compute<'i, 't>(
264        input: &mut CSSParser<'i, 't>,
265        registration: &PropertyRegistrationData,
266        url_data: &UrlExtraData,
267        context: &computed::Context,
268        allow_computationally_dependent: AllowComputationallyDependent,
269    ) -> Result<ComputedValue, ()> {
270        debug_assert!(!registration.syntax.is_universal(), "Shouldn't be needed");
271        let Ok(value) = Self::parse(
272            input,
273            &registration.syntax,
274            url_data,
275            allow_computationally_dependent,
276        ) else {
277            return Err(());
278        };
279
280        Ok(value.to_computed_value(context))
281    }
282
283    /// Parse and validate a registered custom property value according to its syntax descriptor,
284    /// and check for computational independence.
285    pub fn parse<'i, 't>(
286        mut input: &mut CSSParser<'i, 't>,
287        syntax: &Descriptor,
288        url_data: &UrlExtraData,
289        allow_computationally_dependent: AllowComputationallyDependent,
290    ) -> Result<Self, StyleParseError<'i>> {
291        if syntax.is_universal() {
292            let parsed = ComputedPropertyValue::parse(&mut input, url_data)?;
293            return Ok(SpecifiedValue {
294                v: ValueInner::Universal(Arc::new(parsed)),
295                url_data: url_data.clone(),
296            });
297        }
298
299        let mut values = SmallComponentVec::new();
300        let mut multiplier = None;
301        {
302            let mut parser = Parser::new(syntax, &mut values, &mut multiplier);
303            parser.parse(&mut input, url_data, allow_computationally_dependent)?;
304        }
305        let v = if let Some(multiplier) = multiplier {
306            ValueInner::List(ComponentList {
307                multiplier,
308                components: values.to_vec().into(),
309            })
310        } else {
311            ValueInner::Component(values[0].clone())
312        };
313        Ok(Self {
314            v,
315            url_data: url_data.clone(),
316        })
317    }
318}
319
320impl ComputedValue {
321    fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) {
322        match &self.v {
323            ValueInner::Component(component) => component.serialization_types(),
324            ValueInner::Universal(_) => unreachable!(),
325            ValueInner::List(list) => list
326                .components
327                .first()
328                .map_or(Default::default(), |f| f.serialization_types()),
329        }
330    }
331
332    fn to_declared_value(&self) -> properties::CustomDeclarationValue {
333        if let ValueInner::Universal(ref var) = self.v {
334            return properties::CustomDeclarationValue::Unparsed(Arc::clone(var));
335        }
336        properties::CustomDeclarationValue::Parsed(Arc::new(ToComputedValue::from_computed_value(
337            self,
338        )))
339    }
340
341    /// Returns the contained variable value if it exists, otherwise `None`.
342    pub fn as_universal(&self) -> Option<&Arc<ComputedPropertyValue>> {
343        if let ValueInner::Universal(ref var) = self.v {
344            Some(var)
345        } else {
346            None
347        }
348    }
349
350    /// Convert to an untyped variable value.
351    pub fn to_variable_value(&self) -> ComputedPropertyValue {
352        if let ValueInner::Universal(ref value) = self.v {
353            return (**value).clone();
354        }
355        let serialization_types = self.serialization_types();
356        ComputedPropertyValue::new(
357            self.to_css_string(),
358            &self.url_data,
359            serialization_types.0,
360            serialization_types.1,
361        )
362    }
363}
364
365/// Whether the computed value parsing should allow computationaly dependent values like 3em or
366/// var(-foo).
367///
368/// https://drafts.css-houdini.org/css-properties-values-api-1/#computationally-independent
369pub enum AllowComputationallyDependent {
370    /// Only computationally independent values are allowed.
371    No,
372    /// Computationally independent and dependent values are allowed.
373    Yes,
374}
375
376type SmallComponentVec = SmallVec<[SpecifiedValueComponent; 1]>;
377
378struct Parser<'a> {
379    syntax: &'a Descriptor,
380    output: &'a mut SmallComponentVec,
381    output_multiplier: &'a mut Option<Multiplier>,
382}
383
384impl<'a> Parser<'a> {
385    fn new(
386        syntax: &'a Descriptor,
387        output: &'a mut SmallComponentVec,
388        output_multiplier: &'a mut Option<Multiplier>,
389    ) -> Self {
390        Self {
391            syntax,
392            output,
393            output_multiplier,
394        }
395    }
396
397    fn parse<'i, 't>(
398        &mut self,
399        input: &mut CSSParser<'i, 't>,
400        url_data: &UrlExtraData,
401        allow_computationally_dependent: AllowComputationallyDependent,
402    ) -> Result<(), StyleParseError<'i>> {
403        use self::AllowComputationallyDependent::*;
404        let parsing_mode = match allow_computationally_dependent {
405            No => ParsingMode::DISALLOW_COMPUTATIONALLY_DEPENDENT,
406            Yes => ParsingMode::DEFAULT,
407        };
408        let ref context = ParserContext::new(
409            Origin::Author,
410            url_data,
411            Some(CssRuleType::Style),
412            parsing_mode,
413            QuirksMode::NoQuirks,
414            /* namespaces = */ Default::default(),
415            None,
416            None,
417        );
418        for component in self.syntax.components.iter() {
419            let result = input.try_parse(|input| {
420                input.parse_entirely(|input| {
421                    Self::parse_value(context, input, &component.unpremultiplied())
422                })
423            });
424            let Ok(values) = result else { continue };
425            self.output.extend(values);
426            *self.output_multiplier = component.multiplier();
427            break;
428        }
429        if self.output.is_empty() {
430            return Err(input.new_error(BasicParseErrorKind::EndOfInput));
431        }
432        Ok(())
433    }
434
435    fn parse_value<'i, 't>(
436        context: &ParserContext,
437        input: &mut CSSParser<'i, 't>,
438        component: &SyntaxComponent,
439    ) -> Result<SmallComponentVec, StyleParseError<'i>> {
440        let mut values = SmallComponentVec::new();
441        values.push(Self::parse_component_without_multiplier(
442            context, input, component,
443        )?);
444
445        if let Some(multiplier) = component.multiplier() {
446            loop {
447                let result = Self::expect_multiplier(input, &multiplier);
448                if Self::expect_multiplier_yielded_eof_error(&result) {
449                    break;
450                }
451                result?;
452                values.push(Self::parse_component_without_multiplier(
453                    context, input, component,
454                )?);
455            }
456        }
457        Ok(values)
458    }
459
460    fn parse_component_without_multiplier<'i, 't>(
461        context: &ParserContext,
462        input: &mut CSSParser<'i, 't>,
463        component: &SyntaxComponent,
464    ) -> Result<SpecifiedValueComponent, StyleParseError<'i>> {
465        let data_type = match component.name() {
466            ComponentName::DataType(ty) => ty,
467            ComponentName::Ident(ref name) => {
468                let ident = CustomIdent::parse(input, &[])?;
469                if ident != *name {
470                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
471                }
472                return Ok(SpecifiedValueComponent::CustomIdent(ident));
473            },
474        };
475
476        let value = match data_type {
477            DataType::Length => {
478                SpecifiedValueComponent::Length(specified::Length::parse(context, input)?)
479            },
480            DataType::Number => {
481                SpecifiedValueComponent::Number(specified::Number::parse(context, input)?)
482            },
483            DataType::Percentage => {
484                SpecifiedValueComponent::Percentage(specified::Percentage::parse(context, input)?)
485            },
486            DataType::LengthPercentage => SpecifiedValueComponent::LengthPercentage(
487                specified::LengthPercentage::parse(context, input)?,
488            ),
489            DataType::Color => {
490                SpecifiedValueComponent::Color(specified::Color::parse(context, input)?)
491            },
492            DataType::Image => {
493                SpecifiedValueComponent::Image(specified::Image::parse(context, input)?)
494            },
495            DataType::Url => {
496                SpecifiedValueComponent::Url(specified::url::SpecifiedUrl::parse(context, input)?)
497            },
498            DataType::Integer => {
499                SpecifiedValueComponent::Integer(specified::Integer::parse(context, input)?)
500            },
501            DataType::Angle => {
502                SpecifiedValueComponent::Angle(specified::Angle::parse(context, input)?)
503            },
504            DataType::Time => {
505                SpecifiedValueComponent::Time(specified::Time::parse(context, input)?)
506            },
507            DataType::Resolution => {
508                SpecifiedValueComponent::Resolution(specified::Resolution::parse(context, input)?)
509            },
510            DataType::TransformFunction => SpecifiedValueComponent::TransformFunction(
511                specified::Transform::parse(context, input)?,
512            ),
513            DataType::CustomIdent => {
514                let name = CustomIdent::parse(input, &[])?;
515                SpecifiedValueComponent::CustomIdent(name)
516            },
517            DataType::TransformList => {
518                let mut values = vec![];
519                let Some(multiplier) = component.unpremultiplied().multiplier() else {
520                    debug_assert!(false, "Unpremultiplied <transform-list> had no multiplier?");
521                    return Err(
522                        input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(
523                            PropertySyntaxParseError::UnexpectedEOF,
524                        )),
525                    );
526                };
527                debug_assert_matches!(multiplier, Multiplier::Space);
528                loop {
529                    values.push(SpecifiedValueComponent::TransformFunction(
530                        specified::Transform::parse(context, input)?,
531                    ));
532                    let result = Self::expect_multiplier(input, &multiplier);
533                    if Self::expect_multiplier_yielded_eof_error(&result) {
534                        break;
535                    }
536                    result?;
537                }
538                let list = ComponentList {
539                    multiplier,
540                    components: values.into(),
541                };
542                SpecifiedValueComponent::TransformList(list)
543            },
544            DataType::String => {
545                let string = input.expect_string()?;
546                SpecifiedValueComponent::String(string.as_ref().to_owned().into())
547            },
548        };
549        Ok(value)
550    }
551
552    fn expect_multiplier_yielded_eof_error<'i>(result: &Result<(), StyleParseError<'i>>) -> bool {
553        matches!(
554            result,
555            Err(StyleParseError {
556                kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
557                ..
558            })
559        )
560    }
561
562    fn expect_multiplier<'i, 't>(
563        input: &mut CSSParser<'i, 't>,
564        multiplier: &Multiplier,
565    ) -> Result<(), StyleParseError<'i>> {
566        match multiplier {
567            Multiplier::Space => {
568                input.expect_whitespace()?;
569                if input.is_exhausted() {
570                    // If there was trailing whitespace, do not interpret it as a multiplier
571                    return Err(input.new_error(BasicParseErrorKind::EndOfInput));
572                }
573                Ok(())
574            },
575            Multiplier::Comma => Ok(input.expect_comma()?),
576        }
577    }
578}
579
580/// An animated value for custom property.
581#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
582pub struct CustomAnimatedValue {
583    /// The name of the custom property.
584    pub(crate) name: crate::custom_properties::Name,
585    /// The computed value of the custom property.
586    value: ComputedValue,
587}
588
589impl Animate for CustomAnimatedValue {
590    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
591        if self.name != other.name {
592            return Err(());
593        }
594        let value = self.value.animate(&other.value, procedure)?;
595        Ok(Self {
596            name: self.name.clone(),
597            value,
598        })
599    }
600}
601
602impl CustomAnimatedValue {
603    pub(crate) fn from_computed(
604        name: &crate::custom_properties::Name,
605        value: &ComputedValue,
606    ) -> Self {
607        Self {
608            name: name.clone(),
609            value: value.clone(),
610        }
611    }
612
613    pub(crate) fn from_declaration(
614        declaration: &properties::CustomDeclaration,
615        context: &mut computed::Context,
616        _initial: &properties::ComputedValues,
617    ) -> Option<Self> {
618        let computed_value = match declaration.value {
619            properties::CustomDeclarationValue::Unparsed(ref value) => {
620                debug_assert!(
621                    context.builder.stylist.is_some(),
622                    "Need a Stylist to get property registration!"
623                );
624                let registration = context
625                    .builder
626                    .stylist
627                    .unwrap()
628                    .get_custom_property_registration(&declaration.name);
629                if registration.syntax.is_universal() {
630                    // FIXME: Do we need to perform substitution here somehow?
631                    ComputedValue {
632                        v: ValueInner::Universal(Arc::clone(value)),
633                        url_data: value.url_data.clone(),
634                    }
635                } else {
636                    let mut input = cssparser::ParserInput::new(&value.css);
637                    let mut input = CSSParser::new(&mut input);
638                    SpecifiedValue::compute(
639                        &mut input,
640                        registration,
641                        &value.url_data,
642                        context,
643                        AllowComputationallyDependent::Yes,
644                    )
645                    .unwrap_or_else(|_| ComputedValue {
646                        v: ValueInner::Universal(Arc::clone(value)),
647                        url_data: value.url_data.clone(),
648                    })
649                }
650            },
651            properties::CustomDeclarationValue::Parsed(ref v) => v.to_computed_value(context),
652            // FIXME: This should be made to work to the extent possible like for non-custom
653            // properties (using `initial` at least to handle unset / inherit).
654            properties::CustomDeclarationValue::CSSWideKeyword(..) => return None,
655        };
656        Some(Self {
657            name: declaration.name.clone(),
658            value: computed_value,
659        })
660    }
661
662    pub(crate) fn to_declaration(&self) -> properties::PropertyDeclaration {
663        properties::PropertyDeclaration::Custom(properties::CustomDeclaration {
664            name: self.name.clone(),
665            value: self.value.to_declared_value(),
666        })
667    }
668}