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