665 lines
23 KiB
Rust
665 lines
23 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
//! Parsing for registered custom properties.
|
|
|
|
use std::fmt::{self, Write};
|
|
|
|
use super::{
|
|
registry::PropertyRegistrationData,
|
|
syntax::{
|
|
data_type::DataType, Component as SyntaxComponent, ComponentName, Descriptor, Multiplier,
|
|
},
|
|
};
|
|
use crate::custom_properties::ComputedValue as ComputedPropertyValue;
|
|
use crate::parser::{Parse, ParserContext};
|
|
use crate::properties;
|
|
use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
|
|
use crate::values::{
|
|
animated::{self, Animate, Procedure},
|
|
computed::{self, ToComputedValue},
|
|
specified, CustomIdent,
|
|
};
|
|
use cssparser::{BasicParseErrorKind, ParseErrorKind, Parser as CSSParser, TokenSerializationType};
|
|
use selectors::matching::QuirksMode;
|
|
use servo_arc::Arc;
|
|
use smallvec::SmallVec;
|
|
use style_traits::{
|
|
owned_str::OwnedStr, CssWriter, ParseError as StyleParseError, ParsingMode,
|
|
PropertySyntaxParseError, StyleParseErrorKind, ToCss,
|
|
};
|
|
|
|
/// A single component of the computed value.
|
|
pub type ComputedValueComponent = GenericValueComponent<
|
|
computed::Length,
|
|
computed::Number,
|
|
computed::Percentage,
|
|
computed::LengthPercentage,
|
|
computed::Color,
|
|
computed::Image,
|
|
computed::url::ComputedUrl,
|
|
computed::Integer,
|
|
computed::Angle,
|
|
computed::Time,
|
|
computed::Resolution,
|
|
computed::Transform,
|
|
>;
|
|
|
|
/// A single component of the specified value.
|
|
pub type SpecifiedValueComponent = GenericValueComponent<
|
|
specified::Length,
|
|
specified::Number,
|
|
specified::Percentage,
|
|
specified::LengthPercentage,
|
|
specified::Color,
|
|
specified::Image,
|
|
specified::url::SpecifiedUrl,
|
|
specified::Integer,
|
|
specified::Angle,
|
|
specified::Time,
|
|
specified::Resolution,
|
|
specified::Transform,
|
|
>;
|
|
|
|
impl<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>
|
|
GenericValueComponent<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>
|
|
{
|
|
fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) {
|
|
let first_token_type = match self {
|
|
Self::Length(_) | Self::Angle(_) | Self::Time(_) | Self::Resolution(_) => {
|
|
TokenSerializationType::Dimension
|
|
},
|
|
Self::Number(_) | Self::Integer(_) => TokenSerializationType::Number,
|
|
Self::Percentage(_) | Self::LengthPercentage(_) => TokenSerializationType::Percentage,
|
|
Self::Color(_) |
|
|
Self::Image(_) |
|
|
Self::Url(_) |
|
|
Self::TransformFunction(_) |
|
|
Self::TransformList(_) => TokenSerializationType::Function,
|
|
Self::CustomIdent(_) => TokenSerializationType::Ident,
|
|
Self::String(_) => TokenSerializationType::Other,
|
|
};
|
|
let last_token_type = if first_token_type == TokenSerializationType::Function {
|
|
TokenSerializationType::Other
|
|
} else {
|
|
first_token_type
|
|
};
|
|
(first_token_type, last_token_type)
|
|
}
|
|
}
|
|
|
|
/// A generic enum used for both specified value components and computed value components.
|
|
#[derive(
|
|
Animate, Clone, ToCss, ToComputedValue, ToResolvedValue, Debug, MallocSizeOf, PartialEq, ToShmem
|
|
)]
|
|
#[animation(no_bound(Image, Url))]
|
|
pub enum GenericValueComponent<
|
|
Length,
|
|
Number,
|
|
Percentage,
|
|
LengthPercentage,
|
|
Color,
|
|
Image,
|
|
Url,
|
|
Integer,
|
|
Angle,
|
|
Time,
|
|
Resolution,
|
|
TransformFunction,
|
|
> {
|
|
/// A <length> value
|
|
Length(Length),
|
|
/// A <number> value
|
|
Number(Number),
|
|
/// A <percentage> value
|
|
Percentage(Percentage),
|
|
/// A <length-percentage> value
|
|
LengthPercentage(LengthPercentage),
|
|
/// A <color> value
|
|
Color(Color),
|
|
/// An <image> value
|
|
#[animation(error)]
|
|
Image(Image),
|
|
/// A <url> value
|
|
#[animation(error)]
|
|
Url(Url),
|
|
/// An <integer> value
|
|
Integer(Integer),
|
|
/// An <angle> value
|
|
Angle(Angle),
|
|
/// A <time> value
|
|
Time(Time),
|
|
/// A <resolution> value
|
|
Resolution(Resolution),
|
|
/// A <transform-function> value
|
|
/// TODO(bug 1884606): <transform-function> `none` should not interpolate.
|
|
TransformFunction(TransformFunction),
|
|
/// A <custom-ident> value
|
|
#[animation(error)]
|
|
CustomIdent(CustomIdent),
|
|
/// A <transform-list> value, equivalent to <transform-function>+
|
|
/// TODO(bug 1884606): <transform-list> `none` should not interpolate.
|
|
TransformList(ComponentList<Self>),
|
|
/// A <string> value
|
|
#[animation(error)]
|
|
String(OwnedStr),
|
|
}
|
|
|
|
/// A list of component values, including the list's multiplier.
|
|
#[derive(Clone, ToComputedValue, ToResolvedValue, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
|
pub struct ComponentList<Component> {
|
|
/// Multiplier
|
|
pub multiplier: Multiplier,
|
|
/// The list of components contained.
|
|
pub components: crate::OwnedSlice<Component>,
|
|
}
|
|
|
|
impl<Component: Animate> Animate for ComponentList<Component> {
|
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
if self.multiplier != other.multiplier {
|
|
return Err(());
|
|
}
|
|
let components = animated::lists::by_computed_value::animate(
|
|
&self.components,
|
|
&other.components,
|
|
procedure,
|
|
)?;
|
|
Ok(Self {
|
|
multiplier: self.multiplier,
|
|
components,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<Component: ToCss> ToCss for ComponentList<Component> {
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
|
where
|
|
W: Write,
|
|
{
|
|
let mut iter = self.components.iter();
|
|
let Some(first) = iter.next() else {
|
|
return Ok(());
|
|
};
|
|
first.to_css(dest)?;
|
|
|
|
// The separator implied by the multiplier for this list.
|
|
let separator = match self.multiplier {
|
|
// <https://drafts.csswg.org/cssom-1/#serialize-a-whitespace-separated-list>
|
|
Multiplier::Space => " ",
|
|
// <https://drafts.csswg.org/cssom-1/#serialize-a-comma-separated-list>
|
|
Multiplier::Comma => ", ",
|
|
};
|
|
for component in iter {
|
|
dest.write_str(separator)?;
|
|
component.to_css(dest)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// A struct for a single specified registered custom property value that includes its original URL
|
|
// data so the value can be uncomputed later.
|
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem)]
|
|
pub struct Value<Component> {
|
|
/// The registered custom property value.
|
|
pub(crate) v: ValueInner<Component>,
|
|
/// The URL data of the registered custom property from before it was computed. This is
|
|
/// necessary to uncompute registered custom properties.
|
|
#[css(skip)]
|
|
url_data: UrlExtraData,
|
|
}
|
|
|
|
impl<Component: Animate> Animate for Value<Component> {
|
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
let v = self.v.animate(&other.v, procedure)?;
|
|
Ok(Value {
|
|
v,
|
|
url_data: self.url_data.clone(),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<Component> Value<Component> {
|
|
/// Creates a new registered custom property value.
|
|
pub fn new(v: ValueInner<Component>, url_data: UrlExtraData) -> Self {
|
|
Self { v, url_data }
|
|
}
|
|
|
|
/// Creates a new registered custom property value presumed to have universal syntax.
|
|
pub fn universal(var: Arc<ComputedPropertyValue>) -> Self {
|
|
let url_data = var.url_data.clone();
|
|
let v = ValueInner::Universal(var);
|
|
Self { v, url_data }
|
|
}
|
|
}
|
|
|
|
/// A specified registered custom property value.
|
|
#[derive(
|
|
Animate, ToComputedValue, ToResolvedValue, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToShmem,
|
|
)]
|
|
pub enum ValueInner<Component> {
|
|
/// A single specified component value whose syntax descriptor component did not have a
|
|
/// multiplier.
|
|
Component(Component),
|
|
/// A specified value whose syntax descriptor was the universal syntax definition.
|
|
#[animation(error)]
|
|
Universal(#[ignore_malloc_size_of = "Arc"] Arc<ComputedPropertyValue>),
|
|
/// A list of specified component values whose syntax descriptor component had a multiplier.
|
|
List(#[animation(field_bound)] ComponentList<Component>),
|
|
}
|
|
|
|
/// Specified custom property value.
|
|
pub type SpecifiedValue = Value<SpecifiedValueComponent>;
|
|
|
|
/// Computed custom property value.
|
|
pub type ComputedValue = Value<ComputedValueComponent>;
|
|
|
|
impl SpecifiedValue {
|
|
/// Convert a registered custom property to a Computed custom property value, given input and a
|
|
/// property registration.
|
|
pub fn compute<'i, 't>(
|
|
input: &mut CSSParser<'i, 't>,
|
|
registration: &PropertyRegistrationData,
|
|
url_data: &UrlExtraData,
|
|
context: &computed::Context,
|
|
allow_computationally_dependent: AllowComputationallyDependent,
|
|
) -> Result<ComputedValue, ()> {
|
|
debug_assert!(!registration.syntax.is_universal(), "Shouldn't be needed");
|
|
let Ok(value) = Self::parse(
|
|
input,
|
|
®istration.syntax,
|
|
url_data,
|
|
allow_computationally_dependent,
|
|
) else {
|
|
return Err(());
|
|
};
|
|
|
|
Ok(value.to_computed_value(context))
|
|
}
|
|
|
|
/// Parse and validate a registered custom property value according to its syntax descriptor,
|
|
/// and check for computational independence.
|
|
pub fn parse<'i, 't>(
|
|
mut input: &mut CSSParser<'i, 't>,
|
|
syntax: &Descriptor,
|
|
url_data: &UrlExtraData,
|
|
allow_computationally_dependent: AllowComputationallyDependent,
|
|
) -> Result<Self, StyleParseError<'i>> {
|
|
if syntax.is_universal() {
|
|
let parsed = ComputedPropertyValue::parse(&mut input, url_data)?;
|
|
return Ok(SpecifiedValue {
|
|
v: ValueInner::Universal(Arc::new(parsed)),
|
|
url_data: url_data.clone(),
|
|
});
|
|
}
|
|
|
|
let mut values = SmallComponentVec::new();
|
|
let mut multiplier = None;
|
|
{
|
|
let mut parser = Parser::new(syntax, &mut values, &mut multiplier);
|
|
parser.parse(&mut input, url_data, allow_computationally_dependent)?;
|
|
}
|
|
let v = if let Some(multiplier) = multiplier {
|
|
ValueInner::List(ComponentList {
|
|
multiplier,
|
|
components: values.to_vec().into(),
|
|
})
|
|
} else {
|
|
ValueInner::Component(values[0].clone())
|
|
};
|
|
Ok(Self {
|
|
v,
|
|
url_data: url_data.clone(),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ComputedValue {
|
|
fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) {
|
|
match &self.v {
|
|
ValueInner::Component(component) => component.serialization_types(),
|
|
ValueInner::Universal(_) => unreachable!(),
|
|
ValueInner::List(list) => list
|
|
.components
|
|
.first()
|
|
.map_or(Default::default(), |f| f.serialization_types()),
|
|
}
|
|
}
|
|
|
|
fn to_declared_value(&self) -> properties::CustomDeclarationValue {
|
|
if let ValueInner::Universal(ref var) = self.v {
|
|
return properties::CustomDeclarationValue::Unparsed(Arc::clone(var));
|
|
}
|
|
properties::CustomDeclarationValue::Parsed(Arc::new(ToComputedValue::from_computed_value(self)))
|
|
}
|
|
|
|
/// Returns the contained variable value if it exists, otherwise `None`.
|
|
pub fn as_universal(&self) -> Option<&Arc<ComputedPropertyValue>> {
|
|
if let ValueInner::Universal(ref var) = self.v {
|
|
Some(var)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Convert to an untyped variable value.
|
|
pub fn to_variable_value(&self) -> ComputedPropertyValue {
|
|
if let ValueInner::Universal(ref value) = self.v {
|
|
return (**value).clone();
|
|
}
|
|
let serialization_types = self.serialization_types();
|
|
ComputedPropertyValue::new(
|
|
self.to_css_string(),
|
|
&self.url_data,
|
|
serialization_types.0,
|
|
serialization_types.1,
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Whether the computed value parsing should allow computationaly dependent values like 3em or
|
|
/// var(-foo).
|
|
///
|
|
/// https://drafts.css-houdini.org/css-properties-values-api-1/#computationally-independent
|
|
pub enum AllowComputationallyDependent {
|
|
/// Only computationally independent values are allowed.
|
|
No,
|
|
/// Computationally independent and dependent values are allowed.
|
|
Yes,
|
|
}
|
|
|
|
type SmallComponentVec = SmallVec<[SpecifiedValueComponent; 1]>;
|
|
|
|
struct Parser<'a> {
|
|
syntax: &'a Descriptor,
|
|
output: &'a mut SmallComponentVec,
|
|
output_multiplier: &'a mut Option<Multiplier>,
|
|
}
|
|
|
|
impl<'a> Parser<'a> {
|
|
fn new(
|
|
syntax: &'a Descriptor,
|
|
output: &'a mut SmallComponentVec,
|
|
output_multiplier: &'a mut Option<Multiplier>,
|
|
) -> Self {
|
|
Self {
|
|
syntax,
|
|
output,
|
|
output_multiplier,
|
|
}
|
|
}
|
|
|
|
fn parse<'i, 't>(
|
|
&mut self,
|
|
input: &mut CSSParser<'i, 't>,
|
|
url_data: &UrlExtraData,
|
|
allow_computationally_dependent: AllowComputationallyDependent,
|
|
) -> Result<(), StyleParseError<'i>> {
|
|
use self::AllowComputationallyDependent::*;
|
|
let parsing_mode = match allow_computationally_dependent {
|
|
No => ParsingMode::DISALLOW_COMPUTATIONALLY_DEPENDENT,
|
|
Yes => ParsingMode::DEFAULT,
|
|
};
|
|
let ref context = ParserContext::new(
|
|
Origin::Author,
|
|
url_data,
|
|
Some(CssRuleType::Style),
|
|
parsing_mode,
|
|
QuirksMode::NoQuirks,
|
|
/* namespaces = */ Default::default(),
|
|
None,
|
|
None,
|
|
);
|
|
for component in self.syntax.components.iter() {
|
|
let result = input.try_parse(|input| {
|
|
input.parse_entirely(|input| {
|
|
Self::parse_value(context, input, &component.unpremultiplied())
|
|
})
|
|
});
|
|
let Ok(values) = result else { continue };
|
|
self.output.extend(values);
|
|
*self.output_multiplier = component.multiplier();
|
|
break;
|
|
}
|
|
if self.output.is_empty() {
|
|
return Err(input.new_error(BasicParseErrorKind::EndOfInput));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn parse_value<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut CSSParser<'i, 't>,
|
|
component: &SyntaxComponent,
|
|
) -> Result<SmallComponentVec, StyleParseError<'i>> {
|
|
let mut values = SmallComponentVec::new();
|
|
values.push(Self::parse_component_without_multiplier(
|
|
context, input, component,
|
|
)?);
|
|
|
|
if let Some(multiplier) = component.multiplier() {
|
|
loop {
|
|
let result = Self::expect_multiplier(input, &multiplier);
|
|
if Self::expect_multiplier_yielded_eof_error(&result) {
|
|
break;
|
|
}
|
|
result?;
|
|
values.push(Self::parse_component_without_multiplier(
|
|
context, input, component,
|
|
)?);
|
|
}
|
|
}
|
|
Ok(values)
|
|
}
|
|
|
|
fn parse_component_without_multiplier<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut CSSParser<'i, 't>,
|
|
component: &SyntaxComponent,
|
|
) -> Result<SpecifiedValueComponent, StyleParseError<'i>> {
|
|
let data_type = match component.name() {
|
|
ComponentName::DataType(ty) => ty,
|
|
ComponentName::Ident(ref name) => {
|
|
let ident = CustomIdent::parse(input, &[])?;
|
|
if ident != *name {
|
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
|
}
|
|
return Ok(SpecifiedValueComponent::CustomIdent(ident));
|
|
},
|
|
};
|
|
|
|
let value = match data_type {
|
|
DataType::Length => {
|
|
SpecifiedValueComponent::Length(specified::Length::parse(context, input)?)
|
|
},
|
|
DataType::Number => {
|
|
SpecifiedValueComponent::Number(specified::Number::parse(context, input)?)
|
|
},
|
|
DataType::Percentage => {
|
|
SpecifiedValueComponent::Percentage(specified::Percentage::parse(context, input)?)
|
|
},
|
|
DataType::LengthPercentage => SpecifiedValueComponent::LengthPercentage(
|
|
specified::LengthPercentage::parse(context, input)?,
|
|
),
|
|
DataType::Color => {
|
|
SpecifiedValueComponent::Color(specified::Color::parse(context, input)?)
|
|
},
|
|
DataType::Image => {
|
|
SpecifiedValueComponent::Image(specified::Image::parse(context, input)?)
|
|
},
|
|
DataType::Url => {
|
|
SpecifiedValueComponent::Url(specified::url::SpecifiedUrl::parse(context, input)?)
|
|
},
|
|
DataType::Integer => {
|
|
SpecifiedValueComponent::Integer(specified::Integer::parse(context, input)?)
|
|
},
|
|
DataType::Angle => {
|
|
SpecifiedValueComponent::Angle(specified::Angle::parse(context, input)?)
|
|
},
|
|
DataType::Time => {
|
|
SpecifiedValueComponent::Time(specified::Time::parse(context, input)?)
|
|
},
|
|
DataType::Resolution => {
|
|
SpecifiedValueComponent::Resolution(specified::Resolution::parse(context, input)?)
|
|
},
|
|
DataType::TransformFunction => SpecifiedValueComponent::TransformFunction(
|
|
specified::Transform::parse(context, input)?,
|
|
),
|
|
DataType::CustomIdent => {
|
|
let name = CustomIdent::parse(input, &[])?;
|
|
SpecifiedValueComponent::CustomIdent(name)
|
|
},
|
|
DataType::TransformList => {
|
|
let mut values = vec![];
|
|
let Some(multiplier) = component.unpremultiplied().multiplier() else {
|
|
debug_assert!(false, "Unpremultiplied <transform-list> had no multiplier?");
|
|
return Err(
|
|
input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(
|
|
PropertySyntaxParseError::UnexpectedEOF,
|
|
)),
|
|
);
|
|
};
|
|
debug_assert_matches!(multiplier, Multiplier::Space);
|
|
loop {
|
|
values.push(SpecifiedValueComponent::TransformFunction(
|
|
specified::Transform::parse(context, input)?,
|
|
));
|
|
let result = Self::expect_multiplier(input, &multiplier);
|
|
if Self::expect_multiplier_yielded_eof_error(&result) {
|
|
break;
|
|
}
|
|
result?;
|
|
}
|
|
let list = ComponentList {
|
|
multiplier,
|
|
components: values.into(),
|
|
};
|
|
SpecifiedValueComponent::TransformList(list)
|
|
},
|
|
DataType::String => {
|
|
let string = input.expect_string()?;
|
|
SpecifiedValueComponent::String(string.as_ref().to_owned().into())
|
|
},
|
|
};
|
|
Ok(value)
|
|
}
|
|
|
|
fn expect_multiplier_yielded_eof_error<'i>(result: &Result<(), StyleParseError<'i>>) -> bool {
|
|
matches!(
|
|
result,
|
|
Err(StyleParseError {
|
|
kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
|
|
..
|
|
})
|
|
)
|
|
}
|
|
|
|
fn expect_multiplier<'i, 't>(
|
|
input: &mut CSSParser<'i, 't>,
|
|
multiplier: &Multiplier,
|
|
) -> Result<(), StyleParseError<'i>> {
|
|
match multiplier {
|
|
Multiplier::Space => {
|
|
input.expect_whitespace()?;
|
|
if input.is_exhausted() {
|
|
// If there was trailing whitespace, do not interpret it as a multiplier
|
|
return Err(input.new_error(BasicParseErrorKind::EndOfInput));
|
|
}
|
|
Ok(())
|
|
},
|
|
Multiplier::Comma => Ok(input.expect_comma()?),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An animated value for custom property.
|
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
|
|
pub struct CustomAnimatedValue {
|
|
/// The name of the custom property.
|
|
pub(crate) name: crate::custom_properties::Name,
|
|
/// The computed value of the custom property.
|
|
value: ComputedValue,
|
|
}
|
|
|
|
impl Animate for CustomAnimatedValue {
|
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
if self.name != other.name {
|
|
return Err(());
|
|
}
|
|
let value = self.value.animate(&other.value, procedure)?;
|
|
Ok(Self {
|
|
name: self.name.clone(),
|
|
value,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl CustomAnimatedValue {
|
|
pub(crate) fn from_computed(
|
|
name: &crate::custom_properties::Name,
|
|
value: &ComputedValue,
|
|
) -> Self {
|
|
Self {
|
|
name: name.clone(),
|
|
value: value.clone(),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn from_declaration(
|
|
declaration: &properties::CustomDeclaration,
|
|
context: &mut computed::Context,
|
|
_initial: &properties::ComputedValues,
|
|
) -> Option<Self> {
|
|
let computed_value = match declaration.value {
|
|
properties::CustomDeclarationValue::Unparsed(ref value) => {
|
|
debug_assert!(
|
|
context.builder.stylist.is_some(),
|
|
"Need a Stylist to get property registration!"
|
|
);
|
|
let registration = context
|
|
.builder
|
|
.stylist
|
|
.unwrap()
|
|
.get_custom_property_registration(&declaration.name);
|
|
if registration.syntax.is_universal() {
|
|
// FIXME: Do we need to perform substitution here somehow?
|
|
ComputedValue {
|
|
v: ValueInner::Universal(Arc::clone(value)),
|
|
url_data: value.url_data.clone(),
|
|
}
|
|
} else {
|
|
let mut input = cssparser::ParserInput::new(&value.css);
|
|
let mut input = CSSParser::new(&mut input);
|
|
SpecifiedValue::compute(
|
|
&mut input,
|
|
registration,
|
|
&value.url_data,
|
|
context,
|
|
AllowComputationallyDependent::Yes,
|
|
).unwrap_or_else(|_| {
|
|
ComputedValue {
|
|
v: ValueInner::Universal(Arc::clone(value)),
|
|
url_data: value.url_data.clone(),
|
|
}
|
|
})
|
|
}
|
|
}
|
|
properties::CustomDeclarationValue::Parsed(ref v) => v.to_computed_value(context),
|
|
// FIXME: This should be made to work to the extent possible like for non-custom
|
|
// properties (using `initial` at least to handle unset / inherit).
|
|
properties::CustomDeclarationValue::CSSWideKeyword(..) => return None,
|
|
};
|
|
Some(Self {
|
|
name: declaration.name.clone(),
|
|
value: computed_value,
|
|
})
|
|
}
|
|
|
|
pub(crate) fn to_declaration(&self) -> properties::PropertyDeclaration {
|
|
properties::PropertyDeclaration::Custom(properties::CustomDeclaration {
|
|
name: self.name.clone(),
|
|
value: self.value.to_declared_value(),
|
|
})
|
|
}
|
|
}
|