Skip to main content

style/properties_and_values/
value.rs

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