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