tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

value.rs (23968B)


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