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)]
207pub struct Value<Component> {
208 pub(crate) v: ValueInner<Component>,
210 #[css(skip)]
213 url_data: UrlExtraData,
214 #[css(skip)]
216 pub attr_tainted: bool,
217}
218
219impl<Component: PartialEq> PartialEq for Value<Component> {
220 fn eq(&self, other: &Self) -> bool {
224 self.v == other.v && self.attr_tainted == other.attr_tainted
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 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 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 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#[derive(
295 Animate, ToComputedValue, ToResolvedValue, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToShmem,
296)]
297pub enum ValueInner<Component> {
298 Component(Component),
301 #[animation(error)]
303 Universal(#[ignore_malloc_size_of = "Arc"] Arc<ComputedPropertyValue>),
304 List(#[animation(field_bound)] ComponentList<Component>),
306}
307
308pub type SpecifiedValue = Value<SpecifiedValueComponent>;
310
311pub type ComputedValue = Value<ComputedValueComponent>;
313
314impl SpecifiedValue {
315 pub fn compute<'i, 't>(
318 input: &mut CSSParser<'i, 't>,
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 ) -> Result<ComputedValue, ()> {
326 debug_assert!(!registration.is_universal(), "Shouldn't be needed");
327 let Some(ref syntax) = registration.syntax else {
328 return Err(());
329 };
330 let Ok(value) = Self::parse(
331 input,
332 syntax,
333 url_data,
334 namespaces,
335 allow_computationally_dependent,
336 attr_taint,
337 ) else {
338 return Err(());
339 };
340
341 Ok(value.to_computed_value(context))
342 }
343
344 pub fn parse<'i, 't>(
347 mut input: &mut CSSParser<'i, 't>,
348 syntax: &Descriptor,
349 url_data: &UrlExtraData,
350 namespaces: Option<&FxHashMap<Prefix, Namespace>>,
351 allow_computationally_dependent: AllowComputationallyDependent,
352 attr_taint: AttrTaint,
353 ) -> Result<Self, StyleParseError<'i>> {
354 if syntax.is_universal() {
355 let parsed = ComputedPropertyValue::parse(&mut input, namespaces, url_data)?;
356 return Ok(Self::new(
357 ValueInner::Universal(Arc::new(parsed)),
358 url_data.clone(),
359 ));
360 }
361
362 let mut values = SmallComponentVec::new();
363 let mut multiplier = None;
364 {
365 let mut parser = Parser::new(syntax, &mut values, &mut multiplier);
366 parser.parse(
367 &mut input,
368 url_data,
369 allow_computationally_dependent,
370 attr_taint,
371 )?;
372 }
373 let v = if let Some(multiplier) = multiplier {
374 ValueInner::List(ComponentList {
375 multiplier,
376 components: values.to_vec().into(),
377 })
378 } else {
379 ValueInner::Component(values[0].clone())
380 };
381 Ok(Self::new(v, url_data.clone()))
382 }
383}
384
385impl ComputedValue {
386 fn to_declared_value(&self) -> properties::CustomDeclarationValue {
387 if let ValueInner::Universal(ref var) = self.v {
388 return properties::CustomDeclarationValue::Unparsed(Arc::clone(var));
389 }
390 properties::CustomDeclarationValue::Parsed(Arc::new(ToComputedValue::from_computed_value(
391 self,
392 )))
393 }
394
395 pub fn as_universal(&self) -> Option<&Arc<ComputedPropertyValue>> {
397 if let ValueInner::Universal(ref var) = self.v {
398 Some(var)
399 } else {
400 None
401 }
402 }
403}
404
405pub enum AllowComputationallyDependent {
410 No,
412 Yes,
414}
415
416type SmallComponentVec = SmallVec<[SpecifiedValueComponent; 1]>;
417
418struct Parser<'a> {
419 syntax: &'a Descriptor,
420 output: &'a mut SmallComponentVec,
421 output_multiplier: &'a mut Option<Multiplier>,
422}
423
424impl<'a> Parser<'a> {
425 fn new(
426 syntax: &'a Descriptor,
427 output: &'a mut SmallComponentVec,
428 output_multiplier: &'a mut Option<Multiplier>,
429 ) -> Self {
430 Self {
431 syntax,
432 output,
433 output_multiplier,
434 }
435 }
436
437 fn parse<'i, 't>(
438 &mut self,
439 input: &mut CSSParser<'i, 't>,
440 url_data: &UrlExtraData,
441 allow_computationally_dependent: AllowComputationallyDependent,
442 attr_taint: AttrTaint,
443 ) -> Result<(), StyleParseError<'i>> {
444 use self::AllowComputationallyDependent::*;
445 let parsing_mode = match allow_computationally_dependent {
446 No => ParsingMode::DISALLOW_COMPUTATIONALLY_DEPENDENT,
447 Yes => ParsingMode::DEFAULT,
448 };
449 let ref context = ParserContext::new(
450 Origin::Author,
451 url_data,
452 Some(CssRuleType::Style),
453 parsing_mode,
454 QuirksMode::NoQuirks,
455 Default::default(),
456 None,
457 None,
458 attr_taint,
459 );
460 for component in self.syntax.components.iter() {
461 let result = input.try_parse(|input| {
462 input.parse_entirely(|input| {
463 Self::parse_value(context, input, &component.unpremultiplied())
464 })
465 });
466 let Ok(values) = result else { continue };
467 self.output.extend(values);
468 *self.output_multiplier = component.multiplier();
469 break;
470 }
471 if self.output.is_empty() {
472 return Err(input.new_error(BasicParseErrorKind::EndOfInput));
473 }
474 Ok(())
475 }
476
477 fn parse_value<'i, 't>(
478 context: &ParserContext,
479 input: &mut CSSParser<'i, 't>,
480 component: &SyntaxComponent,
481 ) -> Result<SmallComponentVec, StyleParseError<'i>> {
482 let mut values = SmallComponentVec::new();
483 values.push(Self::parse_component_without_multiplier(
484 context, input, component,
485 )?);
486
487 if let Some(multiplier) = component.multiplier() {
488 loop {
489 let result = Self::expect_multiplier(input, &multiplier);
490 if Self::expect_multiplier_yielded_eof_error(&result) {
491 break;
492 }
493 result?;
494 values.push(Self::parse_component_without_multiplier(
495 context, input, component,
496 )?);
497 }
498 }
499 Ok(values)
500 }
501
502 fn parse_component_without_multiplier<'i, 't>(
503 context: &ParserContext,
504 input: &mut CSSParser<'i, 't>,
505 component: &SyntaxComponent,
506 ) -> Result<SpecifiedValueComponent, StyleParseError<'i>> {
507 let data_type = match component.name() {
508 ComponentName::DataType(ty) => ty,
509 ComponentName::Ident(ref name) => {
510 let ident = CustomIdent::parse(input, &[])?;
511 if ident != *name {
512 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
513 }
514 return Ok(SpecifiedValueComponent::CustomIdent(ident));
515 },
516 };
517
518 let value = match data_type {
519 DataType::Length => {
520 SpecifiedValueComponent::Length(specified::Length::parse(context, input)?)
521 },
522 DataType::Number => {
523 SpecifiedValueComponent::Number(specified::Number::parse(context, input)?)
524 },
525 DataType::Percentage => {
526 SpecifiedValueComponent::Percentage(specified::Percentage::parse(context, input)?)
527 },
528 DataType::LengthPercentage => SpecifiedValueComponent::LengthPercentage(
529 specified::LengthPercentage::parse(context, input)?,
530 ),
531 DataType::Color => {
532 SpecifiedValueComponent::Color(specified::Color::parse(context, input)?)
533 },
534 DataType::Image => {
535 SpecifiedValueComponent::Image(specified::Image::parse_forbid_none(context, input)?)
536 },
537 DataType::Url => {
538 SpecifiedValueComponent::Url(specified::url::SpecifiedUrl::parse(context, input)?)
539 },
540 DataType::Integer => {
541 SpecifiedValueComponent::Integer(specified::Integer::parse(context, input)?)
542 },
543 DataType::Angle => {
544 SpecifiedValueComponent::Angle(specified::Angle::parse(context, input)?)
545 },
546 DataType::Time => {
547 SpecifiedValueComponent::Time(specified::Time::parse(context, input)?)
548 },
549 DataType::Resolution => {
550 SpecifiedValueComponent::Resolution(specified::Resolution::parse(context, input)?)
551 },
552 DataType::TransformFunction => SpecifiedValueComponent::TransformFunction(
553 specified::Transform::parse(context, input)?,
554 ),
555 DataType::CustomIdent => {
556 let name = CustomIdent::parse(input, &[])?;
557 SpecifiedValueComponent::CustomIdent(name)
558 },
559 DataType::TransformList => {
560 let mut values = vec![];
561 let Some(multiplier) = component.unpremultiplied().multiplier() else {
562 debug_assert!(false, "Unpremultiplied <transform-list> had no multiplier?");
563 return Err(
564 input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(
565 PropertySyntaxParseError::UnexpectedEOF,
566 )),
567 );
568 };
569 debug_assert_eq!(multiplier, Multiplier::Space);
570 loop {
571 values.push(SpecifiedValueComponent::TransformFunction(
572 specified::Transform::parse(context, input)?,
573 ));
574 let result = Self::expect_multiplier(input, &multiplier);
575 if Self::expect_multiplier_yielded_eof_error(&result) {
576 break;
577 }
578 result?;
579 }
580 let list = ComponentList {
581 multiplier,
582 components: values.into(),
583 };
584 SpecifiedValueComponent::TransformList(list)
585 },
586 DataType::String => {
587 let string = input.expect_string()?;
588 SpecifiedValueComponent::String(string.as_ref().to_owned().into())
589 },
590 };
591 Ok(value)
592 }
593
594 fn expect_multiplier_yielded_eof_error<'i>(result: &Result<(), StyleParseError<'i>>) -> bool {
595 matches!(
596 result,
597 Err(StyleParseError {
598 kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
599 ..
600 })
601 )
602 }
603
604 fn expect_multiplier<'i, 't>(
605 input: &mut CSSParser<'i, 't>,
606 multiplier: &Multiplier,
607 ) -> Result<(), StyleParseError<'i>> {
608 match multiplier {
609 Multiplier::Space => {
610 input.expect_whitespace()?;
611 if input.is_exhausted() {
612 return Err(input.new_error(BasicParseErrorKind::EndOfInput));
614 }
615 Ok(())
616 },
617 Multiplier::Comma => Ok(input.expect_comma()?),
618 }
619 }
620}
621
622#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
624pub struct CustomAnimatedValue {
625 pub(crate) name: crate::custom_properties::Name,
627 pub(crate) value: Option<ComputedValue>,
630}
631
632impl Animate for CustomAnimatedValue {
633 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
634 if self.name != other.name {
635 return Err(());
636 }
637 let value = self.value.animate(&other.value, procedure)?;
638 Ok(Self {
639 name: self.name.clone(),
640 value,
641 })
642 }
643}
644
645impl CustomAnimatedValue {
646 pub(crate) fn from_computed(
647 name: &crate::custom_properties::Name,
648 value: Option<&ComputedValue>,
649 ) -> Self {
650 Self {
651 name: name.clone(),
652 value: value.cloned(),
653 }
654 }
655
656 pub(crate) fn from_declaration(
657 declaration: &properties::CustomDeclaration,
658 context: &mut computed::Context,
659 ) -> Option<Self> {
660 let computed_value = match declaration.value {
661 properties::CustomDeclarationValue::Unparsed(ref value) => Some({
662 debug_assert!(
663 context.builder.stylist.is_some(),
664 "Need a Stylist to get property registration!"
665 );
666 let registration = context
667 .builder
668 .stylist
669 .unwrap()
670 .get_custom_property_registration(&declaration.name);
671 if registration.is_universal() {
672 ComputedValue::new(
674 ValueInner::Universal(Arc::clone(value)),
675 value.url_data.clone(),
676 )
677 } else {
678 let mut input = cssparser::ParserInput::new(&value.css);
679 let mut input = CSSParser::new(&mut input);
680 SpecifiedValue::compute(
681 &mut input,
682 registration,
683 None,
684 &value.url_data,
685 context,
686 AllowComputationallyDependent::Yes,
687 Default::default(),
688 )
689 .unwrap_or_else(|_| {
690 ComputedValue::new(
691 ValueInner::Universal(Arc::clone(value)),
692 value.url_data.clone(),
693 )
694 })
695 }
696 }),
697 properties::CustomDeclarationValue::Parsed(ref v) => Some(v.to_computed_value(context)),
698 properties::CustomDeclarationValue::CSSWideKeyword(keyword) => {
699 let stylist = context.builder.stylist.unwrap();
700 let registration = stylist.get_custom_property_registration(&declaration.name);
701 match keyword {
702 CSSWideKeyword::Initial => stylist
703 .get_custom_property_initial_values()
704 .get(registration, &declaration.name),
705 CSSWideKeyword::Inherit => context
706 .builder
707 .inherited_custom_properties()
708 .get(registration, &declaration.name),
709 CSSWideKeyword::Unset => {
710 if registration.inherits() {
711 context
712 .builder
713 .inherited_custom_properties()
714 .get(registration, &declaration.name)
715 } else {
716 stylist
717 .get_custom_property_initial_values()
718 .get(registration, &declaration.name)
719 }
720 },
721 CSSWideKeyword::Revert
731 | CSSWideKeyword::RevertRule
732 | CSSWideKeyword::RevertLayer => return None,
733 }
734 .cloned()
735 },
736 };
737 Some(Self {
738 name: declaration.name.clone(),
739 value: computed_value,
740 })
741 }
742
743 pub(crate) fn to_declaration(&self) -> properties::PropertyDeclaration {
744 properties::PropertyDeclaration::Custom(properties::CustomDeclaration {
745 name: self.name.clone(),
746 value: match &self.value {
747 Some(value) => value.to_declared_value(),
748 None => CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial),
749 },
750 })
751 }
752}