1use super::{
8 rule::Descriptors as PropertyDescriptors,
9 syntax::{
10 data_type::DataType, Component as SyntaxComponent, ComponentName, Descriptor, Multiplier,
11 },
12};
13use crate::custom_properties::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
36pub 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
52pub 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#[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 Length(Length),
116 Number(Number),
118 Percentage(Percentage),
120 LengthPercentage(LengthPercentage),
122 Color(Color),
124 #[animation(error)]
126 Image(Image),
127 #[animation(error)]
129 Url(Url),
130 Integer(Integer),
132 Angle(Angle),
134 Time(Time),
136 Resolution(Resolution),
138 TransformFunction(TransformFunction),
141 #[animation(error)]
143 CustomIdent(CustomIdent),
144 TransformList(ComponentList<Self>),
147 #[animation(error)]
149 String(OwnedStr),
150}
151
152#[derive(Clone, ToComputedValue, ToResolvedValue, Debug, MallocSizeOf, PartialEq, ToShmem)]
154pub struct ComponentList<Component> {
155 pub multiplier: Multiplier,
157 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 let separator = match self.multiplier {
191 Multiplier::Space => " ",
193 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#[derive(Clone, Debug, MallocSizeOf, ToCss, ToComputedValue, ToResolvedValue, ToShmem)]
207pub struct Value<Component> {
208 pub(crate) v: ValueInner<Component>,
210 #[css(skip)]
213 url_data: UrlExtraData,
214 #[css(skip)]
216 pub attribute_tainted: bool,
217}
218
219impl<Component: PartialEq> PartialEq for Value<Component> {
220 fn eq(&self, other: &Self) -> bool {
222 self.v == other.v
223 }
224}
225
226impl<Component: Animate> Animate for Value<Component> {
227 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
228 let v = self.v.animate(&other.v, procedure)?;
229 Ok(Value {
230 v,
231 url_data: self.url_data.clone(),
232 attribute_tainted: self.attribute_tainted,
233 })
234 }
235}
236
237impl<Component> Value<Component> {
238 pub fn new(v: ValueInner<Component>, url_data: UrlExtraData) -> Self {
240 Self {
241 v,
242 url_data,
243 attribute_tainted: Default::default(),
244 }
245 }
246
247 pub fn universal(var: Arc<ComputedPropertyValue>) -> Self {
249 let attribute_tainted = var.is_tainted_by_attr();
250 let url_data = var.url_data.clone();
251 let v = ValueInner::Universal(var);
252 Self {
253 v,
254 url_data,
255 attribute_tainted,
256 }
257 }
258}
259
260impl<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>
261 Value<GenericValueComponent<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>>
262where
263 Self: ToCss,
264{
265 fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) {
266 match &self.v {
267 ValueInner::Component(component) => component.serialization_types(),
268 ValueInner::Universal(_) => unreachable!(),
269 ValueInner::List(list) => list
270 .components
271 .first()
272 .map_or(Default::default(), |f| f.serialization_types()),
273 }
274 }
275
276 pub fn to_variable_value(&self) -> ComputedPropertyValue {
278 if let ValueInner::Universal(ref value) = self.v {
279 return (**value).clone();
280 }
281 let serialization_types = self.serialization_types();
282 ComputedPropertyValue::new(
283 self.to_css_string(),
284 &self.url_data,
285 serialization_types.0,
286 serialization_types.1,
287 self.attribute_tainted,
288 )
289 }
290}
291
292#[derive(
294 Animate, ToComputedValue, ToResolvedValue, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToShmem,
295)]
296pub enum ValueInner<Component> {
297 Component(Component),
300 #[animation(error)]
302 Universal(#[ignore_malloc_size_of = "Arc"] Arc<ComputedPropertyValue>),
303 List(#[animation(field_bound)] ComponentList<Component>),
305}
306
307pub type SpecifiedValue = Value<SpecifiedValueComponent>;
309
310pub type ComputedValue = Value<ComputedValueComponent>;
312
313impl SpecifiedValue {
314 pub fn compute<'i, 't>(
317 input: &mut CSSParser<'i, 't>,
318 registration: &PropertyDescriptors,
319 namespaces: Option<&FxHashMap<Prefix, Namespace>>,
320 url_data: &UrlExtraData,
321 context: &computed::Context,
322 allow_computationally_dependent: AllowComputationallyDependent,
323 ) -> Result<ComputedValue, ()> {
324 debug_assert!(!registration.is_universal(), "Shouldn't be needed");
325 let Some(ref syntax) = registration.syntax else {
326 return Err(());
327 };
328 let Ok(value) = Self::parse(
329 input,
330 syntax,
331 url_data,
332 namespaces,
333 allow_computationally_dependent,
334 ) else {
335 return Err(());
336 };
337
338 Ok(value.to_computed_value(context))
339 }
340
341 pub fn parse<'i, 't>(
344 mut input: &mut CSSParser<'i, 't>,
345 syntax: &Descriptor,
346 url_data: &UrlExtraData,
347 namespaces: Option<&FxHashMap<Prefix, Namespace>>,
348 allow_computationally_dependent: AllowComputationallyDependent,
349 ) -> Result<Self, StyleParseError<'i>> {
350 if syntax.is_universal() {
351 let parsed = ComputedPropertyValue::parse(&mut input, namespaces, url_data)?;
352 return Ok(Self::new(
353 ValueInner::Universal(Arc::new(parsed)),
354 url_data.clone(),
355 ));
356 }
357
358 let mut values = SmallComponentVec::new();
359 let mut multiplier = None;
360 {
361 let mut parser = Parser::new(syntax, &mut values, &mut multiplier);
362 parser.parse(&mut input, url_data, allow_computationally_dependent)?;
363 }
364 let v = if let Some(multiplier) = multiplier {
365 ValueInner::List(ComponentList {
366 multiplier,
367 components: values.to_vec().into(),
368 })
369 } else {
370 ValueInner::Component(values[0].clone())
371 };
372 Ok(Self::new(v, url_data.clone()))
373 }
374}
375
376impl ComputedValue {
377 fn to_declared_value(&self) -> properties::CustomDeclarationValue {
378 if let ValueInner::Universal(ref var) = self.v {
379 return properties::CustomDeclarationValue::Unparsed(Arc::clone(var));
380 }
381 properties::CustomDeclarationValue::Parsed(Arc::new(ToComputedValue::from_computed_value(
382 self,
383 )))
384 }
385
386 pub fn as_universal(&self) -> Option<&Arc<ComputedPropertyValue>> {
388 if let ValueInner::Universal(ref var) = self.v {
389 Some(var)
390 } else {
391 None
392 }
393 }
394}
395
396pub enum AllowComputationallyDependent {
401 No,
403 Yes,
405}
406
407type SmallComponentVec = SmallVec<[SpecifiedValueComponent; 1]>;
408
409struct Parser<'a> {
410 syntax: &'a Descriptor,
411 output: &'a mut SmallComponentVec,
412 output_multiplier: &'a mut Option<Multiplier>,
413}
414
415impl<'a> Parser<'a> {
416 fn new(
417 syntax: &'a Descriptor,
418 output: &'a mut SmallComponentVec,
419 output_multiplier: &'a mut Option<Multiplier>,
420 ) -> Self {
421 Self {
422 syntax,
423 output,
424 output_multiplier,
425 }
426 }
427
428 fn parse<'i, 't>(
429 &mut self,
430 input: &mut CSSParser<'i, 't>,
431 url_data: &UrlExtraData,
432 allow_computationally_dependent: AllowComputationallyDependent,
433 ) -> Result<(), StyleParseError<'i>> {
434 use self::AllowComputationallyDependent::*;
435 let parsing_mode = match allow_computationally_dependent {
436 No => ParsingMode::DISALLOW_COMPUTATIONALLY_DEPENDENT,
437 Yes => ParsingMode::DEFAULT,
438 };
439 let ref context = ParserContext::new(
440 Origin::Author,
441 url_data,
442 Some(CssRuleType::Style),
443 parsing_mode,
444 QuirksMode::NoQuirks,
445 Default::default(),
446 None,
447 None,
448 );
449 for component in self.syntax.components.iter() {
450 let result = input.try_parse(|input| {
451 input.parse_entirely(|input| {
452 Self::parse_value(context, input, &component.unpremultiplied())
453 })
454 });
455 let Ok(values) = result else { continue };
456 self.output.extend(values);
457 *self.output_multiplier = component.multiplier();
458 break;
459 }
460 if self.output.is_empty() {
461 return Err(input.new_error(BasicParseErrorKind::EndOfInput));
462 }
463 Ok(())
464 }
465
466 fn parse_value<'i, 't>(
467 context: &ParserContext,
468 input: &mut CSSParser<'i, 't>,
469 component: &SyntaxComponent,
470 ) -> Result<SmallComponentVec, StyleParseError<'i>> {
471 let mut values = SmallComponentVec::new();
472 values.push(Self::parse_component_without_multiplier(
473 context, input, component,
474 )?);
475
476 if let Some(multiplier) = component.multiplier() {
477 loop {
478 let result = Self::expect_multiplier(input, &multiplier);
479 if Self::expect_multiplier_yielded_eof_error(&result) {
480 break;
481 }
482 result?;
483 values.push(Self::parse_component_without_multiplier(
484 context, input, component,
485 )?);
486 }
487 }
488 Ok(values)
489 }
490
491 fn parse_component_without_multiplier<'i, 't>(
492 context: &ParserContext,
493 input: &mut CSSParser<'i, 't>,
494 component: &SyntaxComponent,
495 ) -> Result<SpecifiedValueComponent, StyleParseError<'i>> {
496 let data_type = match component.name() {
497 ComponentName::DataType(ty) => ty,
498 ComponentName::Ident(ref name) => {
499 let ident = CustomIdent::parse(input, &[])?;
500 if ident != *name {
501 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
502 }
503 return Ok(SpecifiedValueComponent::CustomIdent(ident));
504 },
505 };
506
507 let value = match data_type {
508 DataType::Length => {
509 SpecifiedValueComponent::Length(specified::Length::parse(context, input)?)
510 },
511 DataType::Number => {
512 SpecifiedValueComponent::Number(specified::Number::parse(context, input)?)
513 },
514 DataType::Percentage => {
515 SpecifiedValueComponent::Percentage(specified::Percentage::parse(context, input)?)
516 },
517 DataType::LengthPercentage => SpecifiedValueComponent::LengthPercentage(
518 specified::LengthPercentage::parse(context, input)?,
519 ),
520 DataType::Color => {
521 SpecifiedValueComponent::Color(specified::Color::parse(context, input)?)
522 },
523 DataType::Image => {
524 SpecifiedValueComponent::Image(specified::Image::parse(context, input)?)
525 },
526 DataType::Url => {
527 SpecifiedValueComponent::Url(specified::url::SpecifiedUrl::parse(context, input)?)
528 },
529 DataType::Integer => {
530 SpecifiedValueComponent::Integer(specified::Integer::parse(context, input)?)
531 },
532 DataType::Angle => {
533 SpecifiedValueComponent::Angle(specified::Angle::parse(context, input)?)
534 },
535 DataType::Time => {
536 SpecifiedValueComponent::Time(specified::Time::parse(context, input)?)
537 },
538 DataType::Resolution => {
539 SpecifiedValueComponent::Resolution(specified::Resolution::parse(context, input)?)
540 },
541 DataType::TransformFunction => SpecifiedValueComponent::TransformFunction(
542 specified::Transform::parse(context, input)?,
543 ),
544 DataType::CustomIdent => {
545 let name = CustomIdent::parse(input, &[])?;
546 SpecifiedValueComponent::CustomIdent(name)
547 },
548 DataType::TransformList => {
549 let mut values = vec![];
550 let Some(multiplier) = component.unpremultiplied().multiplier() else {
551 debug_assert!(false, "Unpremultiplied <transform-list> had no multiplier?");
552 return Err(
553 input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(
554 PropertySyntaxParseError::UnexpectedEOF,
555 )),
556 );
557 };
558 debug_assert_eq!(multiplier, Multiplier::Space);
559 loop {
560 values.push(SpecifiedValueComponent::TransformFunction(
561 specified::Transform::parse(context, input)?,
562 ));
563 let result = Self::expect_multiplier(input, &multiplier);
564 if Self::expect_multiplier_yielded_eof_error(&result) {
565 break;
566 }
567 result?;
568 }
569 let list = ComponentList {
570 multiplier,
571 components: values.into(),
572 };
573 SpecifiedValueComponent::TransformList(list)
574 },
575 DataType::String => {
576 let string = input.expect_string()?;
577 SpecifiedValueComponent::String(string.as_ref().to_owned().into())
578 },
579 };
580 Ok(value)
581 }
582
583 fn expect_multiplier_yielded_eof_error<'i>(result: &Result<(), StyleParseError<'i>>) -> bool {
584 matches!(
585 result,
586 Err(StyleParseError {
587 kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
588 ..
589 })
590 )
591 }
592
593 fn expect_multiplier<'i, 't>(
594 input: &mut CSSParser<'i, 't>,
595 multiplier: &Multiplier,
596 ) -> Result<(), StyleParseError<'i>> {
597 match multiplier {
598 Multiplier::Space => {
599 input.expect_whitespace()?;
600 if input.is_exhausted() {
601 return Err(input.new_error(BasicParseErrorKind::EndOfInput));
603 }
604 Ok(())
605 },
606 Multiplier::Comma => Ok(input.expect_comma()?),
607 }
608 }
609}
610
611#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
613pub struct CustomAnimatedValue {
614 pub(crate) name: crate::custom_properties::Name,
616 pub(crate) value: Option<ComputedValue>,
619}
620
621impl Animate for CustomAnimatedValue {
622 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
623 if self.name != other.name {
624 return Err(());
625 }
626 let value = self.value.animate(&other.value, procedure)?;
627 Ok(Self {
628 name: self.name.clone(),
629 value,
630 })
631 }
632}
633
634impl CustomAnimatedValue {
635 pub(crate) fn from_computed(
636 name: &crate::custom_properties::Name,
637 value: Option<&ComputedValue>,
638 ) -> Self {
639 Self {
640 name: name.clone(),
641 value: value.cloned(),
642 }
643 }
644
645 pub(crate) fn from_declaration(
646 declaration: &properties::CustomDeclaration,
647 context: &mut computed::Context,
648 ) -> Option<Self> {
649 let computed_value = match declaration.value {
650 properties::CustomDeclarationValue::Unparsed(ref value) => Some({
651 debug_assert!(
652 context.builder.stylist.is_some(),
653 "Need a Stylist to get property registration!"
654 );
655 let registration = context
656 .builder
657 .stylist
658 .unwrap()
659 .get_custom_property_registration(&declaration.name);
660 if registration.is_universal() {
661 ComputedValue::new(
663 ValueInner::Universal(Arc::clone(value)),
664 value.url_data.clone(),
665 )
666 } else {
667 let mut input = cssparser::ParserInput::new(&value.css);
668 let mut input = CSSParser::new(&mut input);
669 SpecifiedValue::compute(
670 &mut input,
671 registration,
672 None,
673 &value.url_data,
674 context,
675 AllowComputationallyDependent::Yes,
676 )
677 .unwrap_or_else(|_| {
678 ComputedValue::new(
679 ValueInner::Universal(Arc::clone(value)),
680 value.url_data.clone(),
681 )
682 })
683 }
684 }),
685 properties::CustomDeclarationValue::Parsed(ref v) => Some(v.to_computed_value(context)),
686 properties::CustomDeclarationValue::CSSWideKeyword(keyword) => {
687 let stylist = context.builder.stylist.unwrap();
688 let registration = stylist.get_custom_property_registration(&declaration.name);
689 match keyword {
690 CSSWideKeyword::Initial => stylist
691 .get_custom_property_initial_values()
692 .get(registration, &declaration.name),
693 CSSWideKeyword::Inherit => context
694 .builder
695 .inherited_custom_properties()
696 .get(registration, &declaration.name),
697 CSSWideKeyword::Unset => {
698 if registration.inherits() {
699 context
700 .builder
701 .inherited_custom_properties()
702 .get(registration, &declaration.name)
703 } else {
704 stylist
705 .get_custom_property_initial_values()
706 .get(registration, &declaration.name)
707 }
708 },
709 CSSWideKeyword::Revert
719 | CSSWideKeyword::RevertRule
720 | CSSWideKeyword::RevertLayer => return None,
721 }
722 .cloned()
723 },
724 };
725 Some(Self {
726 name: declaration.name.clone(),
727 value: computed_value,
728 })
729 }
730
731 pub(crate) fn to_declaration(&self) -> properties::PropertyDeclaration {
732 properties::PropertyDeclaration::Custom(properties::CustomDeclaration {
733 name: self.name.clone(),
734 value: match &self.value {
735 Some(value) => value.to_declared_value(),
736 None => CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial),
737 },
738 })
739 }
740}