Skip to main content

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