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