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::{AttrTaint, ComputedValue as ComputedPropertyValue};
14use crate::derives::*;
15use crate::parser::{Parse, ParserContext};
16use crate::properties;
17use crate::properties::{CSSWideKeyword, CustomDeclarationValue};
18use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
19use crate::values::{
20 animated::{self, Animate, Procedure},
21 computed::{self, ToComputedValue},
22 specified, CustomIdent,
23};
24use crate::{Namespace, Prefix};
25use cssparser::{BasicParseErrorKind, ParseErrorKind, Parser as CSSParser, TokenSerializationType};
26use rustc_hash::FxHashMap;
27use selectors::matching::QuirksMode;
28use servo_arc::Arc;
29use smallvec::SmallVec;
30use std::fmt::{self, Write};
31use style_traits::{
32 owned_str::OwnedStr, CssWriter, ParseError as StyleParseError, ParsingMode,
33 PropertySyntaxParseError, StyleParseErrorKind, ToCss,
34};
35
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, ToTyped)]
207#[typed(todo_derive_fields)]
208pub struct Value<Component> {
209 pub(crate) v: ValueInner<Component>,
211 #[css(skip)]
214 url_data: UrlExtraData,
215 #[css(skip)]
217 pub attr_tainted: bool,
218}
219
220impl<Component: PartialEq> PartialEq for Value<Component> {
221 fn eq(&self, other: &Self) -> bool {
225 self.v == other.v && self.attr_tainted == other.attr_tainted
226 }
227}
228
229impl<Component: Animate> Animate for Value<Component> {
230 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
231 let v = self.v.animate(&other.v, procedure)?;
232 Ok(Value {
233 v,
234 url_data: self.url_data.clone(),
235 attr_tainted: self.attr_tainted,
236 })
237 }
238}
239
240impl<Component> Value<Component> {
241 pub fn new(v: ValueInner<Component>, url_data: UrlExtraData) -> Self {
243 Self {
244 v,
245 url_data,
246 attr_tainted: Default::default(),
247 }
248 }
249
250 pub fn universal(var: Arc<ComputedPropertyValue>) -> Self {
252 let attr_tainted = var.is_attr_tainted();
253 let url_data = var.url_data.clone();
254 let v = ValueInner::Universal(var);
255 Self {
256 v,
257 url_data,
258 attr_tainted,
259 }
260 }
261}
262
263impl<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>
264 Value<GenericValueComponent<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>>
265where
266 Self: ToCss,
267{
268 fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) {
269 match &self.v {
270 ValueInner::Component(component) => component.serialization_types(),
271 ValueInner::Universal(_) => unreachable!(),
272 ValueInner::List(list) => list
273 .components
274 .first()
275 .map_or(Default::default(), |f| f.serialization_types()),
276 }
277 }
278
279 pub fn to_variable_value(&self) -> ComputedPropertyValue {
281 if let ValueInner::Universal(ref value) = self.v {
282 return (**value).clone();
283 }
284 let serialization_types = self.serialization_types();
285 ComputedPropertyValue::new(
286 self.to_css_string(),
287 &self.url_data,
288 serialization_types.0,
289 serialization_types.1,
290 )
291 }
292}
293
294#[derive(
296 Animate, ToComputedValue, ToResolvedValue, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToShmem,
297)]
298pub enum ValueInner<Component> {
299 Component(Component),
302 #[animation(error)]
304 Universal(#[ignore_malloc_size_of = "Arc"] Arc<ComputedPropertyValue>),
305 List(#[animation(field_bound)] ComponentList<Component>),
307}
308
309pub type SpecifiedValue = Value<SpecifiedValueComponent>;
311
312pub type ComputedValue = Value<ComputedValueComponent>;
314
315impl SpecifiedValue {
316 pub fn compute(
319 input: &mut CSSParser,
320 registration: &PropertyDescriptors,
321 namespaces: Option<&FxHashMap<Prefix, Namespace>>,
322 url_data: &UrlExtraData,
323 context: &computed::Context,
324 allow_computationally_dependent: AllowComputationallyDependent,
325 attr_taint: AttrTaint,
326 ) -> Result<ComputedValue, ()> {
327 debug_assert!(!registration.is_universal(), "Shouldn't be needed");
328 let Some(ref syntax) = registration.syntax else {
329 return Err(());
330 };
331 let Ok(value) = Self::parse(
332 input,
333 syntax,
334 url_data,
335 namespaces,
336 allow_computationally_dependent,
337 attr_taint,
338 ) else {
339 return Err(());
340 };
341
342 Ok(value.to_computed_value(context))
343 }
344
345 pub fn parse(
348 input: &mut CSSParser,
349 syntax: &Descriptor,
350 url_data: &UrlExtraData,
351 namespaces: Option<&FxHashMap<Prefix, Namespace>>,
352 allow_computationally_dependent: AllowComputationallyDependent,
353 attr_taint: AttrTaint,
354 ) -> Result<Self, StyleParseError> {
355 if syntax.is_universal() {
356 let parsed = ComputedPropertyValue::parse(input, namespaces, url_data)?;
357 return Ok(Self::new(
358 ValueInner::Universal(Arc::new(parsed)),
359 url_data.clone(),
360 ));
361 }
362
363 let mut values = SmallComponentVec::new();
364 let mut multiplier = None;
365 {
366 let mut parser = Parser::new(syntax, &mut values, &mut multiplier);
367 parser.parse(input, url_data, allow_computationally_dependent, attr_taint)?;
368 }
369 let v = if let Some(multiplier) = multiplier {
370 ValueInner::List(ComponentList {
371 multiplier,
372 components: values.to_vec().into(),
373 })
374 } else {
375 ValueInner::Component(values[0].clone())
376 };
377 Ok(Self::new(v, url_data.clone()))
378 }
379}
380
381impl ComputedValue {
382 pub fn to_declared_value(&self) -> properties::CustomDeclarationValue {
384 if let ValueInner::Universal(ref var) = self.v {
385 if self.attr_tainted && !var.is_attr_tainted() {
393 let mut tainted = (**var).clone();
394 tainted.explicitly_attr_tainted = true;
395 return properties::CustomDeclarationValue::Unparsed(Arc::new(tainted));
396 }
397 return properties::CustomDeclarationValue::Unparsed(Arc::clone(var));
398 }
399 properties::CustomDeclarationValue::Parsed(Arc::new(ToComputedValue::from_computed_value(
400 self,
401 )))
402 }
403
404 pub fn as_universal(&self) -> Option<&Arc<ComputedPropertyValue>> {
406 if let ValueInner::Universal(ref var) = self.v {
407 Some(var)
408 } else {
409 None
410 }
411 }
412}
413
414pub enum AllowComputationallyDependent {
419 No,
421 Yes,
423}
424
425type SmallComponentVec = SmallVec<[SpecifiedValueComponent; 1]>;
426
427struct Parser<'a> {
428 syntax: &'a Descriptor,
429 output: &'a mut SmallComponentVec,
430 output_multiplier: &'a mut Option<Multiplier>,
431}
432
433impl<'a> Parser<'a> {
434 fn new(
435 syntax: &'a Descriptor,
436 output: &'a mut SmallComponentVec,
437 output_multiplier: &'a mut Option<Multiplier>,
438 ) -> Self {
439 Self {
440 syntax,
441 output,
442 output_multiplier,
443 }
444 }
445
446 fn parse(
447 &mut self,
448 input: &mut CSSParser,
449 url_data: &UrlExtraData,
450 allow_computationally_dependent: AllowComputationallyDependent,
451 attr_taint: AttrTaint,
452 ) -> Result<(), StyleParseError> {
453 use self::AllowComputationallyDependent::*;
454 let parsing_mode = match allow_computationally_dependent {
455 No => ParsingMode::DISALLOW_COMPUTATIONALLY_DEPENDENT,
456 Yes => ParsingMode::DEFAULT,
457 };
458 let context = &ParserContext::new(
459 Origin::Author,
460 url_data,
461 Some(CssRuleType::Style),
462 parsing_mode,
463 QuirksMode::NoQuirks,
464 Default::default(),
465 None,
466 None,
467 attr_taint,
468 );
469 for component in self.syntax.components.iter() {
470 let result = input.try_parse(|input| {
471 input.parse_entirely(|input| {
472 Self::parse_value(context, input, &component.unpremultiplied())
473 })
474 });
475 let Ok(values) = result else { continue };
476 self.output.extend(values);
477 *self.output_multiplier = component.multiplier();
478 break;
479 }
480 if self.output.is_empty() {
481 return Err(StyleParseError::from_basic_kind(
482 BasicParseErrorKind::EndOfInput,
483 ));
484 }
485 Ok(())
486 }
487
488 fn parse_value(
489 context: &ParserContext,
490 input: &mut CSSParser,
491 component: &SyntaxComponent,
492 ) -> Result<SmallComponentVec, StyleParseError> {
493 let mut values = SmallComponentVec::new();
494 values.push(Self::parse_component_without_multiplier(
495 context, input, component,
496 )?);
497
498 if let Some(multiplier) = component.multiplier() {
499 loop {
500 let result = Self::expect_multiplier(input, &multiplier);
501 if Self::expect_multiplier_yielded_eof_error(&result) {
502 break;
503 }
504 result?;
505 values.push(Self::parse_component_without_multiplier(
506 context, input, component,
507 )?);
508 }
509 }
510 Ok(values)
511 }
512
513 fn parse_component_without_multiplier(
514 context: &ParserContext,
515 input: &mut CSSParser,
516 component: &SyntaxComponent,
517 ) -> Result<SpecifiedValueComponent, StyleParseError> {
518 let data_type = match component.name() {
519 ComponentName::DataType(ty) => ty,
520 ComponentName::Ident(name) => {
521 let ident = CustomIdent::parse(input, &[])?;
522 if ident != *name {
523 return Err(StyleParseError::custom(
524 StyleParseErrorKind::UnspecifiedError,
525 ));
526 }
527 return Ok(SpecifiedValueComponent::CustomIdent(ident));
528 },
529 };
530
531 let value = match data_type {
532 DataType::Length => {
533 SpecifiedValueComponent::Length(specified::Length::parse(context, input)?)
534 },
535 DataType::Number => {
536 SpecifiedValueComponent::Number(specified::Number::parse(context, input)?)
537 },
538 DataType::Percentage => {
539 SpecifiedValueComponent::Percentage(specified::Percentage::parse(context, input)?)
540 },
541 DataType::LengthPercentage => SpecifiedValueComponent::LengthPercentage(
542 specified::LengthPercentage::parse(context, input)?,
543 ),
544 DataType::Color => {
545 SpecifiedValueComponent::Color(specified::Color::parse(context, input)?)
546 },
547 DataType::Image => {
548 SpecifiedValueComponent::Image(specified::Image::parse_forbid_none(context, input)?)
549 },
550 DataType::Url => {
551 SpecifiedValueComponent::Url(specified::url::SpecifiedUrl::parse(context, input)?)
552 },
553 DataType::Integer => {
554 SpecifiedValueComponent::Integer(specified::Integer::parse(context, input)?)
555 },
556 DataType::Angle => {
557 SpecifiedValueComponent::Angle(specified::Angle::parse(context, input)?)
558 },
559 DataType::Time => {
560 SpecifiedValueComponent::Time(specified::Time::parse(context, input)?)
561 },
562 DataType::Resolution => {
563 SpecifiedValueComponent::Resolution(specified::Resolution::parse(context, input)?)
564 },
565 DataType::TransformFunction => SpecifiedValueComponent::TransformFunction(
566 specified::Transform::parse(context, input)?,
567 ),
568 DataType::CustomIdent => {
569 let name = CustomIdent::parse(input, &[])?;
570 SpecifiedValueComponent::CustomIdent(name)
571 },
572 DataType::TransformList => {
573 let mut values = vec![];
574 let Some(multiplier) = component.unpremultiplied().multiplier() else {
575 debug_assert!(false, "Unpremultiplied <transform-list> had no multiplier?");
576 return Err(StyleParseError::custom(
577 StyleParseErrorKind::PropertySyntaxField(
578 PropertySyntaxParseError::UnexpectedEOF,
579 ),
580 ));
581 };
582 debug_assert_eq!(multiplier, Multiplier::Space);
583 loop {
584 values.push(SpecifiedValueComponent::TransformFunction(
585 specified::Transform::parse(context, input)?,
586 ));
587 let result = Self::expect_multiplier(input, &multiplier);
588 if Self::expect_multiplier_yielded_eof_error(&result) {
589 break;
590 }
591 result?;
592 }
593 let list = ComponentList {
594 multiplier,
595 components: values.into(),
596 };
597 SpecifiedValueComponent::TransformList(list)
598 },
599 DataType::String => {
600 let string = input.expect_string()?;
601 SpecifiedValueComponent::String(string.as_ref().to_owned().into())
602 },
603 };
604 Ok(value)
605 }
606
607 fn expect_multiplier_yielded_eof_error(result: &Result<(), StyleParseError>) -> bool {
608 matches!(
609 result,
610 Err(StyleParseError {
611 kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
612 ..
613 })
614 )
615 }
616
617 fn expect_multiplier(
618 input: &mut CSSParser,
619 multiplier: &Multiplier,
620 ) -> Result<(), StyleParseError> {
621 match multiplier {
622 Multiplier::Space => {
623 input.expect_whitespace()?;
624 if input.is_exhausted() {
625 return Err(StyleParseError::from_basic_kind(
627 BasicParseErrorKind::EndOfInput,
628 ));
629 }
630 Ok(())
631 },
632 Multiplier::Comma => Ok(input.expect_comma()?),
633 }
634 }
635}
636
637#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
639pub struct CustomAnimatedValue {
640 pub(crate) name: crate::custom_properties::Name,
642 pub(crate) value: Option<ComputedValue>,
645}
646
647impl Animate for CustomAnimatedValue {
648 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
649 if self.name != other.name {
650 return Err(());
651 }
652 let value = self.value.animate(&other.value, procedure)?;
653 Ok(Self {
654 name: self.name.clone(),
655 value,
656 })
657 }
658}
659
660impl CustomAnimatedValue {
661 pub(crate) fn from_computed(
662 name: &crate::custom_properties::Name,
663 value: Option<&ComputedValue>,
664 ) -> Self {
665 Self {
666 name: name.clone(),
667 value: value.cloned(),
668 }
669 }
670
671 pub(crate) fn from_declaration(
672 declaration: &properties::CustomDeclaration,
673 context: &mut computed::Context,
674 ) -> Option<Self> {
675 let computed_value = match declaration.value {
676 properties::CustomDeclarationValue::Unparsed(ref value) => Some({
677 debug_assert!(
678 context.builder.stylist.is_some(),
679 "Need a Stylist to get property registration!"
680 );
681 let registration = context
682 .builder
683 .stylist
684 .unwrap()
685 .get_custom_property_registration(&declaration.name);
686 if registration.is_universal() {
687 ComputedValue::universal(Arc::clone(value))
689 } else {
690 let mut input = CSSParser::new(&value.css);
691 SpecifiedValue::compute(
692 &mut input,
693 registration,
694 None,
695 &value.url_data,
696 context,
697 AllowComputationallyDependent::Yes,
698 Default::default(),
699 )
700 .unwrap_or_else(|_| ComputedValue::universal(Arc::clone(value)))
701 }
702 }),
703 properties::CustomDeclarationValue::Parsed(ref v) => Some(v.to_computed_value(context)),
704 properties::CustomDeclarationValue::CSSWideKeyword(keyword) => {
705 let stylist = context.builder.stylist.unwrap();
706 let registration = stylist.get_custom_property_registration(&declaration.name);
707 match keyword {
708 CSSWideKeyword::Initial => stylist
709 .get_custom_property_initial_values()
710 .get(registration, &declaration.name),
711 CSSWideKeyword::Inherit => context
712 .builder
713 .inherited_custom_properties()
714 .get(registration, &declaration.name),
715 CSSWideKeyword::Unset => {
716 if registration.inherits() {
717 context
718 .builder
719 .inherited_custom_properties()
720 .get(registration, &declaration.name)
721 } else {
722 stylist
723 .get_custom_property_initial_values()
724 .get(registration, &declaration.name)
725 }
726 },
727 CSSWideKeyword::Revert
737 | CSSWideKeyword::RevertRule
738 | CSSWideKeyword::RevertLayer => return None,
739 }
740 .cloned()
741 },
742 };
743 Some(Self {
744 name: declaration.name.clone(),
745 value: computed_value,
746 })
747 }
748
749 pub(crate) fn to_declaration(&self) -> properties::PropertyDeclaration {
750 properties::PropertyDeclaration::Custom(properties::CustomDeclaration {
751 name: self.name.clone(),
752 value: match &self.value {
753 Some(value) => value.to_declared_value(),
754 None => CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial),
755 },
756 })
757 }
758}