1pub mod attr;
9pub mod env;
10pub mod var;
11
12use crate::custom_properties::{
13 ComputedSubstitutionFunctions, ComputedValue, Name, VariableValue, compute_value,
14};
15use crate::derives::*;
16use crate::dom::AttributeTracker;
17use crate::parser::{Parse, ParserContext};
18use crate::properties::PropertyIdRef;
19use crate::properties_and_values::{
20 rule::Descriptors as PropertyDescriptors, value::ComputedValue as ComputedRegisteredValue,
21};
22use crate::stylesheets::{Origin, UrlExtraData};
23use crate::stylist::Stylist;
24use crate::values::computed;
25use crate::values::specified::NoCalcLength;
26use cssparser::{
27 CowRcStr, Delimiter, Delimiters, Parser, SourcePosition, Token, TokenSerializationType,
28};
29use servo_arc::Arc;
30use smallvec::SmallVec;
31use std::borrow::Cow;
32use std::num;
33use strum_macros::VariantNames;
34use style_traits::{ParseError, ParsingMode, StyleParseErrorKind};
35
36pub use self::attr::AttrFunction;
37pub use self::env::EnvFunction;
38pub use self::var::VarFunction;
39
40#[derive(Copy, Clone, Debug, MallocSizeOf, Hash, Eq, PartialEq, ToShmem, Parse, VariantNames)]
42#[strum(serialize_all = "kebab-case")]
43pub enum SubstitutionFunctionKind {
44 Var,
46 Env,
48 Attr,
50}
51
52pub struct SubstitutionParserContext<'a> {
54 pub parser_context: &'a ParserContext<'a>,
56 pub input_start: SourcePosition,
58 pub missing_closing_characters: String,
60}
61
62impl<'a> SubstitutionParserContext<'a> {
63 pub fn new(parser_context: &'a ParserContext<'a>, input_start: SourcePosition) -> Self {
65 Self {
66 parser_context,
67 input_start,
68 missing_closing_characters: String::new(),
69 }
70 }
71}
72
73#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
75pub struct ArbitraryDeclarationValue {
76 pub start: num::NonZeroUsize,
78 pub end: num::NonZeroUsize,
80 first_token_type: TokenSerializationType,
81 last_token_type: TokenSerializationType,
82 pub references: References,
84}
85
86impl ArbitraryDeclarationValue {
87 fn parse_internal(
88 input: &mut Parser,
89 context: &mut SubstitutionParserContext,
90 additional_delimiters: Delimiters,
91 ) -> Result<Self, ParseError> {
92 let start = num::NonZeroUsize::new(
93 input.position().byte_index() - context.input_start.byte_index(),
94 )
95 .unwrap();
96 let mut references = References::default();
97 let (first_token_type, last_token_type) =
100 parse_declaration_value(input, context, &mut references, additional_delimiters)?;
101 let end = num::NonZeroUsize::new(
102 input.position().byte_index() - context.input_start.byte_index()
103 + context.missing_closing_characters.len(),
104 )
105 .unwrap();
106 Ok(Self {
107 start,
108 end,
109 first_token_type,
110 last_token_type,
111 references,
112 })
113 }
114
115 fn parse_free_form<'i>(
121 input: &mut Parser<'i>,
122 context: &mut SubstitutionParserContext,
123 additional_delimiters: Delimiters,
124 is_non_strict: bool,
125 ) -> Result<(Self, &'i str), ParseError> {
126 if input
127 .try_parse(|input| input.expect_curly_bracket_block())
128 .is_ok()
129 {
130 let mut inner_end_position = None;
131 let result = input.parse_nested_block(|input| {
132 input.skip_whitespace();
133 let start_position = input.position();
134 let value = Self::parse_internal(input, context, Delimiter::None)?;
135 let end_position = input.position();
136 inner_end_position = Some(end_position);
137 Ok((value, input.slice(start_position..end_position)))
138 })?;
139 if inner_end_position.unwrap() == input.position() {
140 context.missing_closing_characters.push('}');
141 }
142 return Ok(result);
143 }
144 input.skip_whitespace();
145 let start_position = input.position();
146 let delimiters = if is_non_strict {
147 additional_delimiters
148 } else {
149 additional_delimiters | Delimiter::Comma | Delimiter::CurlyBracketBlock
150 };
151 let value = Self::parse_internal(input, context, delimiters)?;
152 Ok((value, input.slice_from(start_position)))
153 }
154
155 fn is_empty(&self) -> bool {
156 self.first_token_type == TokenSerializationType::Nothing
157 }
158}
159
160fn parse_optional_fallback(
161 input: &mut Parser,
162 context: &mut SubstitutionParserContext,
163) -> Result<Option<ArbitraryDeclarationValue>, ParseError> {
164 if input.try_parse(|input| input.expect_comma()).is_ok() {
165 let (fallback, _) = ArbitraryDeclarationValue::parse_free_form(
166 input,
167 context,
168 Delimiter::None,
169 true,
170 )?;
171 Ok(Some(fallback))
172 } else {
173 let state = input.state();
174 let mut ignored_references = References::default();
178 parse_declaration_value_block(input, context, &mut ignored_references)?;
179 input.reset(&state);
180 Ok(None)
181 }
182}
183
184pub fn parse_declaration_value(
186 input: &mut Parser,
187 context: &mut SubstitutionParserContext,
188 references: &mut References,
189 additional_delimiters: Delimiters,
190) -> Result<(TokenSerializationType, TokenSerializationType), ParseError> {
191 input.parse_until_before(
192 Delimiter::Bang | Delimiter::Semicolon | additional_delimiters,
193 |input| parse_declaration_value_block(input, context, references),
194 )
195}
196
197fn parse_declaration_value_block(
199 input: &mut Parser,
200 context: &mut SubstitutionParserContext,
201 references: &mut References,
202) -> Result<(TokenSerializationType, TokenSerializationType), ParseError> {
203 let mut is_first = true;
204 let mut first_token_type = TokenSerializationType::Nothing;
205 let mut last_token_type = TokenSerializationType::Nothing;
206 let mut prev_reference_index: Option<usize> = None;
207 loop {
208 let token_start = input.position();
209 let Ok(token) = input.next_including_whitespace_and_comments() else {
210 break;
211 };
212
213 let prev_token_type = last_token_type;
214 let serialization_type = token.serialization_type();
215 last_token_type = serialization_type;
216 if is_first {
217 first_token_type = last_token_type;
218 is_first = false;
219 }
220
221 macro_rules! nested {
222 ($closing:expr) => {{
223 let mut inner_end_position = None;
224 let result = input.parse_nested_block(|input| {
225 let result = parse_declaration_value_block(input, context, references)?;
226 inner_end_position = Some(input.position());
227 Ok(result)
228 })?;
229 if inner_end_position.unwrap() == input.position() {
230 context.missing_closing_characters.push_str($closing);
231 }
232 result
233 }};
234 }
235 if let Some(index) = prev_reference_index.take() {
236 references.refs[index].next_token_type = serialization_type;
237 }
238 match *token {
239 Token::Comment(_) => {
240 let token_slice = input.slice_from(token_start);
241 if !token_slice.ends_with("*/") {
242 context
243 .missing_closing_characters
244 .push_str(if token_slice.ends_with('*') {
245 "/"
246 } else {
247 "*/"
248 })
249 }
250 },
251 Token::BadUrl(..) => {
252 let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock;
253 return Err(ParseError::custom(e));
254 },
255 Token::BadString(..) => {
256 let e = StyleParseErrorKind::BadStringInDeclarationValueBlock;
257 return Err(ParseError::custom(e));
258 },
259 Token::CloseParenthesis => {
260 let e = StyleParseErrorKind::UnbalancedCloseParenthesisInDeclarationValueBlock;
261 return Err(ParseError::custom(e));
262 },
263 Token::CloseSquareBracket => {
264 let e = StyleParseErrorKind::UnbalancedCloseSquareBracketInDeclarationValueBlock;
265 return Err(ParseError::custom(e));
266 },
267 Token::CloseCurlyBracket => {
268 let e = StyleParseErrorKind::UnbalancedCloseCurlyBracketInDeclarationValueBlock;
269 return Err(ParseError::custom(e));
270 },
271 Token::Function(ref name) => {
272 let substitution_kind = SubstitutionFunctionKind::from_ident(name).ok();
273 if let Some(substitution_kind) = substitution_kind {
274 let start = token_start.byte_index() - context.input_start.byte_index();
275 let mut input_end_position = None;
276 let function = input.parse_nested_block(|input| {
277 let function =
278 SubstitutionFunction::parse(substitution_kind, input, context)?;
279 input.expect_exhausted()?;
280 input_end_position = Some(input.position());
281 Ok(function)
282 })?;
283 if input_end_position.unwrap() == input.position() {
284 context.missing_closing_characters.push(')');
285 }
286 let end = input.position().byte_index() - context.input_start.byte_index()
287 + context.missing_closing_characters.len();
288 references.flags |= function.reference_flags();
289 function.for_each_unparsed_value(|value| {
292 references.flags |= value.references.flags
293 });
294 prev_reference_index = Some(references.refs.len());
295 references.refs.push(SubstitutionFunctionReference {
296 start,
297 end,
298 prev_token_type,
299 next_token_type: TokenSerializationType::Nothing,
301 function,
302 });
303 } else {
304 nested!(")");
305 }
306 },
307 Token::ParenthesisBlock => {
308 nested!(")");
309 },
310 Token::CurlyBracketBlock => {
311 nested!("}");
312 },
313 Token::SquareBracketBlock => {
314 nested!("]");
315 },
316 Token::QuotedString(_) => {
317 let token_slice = input.slice_from(token_start);
318 let quote = &token_slice[..1];
319 debug_assert!(matches!(quote, "\"" | "'"));
320 if !(token_slice.ends_with(quote) && token_slice.len() > 1) {
321 context.missing_closing_characters.push_str(quote)
322 }
323 },
324 Token::Ident(ref value)
325 | Token::AtKeyword(ref value)
326 | Token::Hash(ref value)
327 | Token::IDHash(ref value)
328 | Token::UnquotedUrl(ref value)
329 | Token::Dimension {
330 unit: ref value, ..
331 } => {
332 references.flags.insert(ReferenceFlags::from_unit(value));
333 let is_unquoted_url = matches!(token, Token::UnquotedUrl(_));
334 if value.ends_with("�") && input.slice_from(token_start).ends_with("\\") {
335 context.missing_closing_characters.push('�')
340 }
341 if is_unquoted_url && !input.slice_from(token_start).ends_with(")") {
342 context.missing_closing_characters.push(')');
343 }
344 },
345 _ => {},
346 };
347 }
348 Ok((first_token_type, last_token_type))
349}
350
351#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
353pub enum SubstitutionArgument<T> {
354 Unparsed(ArbitraryDeclarationValue),
356 Parsed(T),
358 Invalid,
361}
362
363impl<T: Parse> SubstitutionArgument<T> {
364 pub fn try_parse(
368 input: &mut Parser,
369 context: &mut SubstitutionParserContext,
370 delimiters: Delimiters,
371 ) -> Result<Self, ParseError> {
372 let (value, css) = ArbitraryDeclarationValue::parse_free_form(
373 input, context, delimiters, false,
374 )?;
375 if value.is_empty() {
376 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
377 }
378 if !value.references.has_references() {
379 let parser_context = context.parser_context;
380 if let Ok(result) =
381 Parser::new(css).parse_entirely(|input| T::parse(parser_context, input))
382 {
383 return Ok(Self::Parsed(result));
384 }
385 return Ok(Self::Invalid);
386 }
387 Ok(Self::Unparsed(value))
388 }
389}
390
391impl<T: Clone + Parse> SubstitutionArgument<T> {
392 pub fn substitute_and_parse<'a>(
395 &'a self,
396 source: &'a VariableValue,
397 context: &mut SubstitutionReplacementContext<'a, '_, '_, '_>,
398 ) -> ParsedArgument<'a, T> {
399 let arg = match self {
400 Self::Unparsed(arg) => arg,
401 Self::Parsed(value) => {
402 return ParsedArgument {
403 value: Some(Cow::Borrowed(value)),
404 attr_tainted: false,
405 };
406 },
407 Self::Invalid => {
408 return ParsedArgument {
409 value: None,
410 attr_tainted: false,
411 };
412 },
413 };
414 let Ok(substitution) = context.substitute_declaration_value(arg, source) else {
415 return ParsedArgument {
416 value: None,
417 attr_tainted: false,
418 };
419 };
420 let parser_context = context.parser_context(&source.url_data);
421 ParsedArgument {
422 value: Parser::new(&substitution.css)
423 .parse_entirely(|input| T::parse(&parser_context, input))
424 .ok()
425 .map(Cow::Owned),
426 attr_tainted: substitution.attr_tainted,
427 }
428 }
429}
430
431pub struct ParsedArgument<'a, T: Clone> {
433 pub value: Option<Cow<'a, T>>,
435 pub attr_tainted: bool,
437}
438
439#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
441pub enum SubstitutionFunction {
442 Var(VarFunction),
444 Env(EnvFunction),
446 Attr(AttrFunction),
448}
449
450impl SubstitutionFunction {
451 pub fn parse(
454 kind: SubstitutionFunctionKind,
455 input: &mut Parser,
456 context: &mut SubstitutionParserContext,
457 ) -> Result<Self, ParseError> {
458 Ok(match kind {
459 SubstitutionFunctionKind::Var => Self::Var(VarFunction::parse(input, context)?),
460 SubstitutionFunctionKind::Env => Self::Env(EnvFunction::parse(input, context)?),
461 SubstitutionFunctionKind::Attr => Self::Attr(AttrFunction::parse(input, context)?),
462 })
463 }
464
465 #[inline]
467 pub fn reference_flags(&self) -> ReferenceFlags {
468 match self {
469 Self::Var(..) => ReferenceFlags::VAR,
470 Self::Env(..) => ReferenceFlags::ENV,
471 Self::Attr(..) => ReferenceFlags::ATTR,
472 }
473 }
474
475 fn replace<'a>(
476 &'a self,
477 source: &'a VariableValue,
478 context: &mut SubstitutionReplacementContext<'a, '_, '_, '_>,
479 ) -> Result<Substitution<'a>, ()> {
480 match self {
481 Self::Var(function) => function.replace(source, context),
482 Self::Env(function) => function.replace(source, context),
483 Self::Attr(function) => function.replace(source, context),
484 }
485 }
486
487 #[inline]
489 pub fn for_each_unparsed_value<F>(&self, f: F)
490 where
491 F: FnMut(&ArbitraryDeclarationValue),
492 {
493 match self {
494 Self::Var(function) => function.for_each_unparsed_value(f),
495 Self::Env(function) => function.for_each_unparsed_value(f),
496 Self::Attr(function) => function.for_each_unparsed_value(f),
497 }
498 }
499}
500
501#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
503pub struct SubstitutionFunctionReference {
504 pub start: usize,
506 pub end: usize,
508 pub prev_token_type: TokenSerializationType,
510 pub next_token_type: TokenSerializationType,
512 pub function: SubstitutionFunction,
514}
515
516#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, MallocSizeOf, ToShmem)]
518pub struct ReferenceFlags(u8);
519
520bitflags! {
521 impl ReferenceFlags : u8 {
522 const FONT_UNITS = 1 << 0;
524 const ROOT_FONT_UNITS = 1 << 1;
526 const LH_UNITS = 1 << 2;
528 const ROOT_LH_UNITS = 1 << 3;
530 const COLOR_SCHEME = 1 << 4;
532 const NON_ROOT_DEPENDENCIES = Self::FONT_UNITS.0 | Self::LH_UNITS.0;
534 const ROOT_DEPENDENCIES = Self::ROOT_FONT_UNITS.0 | Self::ROOT_LH_UNITS.0;
536 const NON_CUSTOM = Self::NON_ROOT_DEPENDENCIES.0 | Self::ROOT_DEPENDENCIES.0;
538 const ATTR = 1 << 5;
540 const ENV = 1 << 6;
542 const VAR = 1 << 7;
544 }
545}
546
547impl ReferenceFlags {
548 fn from_unit(value: &CowRcStr) -> Self {
549 if value.eq_ignore_ascii_case(NoCalcLength::LH) {
554 return Self::FONT_UNITS | Self::LH_UNITS;
555 }
556 if value.eq_ignore_ascii_case(NoCalcLength::EM)
557 || value.eq_ignore_ascii_case(NoCalcLength::EX)
558 || value.eq_ignore_ascii_case(NoCalcLength::CAP)
559 || value.eq_ignore_ascii_case(NoCalcLength::CH)
560 || value.eq_ignore_ascii_case(NoCalcLength::IC)
561 {
562 return Self::FONT_UNITS;
563 }
564 if value.eq_ignore_ascii_case(NoCalcLength::RLH) {
565 return Self::ROOT_FONT_UNITS | Self::ROOT_LH_UNITS;
566 }
567 if value.eq_ignore_ascii_case(NoCalcLength::REM)
568 || value.eq_ignore_ascii_case(NoCalcLength::REX)
569 || value.eq_ignore_ascii_case(NoCalcLength::RCH)
570 || value.eq_ignore_ascii_case(NoCalcLength::RCAP)
571 || value.eq_ignore_ascii_case(NoCalcLength::RIC)
572 {
573 return Self::ROOT_FONT_UNITS;
574 }
575 Self::empty()
576 }
577}
578
579#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
582pub struct References {
583 pub refs: Vec<SubstitutionFunctionReference>,
585 pub flags: ReferenceFlags,
590}
591
592impl References {
593 pub fn has_references(&self) -> bool {
595 !self.refs.is_empty()
596 }
597
598 pub(crate) fn non_custom_references(&self, is_root_element: bool) -> ReferenceFlags {
599 let mut mask = ReferenceFlags::NON_ROOT_DEPENDENCIES;
600 if is_root_element {
601 mask |= ReferenceFlags::ROOT_DEPENDENCIES
602 }
603 self.flags & mask
604 }
605}
606
607#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToShmem, ToComputedValue)]
609pub struct AttrTaintedRange {
610 start: usize,
612 end: usize,
614}
615
616impl AttrTaintedRange {
617 #[inline(always)]
619 pub fn new(start: usize, end: usize) -> Self {
620 debug_assert!(start <= end);
621 Self { start, end }
622 }
623}
624
625#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
630pub struct AttrTaint(SmallVec<[AttrTaintedRange; 1]>);
631
632impl AttrTaint {
633 #[inline(always)]
636 pub fn should_disallow_urls_in_range(&self, range: &AttrTaintedRange) -> bool {
637 self.0
638 .iter()
639 .any(|r| r.start <= range.end && r.end >= range.start)
640 }
641
642 #[inline(always)]
644 pub fn is_empty(&self) -> bool {
645 self.0.is_empty()
646 }
647
648 #[inline(always)]
649 fn new_fully_tainted(end: usize) -> Self {
650 let mut taint = Self::default();
651 taint.push(0, end);
652 taint
653 }
654
655 #[inline(always)]
657 pub fn push(&mut self, start: usize, end: usize) {
658 self.0.push(AttrTaintedRange::new(start, end));
659 }
660}
661
662#[derive(Default, Debug)]
664pub struct Substitution<'a> {
665 pub css: Cow<'a, str>,
667 pub first_token_type: TokenSerializationType,
669 pub last_token_type: TokenSerializationType,
671 pub attr_tainted: bool,
673}
674
675impl<'a> Substitution<'a> {
676 pub fn from_value(v: VariableValue, attr_tainted: bool) -> Self {
678 Substitution {
679 css: v.css.into(),
680 first_token_type: v.first_token_type,
681 last_token_type: v.last_token_type,
682 attr_tainted,
683 }
684 }
685
686 pub fn into_value(
688 self,
689 url_data: &UrlExtraData,
690 registration: &PropertyDescriptors,
691 computed_context: &computed::Context,
692 property_id: Option<PropertyIdRef>,
693 ) -> Result<ComputedRegisteredValue, ()> {
694 if registration.is_universal() {
695 let mut value = ComputedRegisteredValue::universal(Arc::new(VariableValue::new(
696 self.css.into_owned(),
697 url_data,
698 self.first_token_type,
699 self.last_token_type,
700 )));
701 value.attr_tainted |= self.attr_tainted;
702 return Ok(value);
703 }
704 let taint = if self.attr_tainted {
705 AttrTaint::new_fully_tainted(self.css.len())
710 } else {
711 AttrTaint::default()
712 };
713 let mut v = compute_value(
714 &self.css,
715 url_data,
716 registration,
717 computed_context,
718 taint,
719 property_id,
720 )?;
721 v.attr_tainted |= self.attr_tainted;
722 Ok(v)
723 }
724
725 pub fn new(
727 css: Cow<'a, str>,
728 first_token_type: TokenSerializationType,
729 last_token_type: TokenSerializationType,
730 attr_tainted: bool,
731 ) -> Self {
732 Self {
733 css,
734 first_token_type,
735 last_token_type,
736 attr_tainted,
737 }
738 }
739}
740
741pub struct SubstitutionReplacementContext<'a, 'b, 'c, 't> {
743 pub property_id: Option<PropertyIdRef<'b>>,
745 pub substitution_functions: &'a ComputedSubstitutionFunctions,
747 pub stylist: &'b Stylist,
749 pub computed_context: &'b computed::Context<'c>,
751 pub attribute_tracker: &'b mut AttributeTracker<'t>,
753 pub seen: SmallVec<[Name; 8]>,
755}
756
757impl<'a, 'b, 'c, 't> SubstitutionReplacementContext<'a, 'b, 'c, 't> {
758 pub fn new(
760 property_id: Option<PropertyIdRef<'b>>,
761 substitution_functions: &'a ComputedSubstitutionFunctions,
762 stylist: &'b Stylist,
763 computed_context: &'b computed::Context<'c>,
764 attribute_tracker: &'b mut AttributeTracker<'t>,
765 ) -> Self {
766 Self {
767 property_id,
768 substitution_functions,
769 stylist,
770 computed_context,
771 attribute_tracker,
772 seen: SmallVec::new(),
773 }
774 }
775
776 pub fn substitute_variable_value(
779 &mut self,
780 variable_value: &'a VariableValue,
781 attr_taint: Option<&mut AttrTaint>,
782 ) -> Result<Substitution<'a>, ()> {
783 self.substitute_chunk(
784 variable_value,
785 0,
786 variable_value.css.len(),
787 variable_value.first_token_type,
788 variable_value.last_token_type,
789 &variable_value.references.refs,
790 attr_taint,
791 )
792 }
793
794 pub fn substitute_declaration_value(
796 &mut self,
797 value: &'a ArbitraryDeclarationValue,
798 source: &'a VariableValue,
799 ) -> Result<Substitution<'a>, ()> {
800 self.substitute_chunk(
801 source,
802 value.start.get(),
803 value.end.get(),
804 value.first_token_type,
805 value.last_token_type,
806 &value.references.refs,
807 None,
808 )
809 }
810
811 fn substitute_chunk(
812 &mut self,
813 source: &'a VariableValue,
814 start: usize,
815 end: usize,
816 first_token_type: TokenSerializationType,
817 last_token_type: TokenSerializationType,
818 references: &'a [SubstitutionFunctionReference],
819 mut attr_taint: Option<&mut AttrTaint>,
820 ) -> Result<Substitution<'a>, ()> {
821 if start == end {
822 return Ok(Substitution::default());
824 }
825 let css = &source.css;
826 if references.is_empty() {
828 let result = &css[start..end];
829 return Ok(Substitution::new(
830 Cow::Borrowed(result),
831 first_token_type,
832 last_token_type,
833 Default::default(),
834 ));
835 }
836
837 let mut substituted = ComputedValue::empty(&source.url_data);
838 let mut next_token_type = first_token_type;
839 let mut cur_pos = start;
840 let mut attr_tainted = false;
841 for reference in references {
842 if reference.start != cur_pos {
843 substituted.push(
844 &css[cur_pos..reference.start],
845 next_token_type,
846 reference.prev_token_type,
847 None,
848 )?;
849 }
850
851 let substitution = reference.function.replace(source, self)?;
852
853 if reference.start == start && reference.end == end {
855 if let Some(taint) = attr_taint.filter(|_| substitution.attr_tainted) {
856 taint.push(start, substitution.css.len());
857 }
858 return Ok(substitution);
859 }
860
861 substituted.push(
862 &substitution.css,
863 substitution.first_token_type,
864 substitution.last_token_type,
865 attr_taint
866 .as_deref_mut()
867 .filter(|_| substitution.attr_tainted),
868 )?;
869 attr_tainted |= substitution.attr_tainted;
870 next_token_type = reference.next_token_type;
871 cur_pos = reference.end;
872 }
873 if cur_pos != end {
875 substituted.push(
876 &css[cur_pos..end],
877 next_token_type,
878 last_token_type,
879 None,
880 )?;
881 }
882 Ok(Substitution::from_value(substituted, attr_tainted))
883 }
884
885 pub fn parser_context(&self, url_data: &'b UrlExtraData) -> ParserContext<'b> {
887 ParserContext::new_with_property_id(
888 Origin::Author,
889 url_data,
890 None,
891 ParsingMode::DEFAULT,
892 self.computed_context.quirks_mode,
893 Default::default(),
894 None,
895 None,
896 AttrTaint::default(),
897 self.property_id,
898 )
899 }
900}
901
902#[derive(Debug)]
904pub struct SubstitutionResult<'a> {
905 pub css: Cow<'a, str>,
907 pub attr_taint: AttrTaint,
909}
910
911pub fn substitute<'a>(
913 variable_value: &'a VariableValue,
914 property_id: Option<PropertyIdRef>,
915 substitution_functions: &'a ComputedSubstitutionFunctions,
916 stylist: &Stylist,
917 computed_context: &computed::Context,
918 attribute_tracker: &mut AttributeTracker,
919) -> Result<SubstitutionResult<'a>, ()> {
920 debug_assert!(variable_value.has_references());
921 let mut attr_taint = AttrTaint::default();
922 let v = SubstitutionReplacementContext::new(
923 property_id,
924 substitution_functions,
925 stylist,
926 computed_context,
927 attribute_tracker,
928 )
929 .substitute_variable_value(variable_value, Some(&mut attr_taint))?;
930 Ok(SubstitutionResult {
931 css: v.css,
932 attr_taint,
933 })
934}