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 super::{
8    rule::Descriptors as PropertyDescriptors,
9    syntax::{
10        data_type::DataType, Component as SyntaxComponent, ComponentName, Descriptor, Multiplier,
11    },
12};
13use crate::custom_properties::{AttrTaint, ComputedValue as ComputedPropertyValue};
14use crate::derives::*;
15use crate::parser::{Parse, ParserContext};
16use crate::properties;
17use crate::properties::{CSSWideKeyword, CustomDeclarationValue};
18use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
19use crate::values::{
20    animated::{self, Animate, Procedure},
21    computed::{self, ToComputedValue},
22    specified, CustomIdent,
23};
24use crate::{Namespace, Prefix};
25use cssparser::{BasicParseErrorKind, ParseErrorKind, Parser as CSSParser, TokenSerializationType};
26use rustc_hash::FxHashMap;
27use selectors::matching::QuirksMode;
28use servo_arc::Arc;
29use smallvec::SmallVec;
30use std::fmt::{self, Write};
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(Clone, Debug, MallocSizeOf, ToCss, ToComputedValue, ToResolvedValue, ToShmem)]
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    /// Flag indicating whether this value is tainted by an attr().
215    #[css(skip)]
216    pub attr_tainted: bool,
217}
218
219impl<Component: PartialEq> PartialEq for Value<Component> {
220    // Ignore the url_data field when comparing values for equality.
221    fn eq(&self, other: &Self) -> bool {
222        self.v == other.v
223    }
224}
225
226impl<Component: Animate> Animate for Value<Component> {
227    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
228        let v = self.v.animate(&other.v, procedure)?;
229        Ok(Value {
230            v,
231            url_data: self.url_data.clone(),
232            attr_tainted: self.attr_tainted,
233        })
234    }
235}
236
237impl<Component> Value<Component> {
238    /// Creates a new registered custom property value.
239    pub fn new(v: ValueInner<Component>, url_data: UrlExtraData) -> Self {
240        Self {
241            v,
242            url_data,
243            attr_tainted: Default::default(),
244        }
245    }
246
247    /// Creates a new registered custom property value presumed to have universal syntax.
248    pub fn universal(var: Arc<ComputedPropertyValue>) -> Self {
249        let attr_tainted = var.is_attr_tainted();
250        let url_data = var.url_data.clone();
251        let v = ValueInner::Universal(var);
252        Self {
253            v,
254            url_data,
255            attr_tainted,
256        }
257    }
258}
259
260impl<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>
261    Value<GenericValueComponent<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>>
262where
263    Self: ToCss,
264{
265    fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) {
266        match &self.v {
267            ValueInner::Component(component) => component.serialization_types(),
268            ValueInner::Universal(_) => unreachable!(),
269            ValueInner::List(list) => list
270                .components
271                .first()
272                .map_or(Default::default(), |f| f.serialization_types()),
273        }
274    }
275
276    /// Convert to an untyped variable value.
277    pub fn to_variable_value(&self) -> ComputedPropertyValue {
278        if let ValueInner::Universal(ref value) = self.v {
279            return (**value).clone();
280        }
281        let serialization_types = self.serialization_types();
282        ComputedPropertyValue::new(
283            self.to_css_string(),
284            &self.url_data,
285            serialization_types.0,
286            serialization_types.1,
287        )
288    }
289}
290
291/// A specified registered custom property value.
292#[derive(
293    Animate, ToComputedValue, ToResolvedValue, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToShmem,
294)]
295pub enum ValueInner<Component> {
296    /// A single specified component value whose syntax descriptor component did not have a
297    /// multiplier.
298    Component(Component),
299    /// A specified value whose syntax descriptor was the universal syntax definition.
300    #[animation(error)]
301    Universal(#[ignore_malloc_size_of = "Arc"] Arc<ComputedPropertyValue>),
302    /// A list of specified component values whose syntax descriptor component had a multiplier.
303    List(#[animation(field_bound)] ComponentList<Component>),
304}
305
306/// Specified custom property value.
307pub type SpecifiedValue = Value<SpecifiedValueComponent>;
308
309/// Computed custom property value.
310pub type ComputedValue = Value<ComputedValueComponent>;
311
312impl SpecifiedValue {
313    /// Convert a registered custom property to a Computed custom property value, given input and a
314    /// property registration.
315    pub fn compute<'i, 't>(
316        input: &mut CSSParser<'i, 't>,
317        registration: &PropertyDescriptors,
318        namespaces: Option<&FxHashMap<Prefix, Namespace>>,
319        url_data: &UrlExtraData,
320        context: &computed::Context,
321        allow_computationally_dependent: AllowComputationallyDependent,
322        attr_taint: AttrTaint,
323    ) -> Result<ComputedValue, ()> {
324        debug_assert!(!registration.is_universal(), "Shouldn't be needed");
325        let Some(ref syntax) = registration.syntax else {
326            return Err(());
327        };
328        let Ok(value) = Self::parse(
329            input,
330            syntax,
331            url_data,
332            namespaces,
333            allow_computationally_dependent,
334            attr_taint,
335        ) else {
336            return Err(());
337        };
338
339        Ok(value.to_computed_value(context))
340    }
341
342    /// Parse and validate a registered custom property value according to its syntax descriptor,
343    /// and check for computational independence.
344    pub fn parse<'i, 't>(
345        mut input: &mut CSSParser<'i, 't>,
346        syntax: &Descriptor,
347        url_data: &UrlExtraData,
348        namespaces: Option<&FxHashMap<Prefix, Namespace>>,
349        allow_computationally_dependent: AllowComputationallyDependent,
350        attr_taint: AttrTaint,
351    ) -> Result<Self, StyleParseError<'i>> {
352        if syntax.is_universal() {
353            let parsed = ComputedPropertyValue::parse(&mut input, namespaces, url_data)?;
354            return Ok(Self::new(
355                ValueInner::Universal(Arc::new(parsed)),
356                url_data.clone(),
357            ));
358        }
359
360        let mut values = SmallComponentVec::new();
361        let mut multiplier = None;
362        {
363            let mut parser = Parser::new(syntax, &mut values, &mut multiplier);
364            parser.parse(
365                &mut input,
366                url_data,
367                allow_computationally_dependent,
368                attr_taint,
369            )?;
370        }
371        let v = if let Some(multiplier) = multiplier {
372            ValueInner::List(ComponentList {
373                multiplier,
374                components: values.to_vec().into(),
375            })
376        } else {
377            ValueInner::Component(values[0].clone())
378        };
379        Ok(Self::new(v, url_data.clone()))
380    }
381}
382
383impl ComputedValue {
384    fn to_declared_value(&self) -> properties::CustomDeclarationValue {
385        if let ValueInner::Universal(ref var) = self.v {
386            return properties::CustomDeclarationValue::Unparsed(Arc::clone(var));
387        }
388        properties::CustomDeclarationValue::Parsed(Arc::new(ToComputedValue::from_computed_value(
389            self,
390        )))
391    }
392
393    /// Returns the contained variable value if it exists, otherwise `None`.
394    pub fn as_universal(&self) -> Option<&Arc<ComputedPropertyValue>> {
395        if let ValueInner::Universal(ref var) = self.v {
396            Some(var)
397        } else {
398            None
399        }
400    }
401}
402
403/// Whether the computed value parsing should allow computationaly dependent values like 3em or
404/// var(-foo).
405///
406/// https://drafts.css-houdini.org/css-properties-values-api-1/#computationally-independent
407pub enum AllowComputationallyDependent {
408    /// Only computationally independent values are allowed.
409    No,
410    /// Computationally independent and dependent values are allowed.
411    Yes,
412}
413
414type SmallComponentVec = SmallVec<[SpecifiedValueComponent; 1]>;
415
416struct Parser<'a> {
417    syntax: &'a Descriptor,
418    output: &'a mut SmallComponentVec,
419    output_multiplier: &'a mut Option<Multiplier>,
420}
421
422impl<'a> Parser<'a> {
423    fn new(
424        syntax: &'a Descriptor,
425        output: &'a mut SmallComponentVec,
426        output_multiplier: &'a mut Option<Multiplier>,
427    ) -> Self {
428        Self {
429            syntax,
430            output,
431            output_multiplier,
432        }
433    }
434
435    fn parse<'i, 't>(
436        &mut self,
437        input: &mut CSSParser<'i, 't>,
438        url_data: &UrlExtraData,
439        allow_computationally_dependent: AllowComputationallyDependent,
440        attr_taint: AttrTaint,
441    ) -> Result<(), StyleParseError<'i>> {
442        use self::AllowComputationallyDependent::*;
443        let parsing_mode = match allow_computationally_dependent {
444            No => ParsingMode::DISALLOW_COMPUTATIONALLY_DEPENDENT,
445            Yes => ParsingMode::DEFAULT,
446        };
447        let ref context = ParserContext::new(
448            Origin::Author,
449            url_data,
450            Some(CssRuleType::Style),
451            parsing_mode,
452            QuirksMode::NoQuirks,
453            /* namespaces = */ Default::default(),
454            None,
455            None,
456            attr_taint,
457        );
458        for component in self.syntax.components.iter() {
459            let result = input.try_parse(|input| {
460                input.parse_entirely(|input| {
461                    Self::parse_value(context, input, &component.unpremultiplied())
462                })
463            });
464            let Ok(values) = result else { continue };
465            self.output.extend(values);
466            *self.output_multiplier = component.multiplier();
467            break;
468        }
469        if self.output.is_empty() {
470            return Err(input.new_error(BasicParseErrorKind::EndOfInput));
471        }
472        Ok(())
473    }
474
475    fn parse_value<'i, 't>(
476        context: &ParserContext,
477        input: &mut CSSParser<'i, 't>,
478        component: &SyntaxComponent,
479    ) -> Result<SmallComponentVec, StyleParseError<'i>> {
480        let mut values = SmallComponentVec::new();
481        values.push(Self::parse_component_without_multiplier(
482            context, input, component,
483        )?);
484
485        if let Some(multiplier) = component.multiplier() {
486            loop {
487                let result = Self::expect_multiplier(input, &multiplier);
488                if Self::expect_multiplier_yielded_eof_error(&result) {
489                    break;
490                }
491                result?;
492                values.push(Self::parse_component_without_multiplier(
493                    context, input, component,
494                )?);
495            }
496        }
497        Ok(values)
498    }
499
500    fn parse_component_without_multiplier<'i, 't>(
501        context: &ParserContext,
502        input: &mut CSSParser<'i, 't>,
503        component: &SyntaxComponent,
504    ) -> Result<SpecifiedValueComponent, StyleParseError<'i>> {
505        let data_type = match component.name() {
506            ComponentName::DataType(ty) => ty,
507            ComponentName::Ident(ref name) => {
508                let ident = CustomIdent::parse(input, &[])?;
509                if ident != *name {
510                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
511                }
512                return Ok(SpecifiedValueComponent::CustomIdent(ident));
513            },
514        };
515
516        let value = match data_type {
517            DataType::Length => {
518                SpecifiedValueComponent::Length(specified::Length::parse(context, input)?)
519            },
520            DataType::Number => {
521                SpecifiedValueComponent::Number(specified::Number::parse(context, input)?)
522            },
523            DataType::Percentage => {
524                SpecifiedValueComponent::Percentage(specified::Percentage::parse(context, input)?)
525            },
526            DataType::LengthPercentage => SpecifiedValueComponent::LengthPercentage(
527                specified::LengthPercentage::parse(context, input)?,
528            ),
529            DataType::Color => {
530                SpecifiedValueComponent::Color(specified::Color::parse(context, input)?)
531            },
532            DataType::Image => {
533                SpecifiedValueComponent::Image(specified::Image::parse(context, input)?)
534            },
535            DataType::Url => {
536                SpecifiedValueComponent::Url(specified::url::SpecifiedUrl::parse(context, input)?)
537            },
538            DataType::Integer => {
539                SpecifiedValueComponent::Integer(specified::Integer::parse(context, input)?)
540            },
541            DataType::Angle => {
542                SpecifiedValueComponent::Angle(specified::Angle::parse(context, input)?)
543            },
544            DataType::Time => {
545                SpecifiedValueComponent::Time(specified::Time::parse(context, input)?)
546            },
547            DataType::Resolution => {
548                SpecifiedValueComponent::Resolution(specified::Resolution::parse(context, input)?)
549            },
550            DataType::TransformFunction => SpecifiedValueComponent::TransformFunction(
551                specified::Transform::parse(context, input)?,
552            ),
553            DataType::CustomIdent => {
554                let name = CustomIdent::parse(input, &[])?;
555                SpecifiedValueComponent::CustomIdent(name)
556            },
557            DataType::TransformList => {
558                let mut values = vec![];
559                let Some(multiplier) = component.unpremultiplied().multiplier() else {
560                    debug_assert!(false, "Unpremultiplied <transform-list> had no multiplier?");
561                    return Err(
562                        input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(
563                            PropertySyntaxParseError::UnexpectedEOF,
564                        )),
565                    );
566                };
567                debug_assert_eq!(multiplier, Multiplier::Space);
568                loop {
569                    values.push(SpecifiedValueComponent::TransformFunction(
570                        specified::Transform::parse(context, input)?,
571                    ));
572                    let result = Self::expect_multiplier(input, &multiplier);
573                    if Self::expect_multiplier_yielded_eof_error(&result) {
574                        break;
575                    }
576                    result?;
577                }
578                let list = ComponentList {
579                    multiplier,
580                    components: values.into(),
581                };
582                SpecifiedValueComponent::TransformList(list)
583            },
584            DataType::String => {
585                let string = input.expect_string()?;
586                SpecifiedValueComponent::String(string.as_ref().to_owned().into())
587            },
588        };
589        Ok(value)
590    }
591
592    fn expect_multiplier_yielded_eof_error<'i>(result: &Result<(), StyleParseError<'i>>) -> bool {
593        matches!(
594            result,
595            Err(StyleParseError {
596                kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
597                ..
598            })
599        )
600    }
601
602    fn expect_multiplier<'i, 't>(
603        input: &mut CSSParser<'i, 't>,
604        multiplier: &Multiplier,
605    ) -> Result<(), StyleParseError<'i>> {
606        match multiplier {
607            Multiplier::Space => {
608                input.expect_whitespace()?;
609                if input.is_exhausted() {
610                    // If there was trailing whitespace, do not interpret it as a multiplier
611                    return Err(input.new_error(BasicParseErrorKind::EndOfInput));
612                }
613                Ok(())
614            },
615            Multiplier::Comma => Ok(input.expect_comma()?),
616        }
617    }
618}
619
620/// An animated value for custom property.
621#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
622pub struct CustomAnimatedValue {
623    /// The name of the custom property.
624    pub(crate) name: crate::custom_properties::Name,
625    /// The computed value of the custom property.
626    /// `None` represents the guaranteed-invalid value.
627    pub(crate) value: Option<ComputedValue>,
628}
629
630impl Animate for CustomAnimatedValue {
631    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
632        if self.name != other.name {
633            return Err(());
634        }
635        let value = self.value.animate(&other.value, procedure)?;
636        Ok(Self {
637            name: self.name.clone(),
638            value,
639        })
640    }
641}
642
643impl CustomAnimatedValue {
644    pub(crate) fn from_computed(
645        name: &crate::custom_properties::Name,
646        value: Option<&ComputedValue>,
647    ) -> Self {
648        Self {
649            name: name.clone(),
650            value: value.cloned(),
651        }
652    }
653
654    pub(crate) fn from_declaration(
655        declaration: &properties::CustomDeclaration,
656        context: &mut computed::Context,
657    ) -> Option<Self> {
658        let computed_value = match declaration.value {
659            properties::CustomDeclarationValue::Unparsed(ref value) => Some({
660                debug_assert!(
661                    context.builder.stylist.is_some(),
662                    "Need a Stylist to get property registration!"
663                );
664                let registration = context
665                    .builder
666                    .stylist
667                    .unwrap()
668                    .get_custom_property_registration(&declaration.name);
669                if registration.is_universal() {
670                    // FIXME: Do we need to perform substitution here somehow?
671                    ComputedValue::new(
672                        ValueInner::Universal(Arc::clone(value)),
673                        value.url_data.clone(),
674                    )
675                } else {
676                    let mut input = cssparser::ParserInput::new(&value.css);
677                    let mut input = CSSParser::new(&mut input);
678                    SpecifiedValue::compute(
679                        &mut input,
680                        registration,
681                        None,
682                        &value.url_data,
683                        context,
684                        AllowComputationallyDependent::Yes,
685                        /* attr_taint */ Default::default(),
686                    )
687                    .unwrap_or_else(|_| {
688                        ComputedValue::new(
689                            ValueInner::Universal(Arc::clone(value)),
690                            value.url_data.clone(),
691                        )
692                    })
693                }
694            }),
695            properties::CustomDeclarationValue::Parsed(ref v) => Some(v.to_computed_value(context)),
696            properties::CustomDeclarationValue::CSSWideKeyword(keyword) => {
697                let stylist = context.builder.stylist.unwrap();
698                let registration = stylist.get_custom_property_registration(&declaration.name);
699                match keyword {
700                    CSSWideKeyword::Initial => stylist
701                        .get_custom_property_initial_values()
702                        .get(registration, &declaration.name),
703                    CSSWideKeyword::Inherit => context
704                        .builder
705                        .inherited_custom_properties()
706                        .get(registration, &declaration.name),
707                    CSSWideKeyword::Unset => {
708                        if registration.inherits() {
709                            context
710                                .builder
711                                .inherited_custom_properties()
712                                .get(registration, &declaration.name)
713                        } else {
714                            stylist
715                                .get_custom_property_initial_values()
716                                .get(registration, &declaration.name)
717                        }
718                    },
719                    // FIXME(emilio, bug 1533327): I think revert (and
720                    // revert-layer) handling is not fine here, but what to
721                    // do instead?
722                    //
723                    // Seems we'd need the computed value as if it was
724                    // revert, somehow. Returning `None` seems fine for now...
725                    //
726                    // Note that once this is fixed, this method should be
727                    // able to return `Self` instead of Option<Self>`.
728                    CSSWideKeyword::Revert
729                    | CSSWideKeyword::RevertRule
730                    | CSSWideKeyword::RevertLayer => return None,
731                }
732                .cloned()
733            },
734        };
735        Some(Self {
736            name: declaration.name.clone(),
737            value: computed_value,
738        })
739    }
740
741    pub(crate) fn to_declaration(&self) -> properties::PropertyDeclaration {
742        properties::PropertyDeclaration::Custom(properties::CustomDeclaration {
743            name: self.name.clone(),
744            value: match &self.value {
745                Some(value) => value.to_declared_value(),
746                None => CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial),
747            },
748        })
749    }
750}