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