1use crate::applicable_declarations::{CascadePriority, RevertKind};
8use crate::color::AbsoluteColor;
9use crate::computed_value_flags::ComputedValueFlags;
10use crate::context::TreeCountingCaches;
11use crate::custom_properties::{
12 get_attr_value_for_cycle_resolution, handle_invalid_at_computed_value_time,
13 remove_and_insert_initial_value, substitute_references_if_needed_and_apply,
14 ComputedCustomProperties, ComputedSubstitutionFunctions, Name, NonCustomReferenceMap,
15 ReferenceFlags, References, SingleNonCustomReference, SubstitutionFunctionKind, VariableValue,
16};
17use crate::dom::{AttributeTracker, DummyElementContext, ElementContext, TElement};
18#[cfg(feature = "gecko")]
19use crate::font_metrics::FontMetricsOrientation;
20use crate::properties::{
21 property_counts, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, LonghandId,
22 LonghandIdSet, PrioritaryPropertyId, PrioritaryPropertyIdSet, PropertyDeclaration,
23 PropertyDeclarationId, PropertyFlags, ShorthandsWithPropertyReferencesCache, StyleBuilder,
24 CASCADE_PROPERTY,
25};
26use crate::properties::{CustomDeclaration, CustomDeclarationValue, UnparsedValue};
27use crate::properties_and_values::rule::Descriptors as PropertyDescriptors;
28use crate::properties_and_values::value::ComputedValue as ComputedRegisteredValue;
29use crate::rule_cache::{RuleCache, RuleCacheConditions};
30use crate::rule_tree::{CascadeLevel, CascadeOrigin, RuleCascadeFlags, StrongRuleNode};
31use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet};
32use crate::selector_parser::PseudoElement;
33use crate::shared_lock::StylesheetGuards;
34use crate::style_adjuster::StyleAdjuster;
35use crate::stylesheets::container_rule::ContainerSizeQuery;
36use crate::stylesheets::layer_rule::LayerOrder;
37use crate::stylesheets::UrlExtraData;
38use crate::stylist::Stylist;
39use crate::values::computed::ToComputedValue;
40#[cfg(feature = "gecko")]
41use crate::values::specified::length::FontBaseSize;
42use crate::values::specified::position::PositionTryFallbacksTryTactic;
43use crate::values::{computed, specified};
44use rustc_hash::FxHashMap;
45use selectors::matching::ElementSelectorFlags;
46use servo_arc::Arc;
47use smallvec::SmallVec;
48use std::borrow::Cow;
49use std::cmp;
50use std::collections::hash_map::Entry;
51
52#[derive(Copy, Clone)]
54#[allow(missing_docs)]
55pub enum FirstLineReparenting<'a> {
56 No,
57 Yes {
58 style_to_reparent: &'a ComputedValues,
62 },
63}
64
65pub fn cascade<E>(
80 stylist: &Stylist,
81 pseudo: Option<&PseudoElement>,
82 rule_node: &StrongRuleNode,
83 guards: &StylesheetGuards,
84 parent_style: Option<&ComputedValues>,
85 layout_parent_style: Option<&ComputedValues>,
86 first_line_reparenting: FirstLineReparenting,
87 try_tactic: &PositionTryFallbacksTryTactic,
88 visited_rules: Option<&StrongRuleNode>,
89 cascade_input_flags: ComputedValueFlags,
90 included_cascade_flags: RuleCascadeFlags,
91 rule_cache: Option<&RuleCache>,
92 rule_cache_conditions: &mut RuleCacheConditions,
93 element: Option<E>,
94 tree_counting_caches: &mut TreeCountingCaches,
95) -> Arc<ComputedValues>
96where
97 E: TElement,
98{
99 cascade_rules(
100 stylist,
101 pseudo,
102 rule_node,
103 guards,
104 parent_style,
105 layout_parent_style,
106 first_line_reparenting,
107 try_tactic,
108 CascadeMode::Unvisited { visited_rules },
109 cascade_input_flags,
110 included_cascade_flags,
111 rule_cache,
112 rule_cache_conditions,
113 element,
114 tree_counting_caches,
115 )
116}
117
118struct DeclarationIterator<'a> {
119 guards: &'a StylesheetGuards<'a>,
121 restriction: Option<PropertyFlags>,
122 current_rule_node: Option<&'a StrongRuleNode>,
124 declarations: DeclarationImportanceIterator<'a>,
126 priority: CascadePriority,
127}
128
129impl<'a> DeclarationIterator<'a> {
130 #[inline]
131 fn new(
132 rule_node: &'a StrongRuleNode,
133 guards: &'a StylesheetGuards,
134 pseudo: Option<&PseudoElement>,
135 ) -> Self {
136 let restriction = pseudo.and_then(|p| p.property_restriction());
137 let mut iter = Self {
138 guards,
139 current_rule_node: Some(rule_node),
140 priority: CascadePriority::new(
141 CascadeLevel::new(CascadeOrigin::UA),
142 LayerOrder::root(),
143 RuleCascadeFlags::empty(),
144 ),
145 declarations: DeclarationImportanceIterator::default(),
146 restriction,
147 };
148 iter.update_for_node(rule_node);
149 iter
150 }
151
152 fn update_for_node(&mut self, node: &'a StrongRuleNode) {
153 self.priority = node.cascade_priority();
154 let guard = self.priority.cascade_level().origin().guard(&self.guards);
155 self.declarations = match node.style_source() {
156 Some(source) => source.read(guard).declaration_importance_iter(),
157 None => DeclarationImportanceIterator::default(),
158 };
159 }
160}
161
162impl<'a> Iterator for DeclarationIterator<'a> {
163 type Item = (&'a PropertyDeclaration, CascadePriority);
164
165 #[inline]
166 fn next(&mut self) -> Option<Self::Item> {
167 loop {
168 if let Some((decl, importance)) = self.declarations.next_back() {
169 if self.priority.cascade_level().is_important() != importance.important() {
170 continue;
171 }
172
173 if let Some(restriction) = self.restriction {
174 if let PropertyDeclarationId::Longhand(id) = decl.id() {
179 if !id.flags().contains(restriction)
180 && self.priority.cascade_level().origin() != CascadeOrigin::UA
181 {
182 continue;
183 }
184 }
185 }
186
187 return Some((decl, self.priority));
188 }
189
190 let next_node = self.current_rule_node.take()?.parent()?;
191 self.current_rule_node = Some(next_node);
192 self.update_for_node(next_node);
193 }
194 }
195}
196
197fn cascade_rules<E>(
198 stylist: &Stylist,
199 pseudo: Option<&PseudoElement>,
200 rule_node: &StrongRuleNode,
201 guards: &StylesheetGuards,
202 parent_style: Option<&ComputedValues>,
203 layout_parent_style: Option<&ComputedValues>,
204 first_line_reparenting: FirstLineReparenting,
205 try_tactic: &PositionTryFallbacksTryTactic,
206 cascade_mode: CascadeMode,
207 cascade_input_flags: ComputedValueFlags,
208 included_cascade_flags: RuleCascadeFlags,
209 rule_cache: Option<&RuleCache>,
210 rule_cache_conditions: &mut RuleCacheConditions,
211 element: Option<E>,
212 tree_counting_caches: &mut TreeCountingCaches,
213) -> Arc<ComputedValues>
214where
215 E: TElement,
216{
217 apply_declarations(
218 stylist,
219 pseudo,
220 rule_node,
221 guards,
222 DeclarationIterator::new(rule_node, guards, pseudo),
223 parent_style,
224 layout_parent_style,
225 first_line_reparenting,
226 try_tactic,
227 cascade_mode,
228 cascade_input_flags,
229 included_cascade_flags,
230 rule_cache,
231 rule_cache_conditions,
232 element,
233 tree_counting_caches,
234 )
235}
236
237#[derive(Clone, Copy)]
239pub enum CascadeMode<'a, 'b> {
240 Unvisited {
242 visited_rules: Option<&'a StrongRuleNode>,
244 },
245 Visited {
247 unvisited_context: &'a computed::Context<'b>,
249 },
250}
251
252fn iter_declarations<'c, 'decls: 'c>(
253 iter: impl Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
254 declarations: &mut Declarations<'decls>,
255 mut custom: Option<(&mut Cascade<'c>, &mut computed::Context)>,
256 attribute_tracker: &mut AttributeTracker,
257) {
258 for (declaration, priority) in iter {
259 if let PropertyDeclaration::Custom(ref declaration) = *declaration {
260 if let Some((ref mut cascade, ref mut context)) = custom {
261 cascade.cascade_custom_property(context, declaration, priority);
262 }
263 } else {
264 let id = declaration.id().as_longhand().unwrap();
265 declarations.note_declaration(declaration, priority, id);
266 if Cascade::might_have_non_custom_or_attr_dependency(id, declaration) {
267 if let Some((ref mut cascade, ref mut context)) = custom {
268 cascade.maybe_note_non_custom_dependency(
269 context,
270 id,
271 declaration,
272 attribute_tracker,
273 );
274 }
275 }
276 }
277 }
278}
279
280pub fn apply_declarations<'decls, E, I>(
283 stylist: &Stylist,
284 pseudo: Option<&PseudoElement>,
285 rules: &StrongRuleNode,
286 guards: &StylesheetGuards,
287 iter: I,
288 parent_style: Option<&ComputedValues>,
289 layout_parent_style: Option<&ComputedValues>,
290 first_line_reparenting: FirstLineReparenting<'_>,
291 try_tactic: &PositionTryFallbacksTryTactic,
292 cascade_mode: CascadeMode,
293 cascade_input_flags: ComputedValueFlags,
294 included_cascade_flags: RuleCascadeFlags,
295 rule_cache: Option<&RuleCache>,
296 rule_cache_conditions: &mut RuleCacheConditions,
297 element: Option<E>,
298 tree_counting_caches: &mut TreeCountingCaches,
299) -> Arc<ComputedValues>
300where
301 E: TElement,
302 I: Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
303{
304 debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
305 let device = stylist.device();
306 let inherited_style = parent_style.unwrap_or(device.default_computed_values());
307 let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
308 let container_size_query =
309 ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());
310
311 let originating_element = element.map(|e| e.ultimate_originating_element());
312 let element_context = match originating_element {
313 Some(ref e) => e as &dyn ElementContext,
314 None => &DummyElementContext {},
315 };
316
317 let mut context = computed::Context::new(
318 StyleBuilder::new(
322 device,
323 Some(stylist),
324 parent_style,
325 pseudo,
326 Some(rules.clone()),
327 is_root_element,
328 ),
329 stylist.quirks_mode(),
330 rule_cache_conditions,
331 container_size_query,
332 included_cascade_flags,
333 element_context,
334 tree_counting_caches,
335 );
336
337 context.style().add_flags(cascade_input_flags);
338 context
341 .style()
342 .add_flags(stylist.get_custom_property_initial_values_flags());
343
344 let using_cached_reset_properties;
345 let ignore_colors = context.builder.device.forced_colors().is_active();
346 let mut cascade = Cascade::new(first_line_reparenting, stylist, ignore_colors);
347 let mut declarations = Default::default();
348 let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
349 let mut attribute_tracker = AttributeTracker::new(element_context);
350
351 let properties_to_apply = match cascade_mode {
352 CascadeMode::Visited { unvisited_context } => {
353 context.builder.substitution_functions =
354 unvisited_context.builder.substitution_functions.clone();
355 context.builder.writing_mode = unvisited_context.builder.writing_mode;
356 context.builder.color_scheme = unvisited_context.builder.color_scheme;
357 using_cached_reset_properties = false;
361 iter_declarations(iter, &mut declarations, None, &mut attribute_tracker);
365
366 LonghandIdSet::visited_dependent()
367 },
368 CascadeMode::Unvisited { visited_rules } => {
369 cascade.init_custom_properties(&mut context);
370 iter_declarations(
371 iter,
372 &mut declarations,
373 Some((&mut cascade, &mut context)),
374 &mut attribute_tracker,
375 );
376 cascade.apply_custom_and_prioritary_properties(
381 &mut context,
382 &declarations,
383 &mut shorthand_cache,
384 &mut attribute_tracker,
385 );
386
387 if let Some(visited_rules) = visited_rules {
388 cascade.compute_visited_style_if_needed(
389 &mut context,
390 element,
391 parent_style,
392 layout_parent_style,
393 try_tactic,
394 visited_rules,
395 guards,
396 );
397 }
398
399 using_cached_reset_properties =
400 cascade.try_to_use_cached_reset_properties(&mut context, rule_cache, guards);
401
402 if using_cached_reset_properties {
403 LonghandIdSet::late_group_only_inherited()
404 } else {
405 LonghandIdSet::late_group()
406 }
407 },
408 };
409
410 cascade.apply_non_prioritary_properties(
411 &mut context,
412 &declarations.longhand_declarations,
413 &mut shorthand_cache,
414 &properties_to_apply,
415 &mut attribute_tracker,
416 );
417
418 context.builder.attribute_references = attribute_tracker.finalize();
419
420 cascade.finished_applying_properties(&mut context.builder);
421
422 context.builder.clear_modified_reset();
423
424 if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
425 StyleAdjuster::new(&mut context.builder).adjust(
426 layout_parent_style.unwrap_or(inherited_style),
427 element,
428 try_tactic,
429 &cascade.author_specified,
430 );
431 }
432
433 if context.builder.modified_reset() || using_cached_reset_properties {
434 context.rule_cache_conditions.borrow_mut().set_uncacheable();
440 }
441
442 if context
443 .builder
444 .flags()
445 .intersects(ComputedValueFlags::tree_counting_function_flags())
446 {
447 if let Some(el) = element {
448 el.apply_selector_flags(ElementSelectorFlags::MAY_HAVE_TREE_COUNTING_FUNCTION);
449 } else {
450 debug_assert!(
451 false,
452 "Tree counting function flag applied without an element?"
453 );
454 }
455 }
456
457 context.builder.build()
458}
459
460type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
466
467fn is_base_appearance(context: &computed::Context) -> bool {
468 use computed::Appearance;
469 let box_style = context.builder.get_box();
470 match box_style.clone_appearance() {
471 Appearance::BaseSelect => {
472 matches!(
473 box_style.clone__moz_default_appearance(),
474 Appearance::Listbox | Appearance::Menulist
475 )
476 },
477 Appearance::Base => box_style.clone__moz_default_appearance() != Appearance::None,
478 _ => false,
479 }
480}
481
482fn tweak_when_ignoring_colors(
483 context: &computed::Context,
484 longhand_id: LonghandId,
485 origin: CascadeOrigin,
486 declaration: &mut Cow<PropertyDeclaration>,
487 declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
488) {
489 use crate::values::computed::ToComputedValue;
490 use crate::values::specified::Color;
491
492 if !longhand_id.ignored_when_document_colors_disabled() {
493 return;
494 }
495
496 let is_ua_or_user_rule = matches!(origin, CascadeOrigin::User | CascadeOrigin::UA);
497 if is_ua_or_user_rule {
498 return;
499 }
500
501 let forced = context
503 .builder
504 .get_inherited_text()
505 .clone_forced_color_adjust();
506 if forced == computed::ForcedColorAdjust::None {
507 return;
508 }
509
510 fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
511 color
513 .to_computed_value(context)
514 .resolve_to_absolute(&AbsoluteColor::BLACK)
515 .alpha
516 }
517
518 match **declaration {
520 PropertyDeclaration::CSSWideKeyword(..) => return,
522 PropertyDeclaration::BackgroundColor(ref color) => {
523 if color.honored_in_forced_colors_mode(context, true) {
533 return;
534 }
535 let alpha = alpha_channel(color, context);
539 if alpha == 0.0 {
540 return;
541 }
542 let mut color = context.builder.device.default_background_color();
543 color.alpha = alpha;
544 declarations_to_apply_unless_overridden
545 .push(PropertyDeclaration::BackgroundColor(color.into()))
546 },
547 PropertyDeclaration::Color(ref color) => {
548 if color
550 .0
551 .honored_in_forced_colors_mode(context, true)
552 {
553 return;
554 }
555 if context
559 .builder
560 .get_parent_inherited_text()
561 .clone_color()
562 .alpha
563 == 0.0
564 {
565 let color = context.builder.device.default_color();
566 declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
567 specified::ColorPropertyValue(color.into()),
568 ))
569 }
570 },
571 #[cfg(feature = "gecko")]
573 PropertyDeclaration::BackgroundImage(ref bkg) => {
574 use crate::values::generics::image::Image;
575 if static_prefs::pref!("browser.display.permit_backplate") {
576 if bkg
577 .0
578 .iter()
579 .all(|image| matches!(*image, Image::Url(..) | Image::None))
580 {
581 return;
582 }
583 }
584 },
585 _ => {
586 if let Some(color) = declaration.color_value() {
599 if color
600 .honored_in_forced_colors_mode(context, false)
601 {
602 return;
603 }
604 }
605 },
606 }
607
608 *declaration.to_mut() =
609 PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
610}
611
612type DeclarationIndex = u16;
614
615#[derive(Copy, Clone)]
620struct PrioritaryDeclarationPosition {
621 most_important: DeclarationIndex,
623 least_important: DeclarationIndex,
624}
625
626impl Default for PrioritaryDeclarationPosition {
627 fn default() -> Self {
628 Self {
629 most_important: DeclarationIndex::MAX,
630 least_important: DeclarationIndex::MAX,
631 }
632 }
633}
634
635#[derive(Copy, Clone)]
636struct Declaration<'a> {
637 decl: &'a PropertyDeclaration,
638 priority: CascadePriority,
639 next_index: DeclarationIndex,
640}
641
642#[derive(Default)]
644pub(crate) struct Declarations<'a> {
645 has_prioritary_properties: bool,
647 longhand_declarations: SmallVec<[Declaration<'a>; 64]>,
649 prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
651}
652
653impl<'a> Declarations<'a> {
654 fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
655 let new_index = self.longhand_declarations.len();
656 if new_index >= DeclarationIndex::MAX as usize {
657 return;
660 }
661
662 self.has_prioritary_properties = true;
663 let new_index = new_index as DeclarationIndex;
664 let position = &mut self.prioritary_positions[id as usize];
665 if position.most_important == DeclarationIndex::MAX {
666 position.most_important = new_index;
669 } else {
670 self.longhand_declarations[position.least_important as usize].next_index = new_index;
672 }
673 position.least_important = new_index;
674 }
675
676 fn note_declaration(
677 &mut self,
678 decl: &'a PropertyDeclaration,
679 priority: CascadePriority,
680 id: LonghandId,
681 ) {
682 if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
683 self.note_prioritary_property(id);
684 }
685 self.longhand_declarations.push(Declaration {
686 decl,
687 priority,
688 next_index: 0,
689 });
690 }
691}
692
693#[derive(Default)]
694struct RevertedSet {
695 longhands_set: LonghandIdSet,
697 longhands: FxHashMap<LonghandId, (CascadePriority, RevertKind)>,
698 custom: PrecomputedHashMap<Name, (CascadePriority, RevertKind)>,
699}
700
701#[derive(Default)]
702struct SeenSubstitutionFunctions<'a> {
703 var: PrecomputedHashMap<&'a Name, bool>,
706 attr: PrecomputedHashSet<&'a Name>,
707}
708
709#[derive(Default)]
710struct SeenSet<'a> {
711 longhands: LonghandIdSet,
712 custom: SeenSubstitutionFunctions<'a>,
713}
714
715fn find_non_custom_references(
717 registration: &PropertyDescriptors,
718 value: &VariableValue,
719 is_root_element: bool,
720) -> ReferenceFlags {
721 use crate::properties_and_values::syntax::data_type::DependentDataTypes;
722
723 let mut result = ReferenceFlags::empty();
724 let Some(syntax) = registration.syntax.as_ref() else {
725 return result;
726 };
727 let dependent_types = syntax.dependent_types();
728 let may_reference_length = dependent_types.intersects(DependentDataTypes::LENGTH);
729 if may_reference_length {
730 result |= value.references.non_custom_references(is_root_element);
731 }
732 if dependent_types.intersects(DependentDataTypes::COLOR) {
733 result |= ReferenceFlags::COLOR_SCHEME;
736 }
737 result
738}
739
740pub struct KeyframeCustomPropertiesBuilder<'a> {
749 cascade: Cascade<'a>,
750 decls: Declarations<'a>,
751 shorthand_cache: ShorthandsWithPropertyReferencesCache,
752}
753
754impl<'a> KeyframeCustomPropertiesBuilder<'a> {
755 pub fn new(
758 stylist: &'a Stylist,
759 context: &mut computed::Context,
760 base: ComputedCustomProperties,
761 ) -> Self {
762 context.builder.substitution_functions =
763 ComputedSubstitutionFunctions::new(Some(base), None);
764 Self {
765 cascade: Cascade::new_for_custom_properties_only(stylist),
766 decls: Declarations::default(),
767 shorthand_cache: ShorthandsWithPropertyReferencesCache::default(),
768 }
769 }
770
771 pub fn cascade(
773 &mut self,
774 context: &mut computed::Context,
775 declaration: &'a CustomDeclaration,
776 priority: CascadePriority,
777 ) {
778 self.cascade
779 .cascade_custom_property(context, declaration, priority);
780 }
781
782 pub fn build(
784 mut self,
785 context: &mut computed::Context,
786 attribute_tracker: &mut AttributeTracker,
787 ) {
788 self.cascade.apply_custom_and_prioritary_properties(
789 context,
790 &self.decls,
791 &mut self.shorthand_cache,
792 attribute_tracker,
793 );
794 }
795}
796
797pub(crate) struct Cascade<'a> {
798 first_line_reparenting: FirstLineReparenting<'a>,
799 stylist: &'a Stylist,
800 ignore_colors: bool,
801 seen: SeenSet<'a>,
802 reverted: RevertedSet,
803 author_specified: LonghandIdSet,
804 declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
805 may_have_custom_property_cycles: bool,
806 references_from_non_custom_properties: NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
807 ensured_prioritary: PrioritaryPropertyIdSet,
809}
810
811impl<'a> Cascade<'a> {
812 fn new(
813 first_line_reparenting: FirstLineReparenting<'a>,
814 stylist: &'a Stylist,
815 ignore_colors: bool,
816 ) -> Self {
817 Self {
818 first_line_reparenting,
819 stylist,
820 ignore_colors,
821 seen: Default::default(),
822 author_specified: Default::default(),
823 reverted: Default::default(),
824 declarations_to_apply_unless_overridden: Default::default(),
825 may_have_custom_property_cycles: false,
826 ensured_prioritary: PrioritaryPropertyIdSet::default(),
827 references_from_non_custom_properties: Default::default(),
828 }
829 }
830
831 fn new_for_custom_properties_only(stylist: &'a Stylist) -> Self {
834 Self {
835 first_line_reparenting: FirstLineReparenting::No,
836 stylist,
837 ignore_colors: false,
838 seen: Default::default(),
839 author_specified: Default::default(),
840 reverted: Default::default(),
841 declarations_to_apply_unless_overridden: Default::default(),
842 may_have_custom_property_cycles: false,
843 ensured_prioritary: PrioritaryPropertyIdSet::default(),
844 references_from_non_custom_properties: Default::default(),
845 }
846 }
847
848 fn substitute_variables_if_needed<'cache, 'decl>(
849 &self,
850 context: &mut computed::Context,
851 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
852 declaration: &'decl PropertyDeclaration,
853 attribute_tracker: &mut AttributeTracker,
854 ) -> Cow<'decl, PropertyDeclaration>
855 where
856 'cache: 'decl,
857 {
858 let declaration = match *declaration {
859 PropertyDeclaration::WithVariables(ref declaration) => declaration,
860 ref d => return Cow::Borrowed(d),
861 };
862
863 if !declaration.id.inherited() {
864 context.rule_cache_conditions.borrow_mut().set_uncacheable();
865
866 if matches!(declaration.id, LonghandId::Display | LonghandId::Content) {
872 context
873 .builder
874 .add_flags(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE);
875 }
876 }
877
878 debug_assert!(
879 context.builder.stylist.is_some(),
880 "Need a Stylist to substitute variables!"
881 );
882 declaration.value.substitute_variables(
883 declaration.id,
884 &context.builder.substitution_functions(),
885 context.builder.stylist.unwrap(),
886 context,
887 shorthand_cache,
888 attribute_tracker,
889 )
890 }
891
892 fn apply_one_prioritary_property(
893 &mut self,
894 context: &mut computed::Context,
895 decls: &Declarations,
896 cache: &mut ShorthandsWithPropertyReferencesCache,
897 id: PrioritaryPropertyId,
898 attr_provider: &mut AttributeTracker,
899 ) {
900 let mut index = decls.prioritary_positions[id as usize].most_important;
901 if index == DeclarationIndex::MAX {
902 return;
903 }
904
905 let longhand_id = id.to_longhand();
906 debug_assert!(
907 !longhand_id.is_logical(),
908 "That could require more book-keeping"
909 );
910 loop {
911 let decl = decls.longhand_declarations[index as usize];
912 self.apply_one_longhand(
913 context,
914 longhand_id,
915 decl.decl,
916 decl.priority,
917 cache,
918 attr_provider,
919 );
920 if self.seen.longhands.contains(longhand_id) {
921 self.did_apply_prioritary_property(context, id);
923 return;
924 }
925 debug_assert!(
926 decl.next_index == 0 || decl.next_index > index,
927 "should make progress! {} -> {}",
928 index,
929 decl.next_index,
930 );
931 index = decl.next_index;
932 if index == 0 {
933 return;
934 }
935 }
936 }
937
938 fn did_apply_prioritary_property(
940 &mut self,
941 context: &mut computed::Context,
942 id: PrioritaryPropertyId,
943 ) {
944 use crate::properties::PrioritaryPropertyId::*;
945 match id {
946 Appearance => {
947 if is_base_appearance(context) {
948 context
949 .style()
950 .add_flags(ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE);
951 context
952 .included_cascade_flags
953 .insert(RuleCascadeFlags::APPEARANCE_BASE);
954 }
955 },
956 WritingMode | Direction | TextOrientation => {
957 context.builder.writing_mode =
958 crate::logical_geometry::WritingMode::new(context.builder.get_inherited_box());
959 },
960 Zoom => {
961 context.builder.recompute_effective_zooms();
962 if !context.builder.effective_zoom_for_inheritance.is_one() {
963 self.recompute_font_size_for_zoom_change(&mut context.builder);
969 }
970 },
971 XLang => {
972 #[cfg(feature = "gecko")]
973 self.recompute_initial_font_family_if_needed(&mut context.builder);
974 self.recompute_keyword_font_size_if_needed(context);
975 },
976 FontFamily => {
977 #[cfg(feature = "gecko")]
978 self.prioritize_user_fonts_if_needed(&mut context.builder);
979 self.recompute_keyword_font_size_if_needed(context);
980 },
981 FontSize => {
982 if self.seen.longhands.contains(LonghandId::MathDepth) {
983 #[cfg(feature = "gecko")]
984 Self::recompute_math_font_size_if_needed(context);
985 }
986 if self.seen.longhands.contains(LonghandId::XLang)
987 || self.seen.longhands.contains(LonghandId::FontFamily)
988 {
989 self.recompute_keyword_font_size_if_needed(context);
990 }
991 #[cfg(feature = "gecko")]
992 self.constrain_font_size_if_needed(&mut context.builder);
993 },
994 XTextScale => {
995 #[cfg(feature = "gecko")]
996 self.unzoom_fonts_if_needed(&mut context.builder);
997 },
998 MozMinFontSizeRatio => {
999 #[cfg(feature = "gecko")]
1000 self.constrain_font_size_if_needed(&mut context.builder);
1001 },
1002 ColorScheme => {
1003 context.builder.color_scheme =
1004 context.builder.get_inherited_ui().color_scheme_bits();
1005 },
1006 MozDefaultAppearance | MathDepth | FontWeight | FontStretch | FontStyle
1007 | FontSizeAdjust | ForcedColorAdjust | LineHeight => {},
1008 }
1009 }
1010
1011 fn apply_non_prioritary_properties(
1012 &mut self,
1013 context: &mut computed::Context,
1014 longhand_declarations: &[Declaration],
1015 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
1016 properties_to_apply: &LonghandIdSet,
1017 attribute_tracker: &mut AttributeTracker,
1018 ) {
1019 debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
1020 debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
1021 for declaration in &*longhand_declarations {
1022 let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
1023 if !properties_to_apply.contains(longhand_id) {
1024 continue;
1025 }
1026 debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
1027 let is_logical = longhand_id.is_logical();
1028 if is_logical {
1029 let wm = context.builder.writing_mode;
1030 context
1031 .rule_cache_conditions
1032 .borrow_mut()
1033 .set_writing_mode_dependency(wm);
1034 longhand_id = longhand_id.to_physical(wm);
1035 }
1036 self.apply_one_longhand(
1037 context,
1038 longhand_id,
1039 declaration.decl,
1040 declaration.priority,
1041 shorthand_cache,
1042 attribute_tracker,
1043 );
1044 }
1045 if !self.declarations_to_apply_unless_overridden.is_empty() {
1046 debug_assert!(self.ignore_colors);
1047 for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
1048 let longhand_id = declaration.id().as_longhand().unwrap();
1049 debug_assert!(!longhand_id.is_logical());
1050 if !self.seen.longhands.contains(longhand_id) {
1051 unsafe {
1052 self.do_apply_declaration(context, longhand_id, &declaration);
1053 }
1054 }
1055 }
1056 }
1057
1058 if !context.builder.effective_zoom_for_inheritance.is_one() {
1059 self.recompute_zoom_dependent_inherited_lengths(context);
1060 }
1061 }
1062
1063 #[cold]
1064 fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {
1065 debug_assert!(self.seen.longhands.contains(LonghandId::Zoom));
1066 for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {
1067 if self.seen.longhands.contains(prop) {
1068 continue;
1069 }
1070 let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);
1071 unsafe {
1072 self.do_apply_declaration(context, prop, &declaration);
1073 }
1074 }
1075 }
1076
1077 fn apply_one_longhand(
1078 &mut self,
1079 context: &mut computed::Context,
1080 longhand_id: LonghandId,
1081 declaration: &PropertyDeclaration,
1082 priority: CascadePriority,
1083 cache: &mut ShorthandsWithPropertyReferencesCache,
1084 attribute_tracker: &mut AttributeTracker,
1085 ) {
1086 debug_assert!(!longhand_id.is_logical());
1087 if self.seen.longhands.contains(longhand_id) {
1088 return;
1089 }
1090
1091 if !(priority.flags() - context.included_cascade_flags).is_empty() {
1092 return;
1093 }
1094
1095 if self.reverted.longhands_set.contains(longhand_id) {
1096 if let Some(&(reverted_priority, revert_kind)) =
1097 self.reverted.longhands.get(&longhand_id)
1098 {
1099 if !reverted_priority.allows_when_reverted(&priority, revert_kind) {
1100 return;
1101 }
1102 }
1103 }
1104
1105 let mut declaration =
1106 self.substitute_variables_if_needed(context, cache, declaration, attribute_tracker);
1107
1108 let origin = priority.cascade_level().origin();
1111 if self.ignore_colors {
1112 tweak_when_ignoring_colors(
1113 context,
1114 longhand_id,
1115 origin,
1116 &mut declaration,
1117 &mut self.declarations_to_apply_unless_overridden,
1118 );
1119 }
1120 let can_skip_apply = match declaration.get_css_wide_keyword() {
1121 Some(keyword) => {
1122 if let Some(revert_kind) = keyword.revert_kind() {
1123 self.reverted.longhands_set.insert(longhand_id);
1127 self.reverted
1128 .longhands
1129 .insert(longhand_id, (priority, revert_kind));
1130 return;
1131 }
1132
1133 let inherited = longhand_id.inherited();
1134 let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()
1135 && longhand_id.zoom_dependent();
1136 match keyword {
1137 CSSWideKeyword::Revert
1138 | CSSWideKeyword::RevertLayer
1139 | CSSWideKeyword::RevertRule => unreachable!(),
1140 CSSWideKeyword::Unset => !zoomed || !inherited,
1141 CSSWideKeyword::Inherit => inherited && !zoomed,
1142 CSSWideKeyword::Initial => !inherited,
1143 }
1144 },
1145 None => false,
1146 };
1147
1148 self.seen.longhands.insert(longhand_id);
1149 if origin.is_author_origin() {
1150 self.author_specified.insert(longhand_id);
1151 }
1152
1153 if !can_skip_apply {
1154 let old_scope = context.scope;
1158 let cascade_level = priority.cascade_level();
1159 context.scope = cascade_level;
1160 unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
1161 context.scope = old_scope;
1162 }
1163 }
1164
1165 #[inline]
1166 unsafe fn do_apply_declaration(
1167 &self,
1168 context: &mut computed::Context,
1169 longhand_id: LonghandId,
1170 declaration: &PropertyDeclaration,
1171 ) {
1172 debug_assert!(!longhand_id.is_logical());
1173 (CASCADE_PROPERTY[longhand_id as usize])(&declaration, context);
1179 }
1180
1181 fn compute_visited_style_if_needed<E>(
1182 &self,
1183 context: &mut computed::Context,
1184 element: Option<E>,
1185 parent_style: Option<&ComputedValues>,
1186 layout_parent_style: Option<&ComputedValues>,
1187 try_tactic: &PositionTryFallbacksTryTactic,
1188 visited_rules: &StrongRuleNode,
1189 guards: &StylesheetGuards,
1190 ) where
1191 E: TElement,
1192 {
1193 let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
1194
1195 macro_rules! visited_parent {
1196 ($parent:expr) => {
1197 if is_link {
1198 $parent
1199 } else {
1200 $parent.map(|p| p.visited_style().unwrap_or(p))
1201 }
1202 };
1203 }
1204
1205 let style = cascade_rules(
1208 context.builder.stylist.unwrap(),
1209 context.builder.pseudo,
1210 visited_rules,
1211 guards,
1212 visited_parent!(parent_style),
1213 visited_parent!(layout_parent_style),
1214 self.first_line_reparenting,
1215 try_tactic,
1216 CascadeMode::Visited {
1217 unvisited_context: &*context,
1218 },
1219 Default::default(),
1222 context.included_cascade_flags,
1223 None, &mut *context.rule_cache_conditions.borrow_mut(),
1229 element,
1230 &mut *context.tree_counting_caches.borrow_mut(),
1231 );
1232 context.builder.visited_style = Some(style);
1233 }
1234
1235 fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
1236 #[cfg(feature = "gecko")]
1237 {
1238 if let Some(bg) = builder.get_background_if_mutated() {
1239 bg.fill_arrays();
1240 }
1241
1242 if let Some(svg) = builder.get_svg_if_mutated() {
1243 svg.fill_arrays();
1244 }
1245 }
1246
1247 if self
1248 .author_specified
1249 .contains_any(LonghandIdSet::border_background_properties())
1250 {
1251 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
1252 }
1253
1254 if self.author_specified.contains(LonghandId::Color) {
1255 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);
1256 }
1257
1258 if self.author_specified.contains(LonghandId::TextShadow) {
1259 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_SHADOW);
1260 }
1261
1262 if self.author_specified.contains(LonghandId::GridAutoFlow) {
1263 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW);
1264 }
1265 #[cfg(feature = "servo")]
1266 {
1267 if let Some(font) = builder.get_font_if_mutated() {
1268 font.compute_font_hash();
1269 }
1270 }
1271 }
1272
1273 fn try_to_use_cached_reset_properties(
1274 &self,
1275 context: &mut computed::Context<'a>,
1276 cache: Option<&'a RuleCache>,
1277 guards: &StylesheetGuards,
1278 ) -> bool {
1279 let style = match self.first_line_reparenting {
1280 FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
1281 FirstLineReparenting::No => {
1282 let Some(cache) = cache else { return false };
1283 let Some(style) = cache.find(guards, &context) else {
1284 return false;
1285 };
1286 style
1287 },
1288 };
1289
1290 context.builder.copy_reset_from(style);
1291
1292 let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND
1304 | ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW
1305 | ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS
1306 | ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS
1307 | ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE
1308 | ComputedValueFlags::USES_CONTAINER_UNITS
1309 | ComputedValueFlags::USES_VIEWPORT_UNITS
1310 | ComputedValueFlags::USES_FONT_RELATIVE_UNITS
1311 | ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY
1312 | ComputedValueFlags::USES_SIBLING_COUNT
1313 | ComputedValueFlags::USES_SIBLING_INDEX;
1314 context.builder.add_flags(style.flags & bits_to_copy);
1315
1316 true
1317 }
1318
1319 #[inline]
1322 #[cfg(feature = "gecko")]
1323 fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
1324 use crate::gecko_bindings::bindings;
1325 use crate::values::computed::font::FontFamily;
1326
1327 let default_font_type = {
1328 let font = builder.get_font();
1329
1330 if !font.mFont.family.is_initial {
1331 return;
1332 }
1333
1334 let default_font_type = unsafe {
1335 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1336 builder.device.document(),
1337 font.mLanguage.mRawPtr,
1338 )
1339 };
1340
1341 let initial_generic = font.mFont.family.families.single_generic();
1342 debug_assert!(
1343 initial_generic.is_some(),
1344 "Initial font should be just one generic font"
1345 );
1346 if initial_generic == Some(default_font_type) {
1347 return;
1348 }
1349
1350 default_font_type
1351 };
1352
1353 builder.mutate_font().mFont.family.families =
1355 FontFamily::generic(default_font_type).families.clone();
1356 }
1357
1358 #[inline]
1360 #[cfg(feature = "gecko")]
1361 fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1362 use crate::gecko_bindings::bindings;
1363
1364 if static_prefs::pref!("browser.display.use_document_fonts") != 0
1367 || builder.device.chrome_rules_enabled_for_document()
1368 {
1369 return;
1370 }
1371
1372 let default_font_type = {
1373 let font = builder.get_font();
1374
1375 if font.mFont.family.is_system_font {
1376 return;
1377 }
1378
1379 if !font.mFont.family.families.needs_user_font_prioritization() {
1380 return;
1381 }
1382
1383 unsafe {
1384 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1385 builder.device.document(),
1386 font.mLanguage.mRawPtr,
1387 )
1388 }
1389 };
1390
1391 let font = builder.mutate_font();
1392 font.mFont
1393 .family
1394 .families
1395 .prioritize_first_generic_or_prepend(default_font_type);
1396 }
1397
1398 fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
1400 use crate::values::computed::ToComputedValue;
1401
1402 if !self.seen.longhands.contains(LonghandId::XLang)
1403 && !self.seen.longhands.contains(LonghandId::FontFamily)
1404 {
1405 return;
1406 }
1407
1408 let new_size = {
1409 let font = context.builder.get_font();
1410 let info = font.clone_font_size().keyword_info;
1411 let new_size = match info.kw {
1412 specified::FontSizeKeyword::None => return,
1413 _ => {
1414 context.for_non_inherited_property = false;
1415 specified::FontSize::Keyword(info).to_computed_value(context)
1416 },
1417 };
1418
1419 #[cfg(feature = "gecko")]
1420 if font.mScriptUnconstrainedSize == new_size.computed_size {
1421 return;
1422 }
1423
1424 new_size
1425 };
1426
1427 context.builder.mutate_font().set_font_size(new_size);
1428 }
1429
1430 #[cfg(feature = "gecko")]
1433 fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
1434 use crate::gecko_bindings::bindings;
1435 use crate::values::generics::NonNegative;
1436
1437 let min_font_size = {
1438 let font = builder.get_font();
1439 let min_font_size = unsafe {
1440 bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
1441 };
1442
1443 if font.mFont.size.0 >= min_font_size {
1444 return;
1445 }
1446
1447 NonNegative(min_font_size)
1448 };
1449
1450 builder.mutate_font().mFont.size = min_font_size;
1451 }
1452
1453 #[cfg(feature = "gecko")]
1457 fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1458 debug_assert!(self.seen.longhands.contains(LonghandId::XTextScale));
1459
1460 let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
1461 let text_scale = builder.get_font().clone__x_text_scale();
1462 if parent_text_scale == text_scale {
1463 return;
1464 }
1465 debug_assert_ne!(
1466 parent_text_scale.text_zoom_enabled(),
1467 text_scale.text_zoom_enabled(),
1468 "There's only one value that disables it"
1469 );
1470 debug_assert!(
1471 !text_scale.text_zoom_enabled(),
1472 "We only ever disable text zoom never enable it"
1473 );
1474 let device = builder.device;
1475 builder.mutate_font().unzoom_fonts(device);
1476 }
1477
1478 fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
1479 debug_assert!(self.seen.longhands.contains(LonghandId::Zoom));
1480 let old_size = builder.get_font().clone_font_size();
1483 let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);
1484 if old_size == new_size {
1485 return;
1486 }
1487 builder.mutate_font().set_font_size(new_size);
1488 }
1489
1490 #[cfg(feature = "gecko")]
1495 fn recompute_math_font_size_if_needed(context: &mut computed::Context) {
1496 use crate::values::generics::NonNegative;
1497
1498 if context.builder.get_font().clone_font_size().keyword_info.kw
1500 != specified::FontSizeKeyword::Math
1501 {
1502 return;
1503 }
1504
1505 const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
1506
1507 fn scale_factor_for_math_depth_change(
1517 parent_math_depth: i32,
1518 computed_math_depth: i32,
1519 parent_script_percent_scale_down: Option<f32>,
1520 parent_script_script_percent_scale_down: Option<f32>,
1521 ) -> f32 {
1522 let mut a = parent_math_depth;
1523 let mut b = computed_math_depth;
1524 let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
1525 let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c);
1526 let scale_between_0_and_2 =
1527 parent_script_script_percent_scale_down.unwrap_or_else(|| c * c);
1528 let mut s = 1.0;
1529 let mut invert_scale_factor = false;
1530 if a == b {
1531 return s;
1532 }
1533 if b < a {
1534 std::mem::swap(&mut a, &mut b);
1535 invert_scale_factor = true;
1536 }
1537 let mut e = b - a;
1538 if a <= 0 && b >= 2 {
1539 s *= scale_between_0_and_2;
1540 e -= 2;
1541 } else if a == 1 {
1542 s *= scale_between_0_and_2 / scale_between_0_and_1;
1543 e -= 1;
1544 } else if b == 1 {
1545 s *= scale_between_0_and_1;
1546 e -= 1;
1547 }
1548 s *= (c as f32).powi(e);
1549 if invert_scale_factor {
1550 1.0 / s.max(f32::MIN_POSITIVE)
1551 } else {
1552 s
1553 }
1554 }
1555
1556 let (new_size, new_unconstrained_size) = {
1557 use crate::values::specified::font::QueryFontMetricsFlags;
1558
1559 let builder = &context.builder;
1560 let font = builder.get_font();
1561 let parent_font = builder.get_parent_font();
1562
1563 let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1564
1565 if delta == 0 {
1566 return;
1567 }
1568
1569 let mut min = parent_font.mScriptMinSize;
1570 if font.mXTextScale.text_zoom_enabled() {
1571 min = builder.device.zoom_text(min);
1572 }
1573
1574 let scale = {
1576 let font_metrics = context.query_font_metrics(
1578 FontBaseSize::InheritedStyle,
1579 FontMetricsOrientation::Horizontal,
1580 QueryFontMetricsFlags::NEEDS_MATH_SCALES,
1581 );
1582 scale_factor_for_math_depth_change(
1583 parent_font.mMathDepth as i32,
1584 font.mMathDepth as i32,
1585 font_metrics.script_percent_scale_down,
1586 font_metrics.script_script_percent_scale_down,
1587 )
1588 };
1589
1590 let parent_size = parent_font.mSize.0;
1591 let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1592 let new_size = parent_size.scale_by(scale);
1593 let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1594
1595 if scale <= 1. {
1596 if parent_size <= min {
1601 (parent_size, new_unconstrained_size)
1602 } else {
1603 (min.max(new_size), new_unconstrained_size)
1604 }
1605 } else {
1606 (
1612 new_size.min(new_unconstrained_size.max(min)),
1613 new_unconstrained_size,
1614 )
1615 }
1616 };
1617 let font = context.builder.mutate_font();
1618 font.mFont.size = NonNegative(new_size);
1619 font.mSize = NonNegative(new_size);
1620 font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1621 }
1622
1623 fn init_custom_properties(&mut self, context: &mut computed::Context) {
1626 let is_root_element = context.is_root_element();
1627 let initial_values = self.stylist.get_custom_property_initial_values();
1628 let inherited = if is_root_element {
1629 debug_assert!(context.inherited_custom_properties().is_empty());
1630 initial_values.inherited.clone()
1631 } else {
1632 context.inherited_custom_properties().inherited.clone()
1633 };
1634 let properties = ComputedCustomProperties {
1635 inherited,
1636 non_inherited: initial_values.non_inherited.clone(),
1637 };
1638 context.builder.substitution_functions =
1639 ComputedSubstitutionFunctions::new(Some(properties), None);
1640 }
1641
1642 fn apply_custom_and_prioritary_properties(
1648 &mut self,
1649 context: &mut computed::Context,
1650 decls: &Declarations,
1651 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
1652 attribute_tracker: &mut AttributeTracker,
1653 ) {
1654 if self.may_have_custom_property_cycles {
1655 let stylist = self.stylist;
1656 let seen = std::mem::take(&mut self.seen.custom);
1657 let references = std::mem::take(&mut self.references_from_non_custom_properties);
1658 substitute_all(
1659 &seen,
1660 &references,
1661 stylist,
1662 context,
1663 self,
1664 decls,
1665 shorthand_cache,
1666 attribute_tracker,
1667 );
1668 }
1669 if decls.has_prioritary_properties {
1671 for id in PrioritaryPropertyId::each() {
1672 self.ensure_prioritary_property(
1673 context,
1674 decls,
1675 shorthand_cache,
1676 attribute_tracker,
1677 id,
1678 );
1679 }
1680 }
1681 self.finish_cascade_custom_properties(context);
1682 }
1683
1684 fn ensure_prioritary_property(
1686 &mut self,
1687 context: &mut computed::Context,
1688 decls: &Declarations,
1689 cache: &mut ShorthandsWithPropertyReferencesCache,
1690 attribute_tracker: &mut AttributeTracker,
1691 id: PrioritaryPropertyId,
1692 ) {
1693 if self.ensured_prioritary.contains(id) {
1694 return;
1695 }
1696 self.ensured_prioritary.insert(id);
1697 let deps = id.dependencies();
1698 if !self.ensured_prioritary.contains_all(deps) {
1699 for dep in deps.iter() {
1700 self.ensure_prioritary_property(context, decls, cache, attribute_tracker, dep);
1701 }
1702 }
1703 self.apply_one_prioritary_property(context, decls, cache, id, attribute_tracker);
1704 }
1705
1706 fn cascade_custom_property(
1708 &mut self,
1709 context: &mut computed::Context,
1710 declaration: &'a CustomDeclaration,
1711 priority: CascadePriority,
1712 ) {
1713 let CustomDeclaration {
1714 ref name,
1715 ref value,
1716 } = *declaration;
1717
1718 if let Some(&(reverted_priority, revert_kind)) = self.reverted.custom.get(name) {
1719 if !reverted_priority.allows_when_reverted(&priority, revert_kind) {
1720 return;
1721 }
1722 }
1723
1724 if !(priority.flags() - context.included_cascade_flags).is_empty() {
1725 return;
1726 }
1727
1728 let entry = match self.seen.custom.var.entry(name) {
1729 Entry::Occupied(..) => return,
1730 Entry::Vacant(v) => v,
1731 };
1732
1733 let registration = self.stylist.get_custom_property_registration(&name);
1734 let initial_values = self.stylist.get_custom_property_initial_values();
1735 if !Self::value_may_affect_style(context, name, registration, initial_values, value) {
1736 entry.insert(false);
1737 return;
1738 }
1739
1740 let has_references = match value {
1741 CustomDeclarationValue::Unparsed(unparsed_value) => {
1742 unparsed_value
1745 .references
1746 .flags
1747 .intersects(ReferenceFlags::ATTR | ReferenceFlags::VAR)
1748 || !find_non_custom_references(
1749 registration,
1750 unparsed_value,
1751 context.is_root_element(),
1752 )
1753 .is_empty()
1754 },
1755 CustomDeclarationValue::Parsed(..) => false,
1759 CustomDeclarationValue::CSSWideKeyword(..) => false,
1763 };
1764 self.may_have_custom_property_cycles |= has_references;
1765 entry.insert(has_references);
1766
1767 match value {
1768 CustomDeclarationValue::Unparsed(unparsed_value) => {
1769 if !has_references {
1770 substitute_references_if_needed_and_apply(
1774 name,
1775 SubstitutionFunctionKind::Var,
1776 unparsed_value,
1777 self.stylist,
1778 context,
1779 &mut AttributeTracker::new_dummy(),
1781 );
1782 return;
1783 }
1784 let value = ComputedRegisteredValue::universal(Arc::clone(unparsed_value));
1785 context
1786 .builder
1787 .substitution_functions
1788 .insert_var(registration, name, value);
1789 },
1790 CustomDeclarationValue::Parsed(parsed_value) => {
1791 let value = parsed_value.to_computed_value(&context);
1792 context
1793 .builder
1794 .substitution_functions
1795 .insert_var(registration, name, value);
1796 },
1797 CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword.revert_kind() {
1798 Some(revert_kind) => {
1799 self.seen.custom.var.remove(name);
1800 self.reverted
1801 .custom
1802 .insert(name.clone(), (priority, revert_kind));
1803 },
1804 None => match keyword {
1805 CSSWideKeyword::Initial => {
1806 debug_assert!(registration.inherits(), "Should've been handled earlier");
1808 remove_and_insert_initial_value(
1809 name,
1810 registration,
1811 &mut context.builder.substitution_functions,
1812 );
1813 },
1814 CSSWideKeyword::Inherit => {
1815 debug_assert!(!registration.inherits(), "Should've been handled earlier");
1817 context
1818 .style()
1819 .add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
1820 let inherited_value = context
1821 .inherited_custom_properties()
1822 .non_inherited
1823 .get(name)
1824 .cloned();
1825 if let Some(inherited_value) = inherited_value {
1826 context.builder.substitution_functions.insert_var(
1827 registration,
1828 name,
1829 inherited_value,
1830 );
1831 }
1832 },
1833 CSSWideKeyword::Revert
1835 | CSSWideKeyword::RevertLayer
1836 | CSSWideKeyword::RevertRule
1837 | CSSWideKeyword::Unset => unreachable!(),
1838 },
1839 },
1840 }
1841 }
1842
1843 #[inline]
1845 pub fn might_have_non_custom_or_attr_dependency(
1846 id: LonghandId,
1847 decl: &PropertyDeclaration,
1848 ) -> bool {
1849 if let PropertyDeclaration::WithVariables(v) = decl {
1850 return matches!(id, LonghandId::LineHeight | LonghandId::FontSize)
1851 || v.value
1852 .variable_value
1853 .references
1854 .flags
1855 .intersects(ReferenceFlags::ATTR);
1856 }
1857 false
1858 }
1859
1860 pub fn maybe_note_non_custom_dependency(
1863 &mut self,
1864 context: &mut computed::Context,
1865 id: LonghandId,
1866 decl: &'a PropertyDeclaration,
1867 attribute_tracker: &mut AttributeTracker,
1868 ) {
1869 debug_assert!(Self::might_have_non_custom_or_attr_dependency(id, decl));
1870 let PropertyDeclaration::WithVariables(v) = decl else {
1871 return;
1872 };
1873 let value = &v.value.variable_value;
1874 let refs = &value.references;
1875
1876 if !refs
1877 .flags
1878 .intersects(ReferenceFlags::VAR | ReferenceFlags::ATTR)
1879 {
1880 return;
1881 }
1882
1883 if refs.flags.intersects(ReferenceFlags::ATTR) {
1886 self.update_attributes_map(context, value, attribute_tracker);
1887 if !refs.flags.intersects(ReferenceFlags::VAR) {
1888 return;
1889 }
1890 }
1891
1892 let references = match id {
1903 LonghandId::FontSize => ReferenceFlags::FONT_UNITS,
1904 LonghandId::LineHeight => ReferenceFlags::LH_UNITS | ReferenceFlags::FONT_UNITS,
1905 LonghandId::ColorScheme => ReferenceFlags::COLOR_SCHEME,
1906 _ => return,
1907 };
1908
1909 references.for_each_non_custom(context.is_root_element(), |idx| {
1910 self.references_from_non_custom_properties[idx]
1911 .get_or_insert_with(Vec::new)
1912 .push(v.value.clone());
1913 });
1914 }
1915
1916 fn value_may_affect_style(
1917 context: &computed::Context,
1918 name: &Name,
1919 registration: &PropertyDescriptors,
1920 initial_values: &ComputedCustomProperties,
1921 value: &CustomDeclarationValue,
1922 ) -> bool {
1923 match *value {
1924 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
1925 if registration.inherits() {
1929 return false;
1930 }
1931 },
1932 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial) => {
1933 if !registration.inherits() {
1936 return false;
1937 }
1938 },
1939 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) => {
1940 return false;
1944 },
1945 _ => {},
1946 }
1947
1948 let existing_value = context
1949 .builder
1950 .substitution_functions
1951 .get_var(registration, &name);
1952 let Some(existing_value) = existing_value else {
1953 if matches!(
1954 value,
1955 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial)
1956 ) {
1957 debug_assert!(registration.inherits(), "Should've been handled earlier");
1958 if registration.initial_value.is_none() {
1962 return false;
1963 }
1964 }
1965 return true;
1966 };
1967 match value {
1968 CustomDeclarationValue::Unparsed(value) => {
1969 if let Some(existing_value) = existing_value.as_universal() {
1972 return existing_value != value;
1973 }
1974 },
1975 CustomDeclarationValue::Parsed(..) => {
1976 },
1979 CustomDeclarationValue::CSSWideKeyword(kw) => {
1980 match kw {
1981 CSSWideKeyword::Inherit => {
1982 debug_assert!(!registration.inherits(), "Should've been handled earlier");
1983 if context
1987 .inherited_custom_properties()
1988 .non_inherited
1989 .get(name)
1990 .is_none()
1991 {
1992 return false;
1993 }
1994 },
1995 CSSWideKeyword::Initial => {
1996 debug_assert!(registration.inherits(), "Should've been handled earlier");
1997 if let Some(initial_value) = initial_values.get(registration, name) {
2000 return existing_value != initial_value;
2001 }
2002 },
2003 CSSWideKeyword::Unset => {
2004 debug_assert!(false, "Should've been handled earlier");
2005 },
2006 CSSWideKeyword::Revert
2007 | CSSWideKeyword::RevertLayer
2008 | CSSWideKeyword::RevertRule => {},
2009 }
2010 },
2011 };
2012
2013 true
2014 }
2015
2016 pub fn update_attributes_map(
2018 &mut self,
2019 context: &mut computed::Context,
2020 value: &'a VariableValue,
2021 attribute_tracker: &mut AttributeTracker,
2022 ) {
2023 let refs = &value.references;
2024 if !refs.flags.intersects(ReferenceFlags::ATTR) {
2025 return;
2026 }
2027 self.may_have_custom_property_cycles = true;
2028
2029 for next in &refs.refs {
2030 if !next.is_attr_with_type() || !self.seen.custom.attr.insert(&next.name) {
2031 continue;
2034 }
2035 if let Ok(v) = get_attr_value_for_cycle_resolution(
2036 &next.name,
2037 &next.attribute_data,
2038 &value.url_data,
2039 attribute_tracker,
2040 ) {
2041 context
2042 .builder
2043 .substitution_functions
2044 .insert_attr(&next.name, v);
2045 }
2046 }
2047 }
2048
2049 pub fn finish_cascade_custom_properties(&mut self, context: &mut computed::Context) {
2052 context
2053 .builder
2054 .substitution_functions
2055 .custom_properties
2056 .shrink_to_fit();
2057
2058 let initial_values = self.stylist.get_custom_property_initial_values();
2063 let reuse_inherited = context.inherited_custom_properties().inherited
2064 == context
2065 .builder
2066 .substitution_functions
2067 .custom_properties
2068 .inherited;
2069 if reuse_inherited {
2070 let inherited = context.inherited_custom_properties().inherited.clone();
2071 context
2072 .builder
2073 .substitution_functions
2074 .custom_properties
2075 .inherited = inherited;
2076 }
2077 if initial_values.non_inherited
2078 == context
2079 .builder
2080 .substitution_functions
2081 .custom_properties
2082 .non_inherited
2083 {
2084 let non_inherited = initial_values.non_inherited.clone();
2085 context
2086 .builder
2087 .substitution_functions
2088 .custom_properties
2089 .non_inherited = non_inherited;
2090 }
2091 }
2092}
2093
2094fn substitute_all(
2095 seen: &SeenSubstitutionFunctions,
2096 references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2097 stylist: &Stylist,
2098 computed_context: &mut computed::Context,
2099 cascade: &mut Cascade,
2100 decls: &Declarations,
2101 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
2102 attr_tracker: &mut AttributeTracker,
2103) {
2104 #[derive(Clone, Eq, PartialEq, Debug)]
2111 enum VarType {
2112 Attr(Name),
2113 Custom(Name),
2114 NonCustom(SingleNonCustomReference),
2115 }
2116
2117 #[derive(Debug)]
2119 struct VarInfo {
2120 var: Option<VarType>,
2124 lowlink: usize,
2128 }
2129
2130 #[derive(Debug, Default)]
2131 struct OrderIndexMap {
2132 var: PrecomputedHashMap<Name, usize>,
2134 attr: PrecomputedHashMap<Name, usize>,
2136 }
2137
2138 impl OrderIndexMap {
2139 fn clear(&mut self) {
2140 self.var.clear();
2141 self.attr.clear();
2142 }
2143 }
2144
2145 struct Context<'a, 'b: 'a, 'c, 'd> {
2148 count: usize,
2151 index_map: OrderIndexMap,
2153 non_custom_index_map: NonCustomReferenceMap<usize>,
2155 var_info: SmallVec<[VarInfo; 5]>,
2157 stack: SmallVec<[usize; 5]>,
2160 stylist: &'a Stylist,
2163 computed_context: &'a mut computed::Context<'b>,
2166 cascade: &'a mut Cascade<'c>,
2170 decls: &'a Declarations<'d>,
2172 cache: &'a mut ShorthandsWithPropertyReferencesCache,
2174 }
2175
2176 impl<'a, 'b: 'a, 'c, 'd> Context<'a, 'b, 'c, 'd> {
2177 fn reset(&mut self) {
2178 self.count = 0;
2179 self.index_map.clear();
2180 self.non_custom_index_map = Default::default();
2181 self.var_info.clear();
2182 self.stack.clear();
2183 }
2184
2185 fn map(&self) -> &ComputedSubstitutionFunctions {
2186 &self.computed_context.builder.substitution_functions
2187 }
2188
2189 fn map_mut(&mut self) -> &mut ComputedSubstitutionFunctions {
2190 &mut self.computed_context.builder.substitution_functions
2191 }
2192
2193 fn handle_loop(&mut self, name: &VarType) {
2195 match name {
2196 VarType::Attr(name) => {
2197 self.computed_context
2198 .builder
2199 .substitution_functions
2200 .remove_attr(name);
2201 },
2202 VarType::Custom(name) => {
2203 handle_invalid_at_computed_value_time(
2205 name,
2206 self.stylist.get_custom_property_registration(name),
2207 self.computed_context,
2208 );
2209 },
2210 VarType::NonCustom(non_custom) => {
2211 self.computed_context
2212 .builder
2213 .invalid_non_custom_properties
2214 .insert(non_custom.to_prioritary_id().to_longhand());
2215 },
2216 }
2217 }
2218
2219 fn apply_prioritary_property(
2225 &mut self,
2226 id: PrioritaryPropertyId,
2227 attr_tracker: &mut AttributeTracker,
2228 ) {
2229 self.cascade.ensure_prioritary_property(
2230 self.computed_context,
2231 self.decls,
2232 self.cache,
2233 attr_tracker,
2234 id,
2235 );
2236 }
2237 }
2238
2239 fn visit_value_references<'a, 'b, 'c, 'd>(
2250 var: &VarType,
2251 root: &References,
2252 url_data: &UrlExtraData,
2253 index: usize,
2254 references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2255 context: &mut Context<'a, 'b, 'c, 'd>,
2256 lowlink: &mut usize,
2257 self_ref: &mut bool,
2258 attribute_tracker: &mut AttributeTracker,
2259 non_custom_references: &mut ReferenceFlags,
2260 ) {
2261 let mut refs_stack = SmallVec::<[&References; 5]>::new();
2263 refs_stack.push(root);
2264 while let Some(refs) = refs_stack.pop() {
2265 *non_custom_references |= refs.flags;
2266 for next in &refs.refs {
2267 if next.substitution_kind == SubstitutionFunctionKind::Env {
2268 let device = context.stylist.device();
2272 let present = device
2273 .environment()
2274 .get(&next.name, device, url_data)
2275 .is_some();
2276 if !present {
2277 if let Some(ref fallback) = next.fallback {
2278 refs_stack.push(&fallback.references);
2279 }
2280 }
2281 continue;
2282 }
2283
2284 let next_var = if next.substitution_kind == SubstitutionFunctionKind::Attr {
2285 let can_chain = next.is_attr_with_type() || matches!(var, VarType::Attr(..));
2288 if !can_chain {
2289 continue;
2290 }
2291 if context.map().get_attr(&next.name).is_none() {
2292 if let Ok(val) = get_attr_value_for_cycle_resolution(
2293 &next.name,
2294 &next.attribute_data,
2295 url_data,
2296 attribute_tracker,
2297 ) {
2298 context.map_mut().insert_attr(&next.name, val);
2299 }
2300 }
2301 VarType::Attr(next.name.clone())
2302 } else {
2303 VarType::Custom(next.name.clone())
2304 };
2305
2306 visit_link(
2307 next_var,
2308 index,
2309 references_from_non_custom_properties,
2310 context,
2311 lowlink,
2312 self_ref,
2313 attribute_tracker,
2314 );
2315
2316 let kind = next.substitution_kind;
2319 let resolved = match kind {
2320 SubstitutionFunctionKind::Var => {
2321 let registration =
2322 context.stylist.get_custom_property_registration(&next.name);
2323 context.map().get_var(registration, &next.name)
2324 },
2325 SubstitutionFunctionKind::Attr => context.map().get_attr(&next.name),
2326 SubstitutionFunctionKind::Env => unreachable!("Handled above"),
2327 };
2328 let mut primary_valid = false;
2331 if let Some(ref resolved) = resolved {
2332 if let Some(v) = resolved.as_universal() {
2333 primary_valid = !v.has_references();
2334 *non_custom_references |= v.references.flags;
2335 } else {
2336 primary_valid = true;
2338 }
2339 }
2340
2341 if !primary_valid {
2342 if let Some(ref fallback) = next.fallback {
2343 refs_stack.push(&fallback.references);
2344 }
2345 }
2346 }
2347 }
2348 }
2349
2350 fn visit_link<'a, 'b, 'c, 'd>(
2353 var: VarType,
2354 index: usize,
2355 non_custom_references: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2356 context: &mut Context<'a, 'b, 'c, 'd>,
2357 lowlink: &mut usize,
2358 self_ref: &mut bool,
2359 attr_tracker: &mut AttributeTracker,
2360 ) {
2361 let next_index = match traverse(var, non_custom_references, context, attr_tracker) {
2362 Some(index) => index,
2363 None => return,
2366 };
2367 let next_info = &context.var_info[next_index];
2368 if next_index > index {
2369 *lowlink = cmp::min(*lowlink, next_info.lowlink);
2373 } else if next_index == index {
2374 *self_ref = true;
2375 } else if next_info.var.is_some() {
2376 *lowlink = cmp::min(*lowlink, next_index);
2379 }
2380 }
2381
2382 fn traverse<'a, 'b, 'c, 'd>(
2401 var: VarType,
2402 references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2403 context: &mut Context<'a, 'b, 'c, 'd>,
2404 attribute_tracker: &mut AttributeTracker,
2405 ) -> Option<usize> {
2406 let mut value_non_custom_refs = ReferenceFlags::empty();
2410 let mut registered = false;
2411 let value = match var {
2412 VarType::Custom(ref name) | VarType::Attr(ref name) => {
2413 let map = &context.computed_context.builder.substitution_functions;
2414 let (registration, value, kind) = if matches!(var, VarType::Custom(..)) {
2415 let registration = context.stylist.get_custom_property_registration(name);
2416 (
2417 registration,
2418 map.get_var(registration, name)?.as_universal()?,
2419 SubstitutionFunctionKind::Var,
2420 )
2421 } else {
2422 (
2424 PropertyDescriptors::unregistered(),
2425 map.get_attr(name)?.as_universal()?,
2426 SubstitutionFunctionKind::Attr,
2427 )
2428 };
2429 let is_root = context.computed_context.is_root_element();
2430 value_non_custom_refs = find_non_custom_references(registration, value, is_root);
2431 registered = !registration.is_universal();
2432 let has_dependency = value
2433 .references
2434 .flags
2435 .intersects(ReferenceFlags::ATTR | ReferenceFlags::VAR)
2436 || !value_non_custom_refs.is_empty();
2437 if !has_dependency {
2439 debug_assert!(
2440 !value.references.flags.intersects(ReferenceFlags::ENV),
2441 "Should've been handled earlier"
2442 );
2443 if kind == SubstitutionFunctionKind::Attr || registered {
2444 let value = value.clone();
2449 substitute_references_if_needed_and_apply(
2450 name,
2451 kind,
2452 &value,
2453 context.stylist,
2454 context.computed_context,
2455 attribute_tracker,
2456 );
2457 }
2458 return None;
2459 }
2460
2461 let index_map = if kind == SubstitutionFunctionKind::Var {
2463 &mut context.index_map.var
2464 } else {
2465 &mut context.index_map.attr
2466 };
2467 match index_map.entry(name.clone()) {
2468 Entry::Occupied(entry) => {
2469 return Some(*entry.get());
2470 },
2471 Entry::Vacant(entry) => {
2472 entry.insert(context.count);
2473 },
2474 }
2475 Some(value.clone())
2478 },
2479 VarType::NonCustom(ref non_custom) => {
2480 let entry = &mut context.non_custom_index_map[*non_custom];
2481 if let Some(v) = entry {
2482 return Some(*v);
2483 }
2484 *entry = Some(context.count);
2485 None
2486 },
2487 };
2488
2489 let index = context.count;
2491 context.count += 1;
2492 debug_assert_eq!(index, context.var_info.len());
2493 context.var_info.push(VarInfo {
2494 var: Some(var.clone()),
2495 lowlink: index,
2496 });
2497 context.stack.push(index);
2498
2499 let mut self_ref = false;
2500 let mut lowlink = index;
2501 if let Some(ref v) = value.as_ref() {
2502 debug_assert!(
2503 matches!(var, VarType::Custom(_) | VarType::Attr(_)),
2504 "Non-custom property has references?"
2505 );
2506
2507 visit_value_references(
2509 &var,
2510 &v.references,
2511 &v.url_data,
2512 index,
2513 references_from_non_custom_properties,
2514 context,
2515 &mut lowlink,
2516 &mut self_ref,
2517 attribute_tracker,
2518 &mut value_non_custom_refs,
2519 );
2520
2521 let is_root = context.computed_context.is_root_element();
2524 if registered && !value_non_custom_refs.is_empty() {
2525 value_non_custom_refs.for_each_non_custom(is_root, |r| {
2526 visit_link(
2527 VarType::NonCustom(r),
2528 index,
2529 references_from_non_custom_properties,
2530 context,
2531 &mut lowlink,
2532 &mut self_ref,
2533 attribute_tracker,
2534 );
2535 });
2536 }
2537 } else if let VarType::NonCustom(non_custom) = var {
2538 if non_custom == SingleNonCustomReference::LhUnits {
2548 visit_link(
2549 VarType::NonCustom(SingleNonCustomReference::FontUnits),
2550 index,
2551 references_from_non_custom_properties,
2552 context,
2553 &mut lowlink,
2554 &mut self_ref,
2555 attribute_tracker,
2556 );
2557 }
2558 let entry = &references_from_non_custom_properties[non_custom];
2559 if let Some(values) = entry.as_ref() {
2560 for value in values {
2561 let value = &value.variable_value;
2568 visit_value_references(
2569 &var,
2570 &value.references,
2571 &value.url_data,
2572 index,
2573 references_from_non_custom_properties,
2574 context,
2575 &mut lowlink,
2576 &mut self_ref,
2577 attribute_tracker,
2578 &mut Default::default(),
2579 );
2580 }
2581 }
2582 }
2583
2584 context.var_info[index].lowlink = lowlink;
2585 if lowlink != index {
2586 return Some(index);
2592 }
2593
2594 let mut in_loop = self_ref;
2596 loop {
2597 let var_index = context
2598 .stack
2599 .pop()
2600 .expect("The current variable should still be in stack");
2601 let var_info = &mut context.var_info[var_index];
2602 let var_name = var_info
2604 .var
2605 .take()
2606 .expect("Variable should not be popped from stack twice");
2607 if var_index != index {
2608 in_loop = true;
2611 }
2612 if in_loop {
2613 context.handle_loop(&var_name);
2614 }
2615 if var_index == index {
2616 debug_assert_eq!(var_name, var);
2617 break;
2618 }
2619 }
2620
2621 if in_loop {
2622 return None;
2623 }
2624
2625 match var {
2627 VarType::Custom(ref name) | VarType::Attr(ref name) => {
2628 if let Some(ref v) = value {
2629 let kind = if matches!(var, VarType::Custom(..)) {
2630 SubstitutionFunctionKind::Var
2631 } else {
2632 SubstitutionFunctionKind::Attr
2633 };
2634 substitute_references_if_needed_and_apply(
2637 name,
2638 kind,
2639 v,
2640 context.stylist,
2641 context.computed_context,
2642 attribute_tracker,
2643 );
2644 }
2645 },
2646 VarType::NonCustom(non_custom) => {
2647 context.apply_prioritary_property(non_custom.to_prioritary_id(), attribute_tracker);
2648 },
2649 }
2650 None
2652 }
2653
2654 let mut context = Context {
2655 count: 0,
2656 index_map: OrderIndexMap::default(),
2657 non_custom_index_map: NonCustomReferenceMap::default(),
2658 stack: SmallVec::new(),
2659 var_info: SmallVec::new(),
2660 stylist,
2661 computed_context: &mut *computed_context,
2662 cascade: &mut *cascade,
2663 decls,
2664 cache: &mut *shorthand_cache,
2665 };
2666 let mut first = true;
2667 let mut run_one = |var: VarType| {
2668 if !first {
2669 context.reset();
2670 }
2671 first = false;
2672 traverse(
2673 var,
2674 references_from_non_custom_properties,
2675 &mut context,
2676 attr_tracker,
2677 );
2678 };
2679 for (var, has_refs) in &seen.var {
2683 if !has_refs {
2684 continue;
2685 }
2686 run_one(VarType::Custom((*var).clone()));
2687 }
2688 for attr in &seen.attr {
2691 run_one(VarType::Attr((*attr).clone()));
2692 }
2693}