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