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