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 unvisited_properties: &'a LonghandIdSet,
252 },
253}
254
255fn iter_declarations<'c, 'decls: 'c>(
256 iter: impl Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
257 declarations: &mut Declarations<'decls>,
258 mut custom: Option<(&mut Cascade<'c>, &mut computed::Context)>,
259 attribute_tracker: &mut AttributeTracker,
260) {
261 for (declaration, priority) in iter {
262 if let PropertyDeclaration::Custom(ref declaration) = *declaration {
263 if let Some((ref mut cascade, ref mut context)) = custom {
264 cascade.cascade_custom_property(context, declaration, priority);
265 }
266 } else {
267 let id = declaration.id().as_longhand().unwrap();
268 declarations.note_declaration(declaration, priority, id);
269 if Cascade::might_have_non_custom_or_attr_dependency(id, declaration) {
270 if let Some((ref mut cascade, ref mut context)) = custom {
271 cascade.maybe_note_non_custom_dependency(
272 context,
273 id,
274 declaration,
275 attribute_tracker,
276 );
277 }
278 }
279 }
280 }
281}
282
283pub fn apply_declarations<'decls, E, I>(
286 stylist: &Stylist,
287 pseudo: Option<&PseudoElement>,
288 rules: &StrongRuleNode,
289 guards: &StylesheetGuards,
290 iter: I,
291 parent_style: Option<&ComputedValues>,
292 layout_parent_style: Option<&ComputedValues>,
293 first_line_reparenting: FirstLineReparenting<'_>,
294 try_tactic: &PositionTryFallbacksTryTactic,
295 cascade_mode: CascadeMode,
296 cascade_input_flags: ComputedValueFlags,
297 included_cascade_flags: RuleCascadeFlags,
298 rule_cache: Option<&RuleCache>,
299 rule_cache_conditions: &mut RuleCacheConditions,
300 element: Option<E>,
301 tree_counting_caches: &mut TreeCountingCaches,
302) -> Arc<ComputedValues>
303where
304 E: TElement,
305 I: Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
306{
307 debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
308 let device = stylist.device();
309 let inherited_style = parent_style.unwrap_or(device.default_computed_values());
310 let is_root_element = pseudo.is_none() && element.is_some_and(|e| e.is_root());
311 let container_size_query =
312 ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());
313
314 let originating_element = element.map(|e| e.ultimate_originating_element());
315 let element_context = match originating_element {
316 Some(ref e) => e as &dyn ElementContext,
317 None => &DummyElementContext {},
318 };
319
320 let mut context = computed::Context::new(
321 StyleBuilder::new(
325 device,
326 Some(stylist),
327 parent_style,
328 pseudo,
329 Some(rules.clone()),
330 is_root_element,
331 ),
332 stylist.quirks_mode(),
333 rule_cache_conditions,
334 container_size_query,
335 included_cascade_flags,
336 element_context,
337 tree_counting_caches,
338 );
339
340 context.style().add_flags(cascade_input_flags);
341 context
344 .style()
345 .add_flags(stylist.get_custom_property_initial_values_flags());
346
347 let using_cached_reset_properties;
348 let ignore_colors = context.builder.device.forced_colors().is_active();
349 let mut cascade = Cascade::new(first_line_reparenting, stylist, ignore_colors);
350 let mut declarations = Default::default();
351 let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
352 let mut attribute_tracker = AttributeTracker::new(element_context);
353
354 let properties_to_apply = match cascade_mode {
355 CascadeMode::Visited {
356 unvisited_context,
357 unvisited_properties,
358 } => {
359 context.builder.substitution_functions =
360 unvisited_context.builder.substitution_functions.clone();
361 context.builder.writing_mode = unvisited_context.builder.writing_mode;
362 context.builder.color_scheme = unvisited_context.builder.color_scheme;
363 using_cached_reset_properties = false;
367 let visited_dependent_props = LonghandIdSet::visited_dependent();
368 if unvisited_context.builder.rules.as_ref() != Some(rules)
372 || unvisited_properties.contains_any(visited_dependent_props)
373 {
374 iter_declarations(iter, &mut declarations, None, &mut attribute_tracker);
375 }
376 visited_dependent_props
377 },
378 CascadeMode::Unvisited { .. } => {
379 cascade.init_custom_properties(&mut context);
380 iter_declarations(
381 iter,
382 &mut declarations,
383 Some((&mut cascade, &mut context)),
384 &mut attribute_tracker,
385 );
386 cascade.apply_custom_and_prioritary_properties(
391 &mut context,
392 &declarations,
393 &mut shorthand_cache,
394 &mut attribute_tracker,
395 );
396
397 using_cached_reset_properties =
398 cascade.try_to_use_cached_reset_properties(&mut context, rule_cache, guards);
399
400 if using_cached_reset_properties {
401 LonghandIdSet::late_group_only_inherited()
402 } else {
403 LonghandIdSet::late_group()
404 }
405 },
406 };
407
408 cascade.apply_non_prioritary_properties(
409 &mut context,
410 &declarations.longhand_declarations,
411 &mut shorthand_cache,
412 properties_to_apply,
413 &mut attribute_tracker,
414 );
415
416 context.builder.attribute_references = attribute_tracker.finalize();
417
418 cascade.finished_applying_properties(&mut context.builder);
419
420 context.builder.clear_modified_reset();
421
422 if let CascadeMode::Unvisited { visited_rules } = cascade_mode {
423 if let Some(visited_rules) = visited_rules {
424 cascade.compute_visited_style_if_needed(
425 &mut context,
426 element,
427 parent_style,
428 layout_parent_style,
429 try_tactic,
430 visited_rules,
431 guards,
432 );
433 }
434
435 StyleAdjuster::new(&mut context.builder).adjust(
436 layout_parent_style.unwrap_or(inherited_style),
437 element,
438 try_tactic,
439 &cascade.author_specified,
440 );
441 }
442
443 if context.builder.modified_reset() || using_cached_reset_properties {
444 context.rule_cache_conditions.borrow_mut().set_uncacheable();
450 }
451
452 if context
453 .builder
454 .flags()
455 .intersects(ComputedValueFlags::tree_counting_function_flags())
456 {
457 if let Some(el) = element {
458 el.apply_selector_flags(ElementSelectorFlags::MAY_HAVE_TREE_COUNTING_FUNCTION);
459 } else {
460 debug_assert!(
461 false,
462 "Tree counting function flag applied without an element?"
463 );
464 }
465 }
466
467 context.builder.build()
468}
469
470type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
476
477fn is_base_appearance(context: &computed::Context) -> bool {
478 use computed::Appearance;
479 let box_style = context.builder.get_box();
480 match box_style.clone_appearance() {
481 Appearance::BaseSelect => {
482 matches!(
483 box_style.clone__moz_default_appearance(),
484 Appearance::Listbox | Appearance::Menulist
485 )
486 },
487 Appearance::Base => box_style.clone__moz_default_appearance() != Appearance::None,
488 _ => false,
489 }
490}
491
492fn tweak_when_ignoring_colors(
493 context: &computed::Context,
494 longhand_id: LonghandId,
495 origin: CascadeOrigin,
496 declaration: &mut Cow<PropertyDeclaration>,
497 declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
498) {
499 use crate::values::computed::ToComputedValue;
500 use crate::values::specified::Color;
501
502 if !longhand_id.ignored_when_document_colors_disabled() {
503 return;
504 }
505
506 let is_ua_or_user_rule = matches!(origin, CascadeOrigin::User | CascadeOrigin::UA);
507 if is_ua_or_user_rule {
508 return;
509 }
510
511 let forced = context
513 .builder
514 .get_inherited_text()
515 .clone_forced_color_adjust();
516 if forced == computed::ForcedColorAdjust::None {
517 return;
518 }
519
520 fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
521 color
523 .to_computed_value(context)
524 .resolve_to_absolute(&AbsoluteColor::BLACK)
525 .alpha
526 }
527
528 match **declaration {
530 PropertyDeclaration::CSSWideKeyword(..) => return,
532 PropertyDeclaration::BackgroundColor(ref color) => {
533 if color.honored_in_forced_colors_mode(context, true) {
543 return;
544 }
545 let alpha = alpha_channel(color, context);
549 if alpha == 0.0 {
550 return;
551 }
552 let mut color = context.builder.device.default_background_color();
553 color.alpha = alpha;
554 declarations_to_apply_unless_overridden
555 .push(PropertyDeclaration::BackgroundColor(color.into()))
556 },
557 PropertyDeclaration::Color(ref color) => {
558 if color
560 .0
561 .honored_in_forced_colors_mode(context, true)
562 {
563 return;
564 }
565 if context
569 .builder
570 .get_parent_inherited_text()
571 .clone_color()
572 .alpha
573 == 0.0
574 {
575 let color = context.builder.device.default_color();
576 declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
577 specified::ColorPropertyValue(color.into()),
578 ))
579 }
580 },
581 #[cfg(feature = "gecko")]
583 PropertyDeclaration::BackgroundImage(ref bkg) => {
584 use crate::values::generics::image::Image;
585 if crate::pref!("browser.display.permit_backplate")
586 && bkg
587 .0
588 .iter()
589 .all(|image| matches!(*image, Image::Url(..) | Image::None))
590 {
591 return;
592 }
593 },
594 _ => {
595 if let Some(color) = declaration.color_value() {
608 if color
609 .honored_in_forced_colors_mode(context, false)
610 {
611 return;
612 }
613 }
614 },
615 }
616
617 *declaration.to_mut() =
618 PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
619}
620
621type DeclarationIndex = u16;
623
624#[derive(Copy, Clone)]
629struct PrioritaryDeclarationPosition {
630 most_important: DeclarationIndex,
632 least_important: DeclarationIndex,
633}
634
635impl Default for PrioritaryDeclarationPosition {
636 fn default() -> Self {
637 Self {
638 most_important: DeclarationIndex::MAX,
639 least_important: DeclarationIndex::MAX,
640 }
641 }
642}
643
644#[derive(Copy, Clone)]
645struct Declaration<'a> {
646 decl: &'a PropertyDeclaration,
647 priority: CascadePriority,
648 next_index: DeclarationIndex,
649}
650
651#[derive(Default)]
653pub(crate) struct Declarations<'a> {
654 has_prioritary_properties: bool,
656 longhand_declarations: SmallVec<[Declaration<'a>; 64]>,
658 prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
660}
661
662impl<'a> Declarations<'a> {
663 fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
664 let new_index = self.longhand_declarations.len();
665 if new_index >= DeclarationIndex::MAX as usize {
666 return;
669 }
670
671 self.has_prioritary_properties = true;
672 let new_index = new_index as DeclarationIndex;
673 let position = &mut self.prioritary_positions[id as usize];
674 if position.most_important == DeclarationIndex::MAX {
675 position.most_important = new_index;
678 } else {
679 self.longhand_declarations[position.least_important as usize].next_index = new_index;
681 }
682 position.least_important = new_index;
683 }
684
685 fn note_declaration(
686 &mut self,
687 decl: &'a PropertyDeclaration,
688 priority: CascadePriority,
689 id: LonghandId,
690 ) {
691 if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
692 self.note_prioritary_property(id);
693 }
694 self.longhand_declarations.push(Declaration {
695 decl,
696 priority,
697 next_index: 0,
698 });
699 }
700}
701
702#[derive(Default)]
703struct RevertedSet {
704 longhands_set: LonghandIdSet,
706 longhands: FxHashMap<LonghandId, (CascadePriority, RevertKind)>,
707 custom: PrecomputedHashMap<Name, (CascadePriority, RevertKind)>,
708}
709
710#[derive(Default)]
711struct SeenSubstitutionFunctions<'a> {
712 var: PrecomputedHashMap<&'a Name, bool>,
715 attr: PrecomputedHashSet<&'a Name>,
716}
717
718#[derive(Default)]
719struct SeenSet<'a> {
720 longhands: LonghandIdSet,
721 custom: SeenSubstitutionFunctions<'a>,
722}
723
724fn find_non_custom_references(
726 registration: &PropertyDescriptors,
727 value: &VariableValue,
728 is_root_element: bool,
729) -> ReferenceFlags {
730 use crate::properties_and_values::syntax::data_type::DependentDataTypes;
731
732 let mut result = ReferenceFlags::empty();
733 let Some(syntax) = registration.syntax.as_ref() else {
734 return result;
735 };
736 let dependent_types = syntax.dependent_types();
737 let may_reference_length = dependent_types.intersects(DependentDataTypes::LENGTH);
738 if may_reference_length {
739 result |= value.references.non_custom_references(is_root_element);
740 }
741 if dependent_types.intersects(DependentDataTypes::COLOR) {
742 result |= ReferenceFlags::COLOR_SCHEME;
745 }
746 result
747}
748
749pub struct KeyframeCustomPropertiesBuilder<'a> {
758 cascade: Cascade<'a>,
759 decls: Declarations<'a>,
760 shorthand_cache: ShorthandsWithPropertyReferencesCache,
761}
762
763impl<'a> KeyframeCustomPropertiesBuilder<'a> {
764 pub fn new(
767 stylist: &'a Stylist,
768 context: &mut computed::Context,
769 base: ComputedCustomProperties,
770 ) -> Self {
771 context.builder.substitution_functions =
772 ComputedSubstitutionFunctions::new(Some(base), None);
773 Self {
774 cascade: Cascade::new_for_custom_properties_only(stylist),
775 decls: Declarations::default(),
776 shorthand_cache: ShorthandsWithPropertyReferencesCache::default(),
777 }
778 }
779
780 pub fn cascade(
782 &mut self,
783 context: &mut computed::Context,
784 declaration: &'a CustomDeclaration,
785 priority: CascadePriority,
786 ) {
787 self.cascade
788 .cascade_custom_property(context, declaration, priority);
789 }
790
791 pub fn build(
793 mut self,
794 context: &mut computed::Context,
795 attribute_tracker: &mut AttributeTracker,
796 ) {
797 self.cascade.apply_custom_and_prioritary_properties(
798 context,
799 &self.decls,
800 &mut self.shorthand_cache,
801 attribute_tracker,
802 );
803 }
804}
805
806pub(crate) struct Cascade<'a> {
807 first_line_reparenting: FirstLineReparenting<'a>,
808 stylist: &'a Stylist,
809 ignore_colors: bool,
810 seen: SeenSet<'a>,
811 reverted: RevertedSet,
812 author_specified: LonghandIdSet,
813 declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
814 may_have_custom_property_cycles: bool,
815 references_from_non_custom_properties: NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
816 ensured_prioritary: PrioritaryPropertyIdSet,
818}
819
820impl<'a> Cascade<'a> {
821 fn new(
822 first_line_reparenting: FirstLineReparenting<'a>,
823 stylist: &'a Stylist,
824 ignore_colors: bool,
825 ) -> Self {
826 Self {
827 first_line_reparenting,
828 stylist,
829 ignore_colors,
830 seen: Default::default(),
831 author_specified: Default::default(),
832 reverted: Default::default(),
833 declarations_to_apply_unless_overridden: Default::default(),
834 may_have_custom_property_cycles: false,
835 ensured_prioritary: PrioritaryPropertyIdSet::default(),
836 references_from_non_custom_properties: Default::default(),
837 }
838 }
839
840 fn new_for_custom_properties_only(stylist: &'a Stylist) -> Self {
843 Self {
844 first_line_reparenting: FirstLineReparenting::No,
845 stylist,
846 ignore_colors: false,
847 seen: Default::default(),
848 author_specified: Default::default(),
849 reverted: Default::default(),
850 declarations_to_apply_unless_overridden: Default::default(),
851 may_have_custom_property_cycles: false,
852 ensured_prioritary: PrioritaryPropertyIdSet::default(),
853 references_from_non_custom_properties: Default::default(),
854 }
855 }
856
857 fn substitute_variables_if_needed<'cache, 'decl>(
858 &self,
859 context: &mut computed::Context,
860 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
861 declaration: &'decl PropertyDeclaration,
862 attribute_tracker: &mut AttributeTracker,
863 ) -> Cow<'decl, PropertyDeclaration>
864 where
865 'cache: 'decl,
866 {
867 let declaration = match *declaration {
868 PropertyDeclaration::WithVariables(ref declaration) => declaration,
869 ref d => return Cow::Borrowed(d),
870 };
871
872 if !declaration.id.inherited() {
873 context.rule_cache_conditions.borrow_mut().set_uncacheable();
874
875 if matches!(declaration.id, LonghandId::Display | LonghandId::Content) {
881 context
882 .builder
883 .add_flags(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE);
884 }
885 }
886
887 debug_assert!(
888 context.builder.stylist.is_some(),
889 "Need a Stylist to substitute variables!"
890 );
891 declaration.value.substitute_variables(
892 declaration.id,
893 context.builder.substitution_functions(),
894 context.builder.stylist.unwrap(),
895 context,
896 shorthand_cache,
897 attribute_tracker,
898 )
899 }
900
901 fn apply_one_prioritary_property(
902 &mut self,
903 context: &mut computed::Context,
904 decls: &Declarations,
905 cache: &mut ShorthandsWithPropertyReferencesCache,
906 id: PrioritaryPropertyId,
907 attr_provider: &mut AttributeTracker,
908 ) {
909 let mut index = decls.prioritary_positions[id as usize].most_important;
910 if index == DeclarationIndex::MAX {
911 return;
912 }
913
914 let longhand_id = id.to_longhand();
915 debug_assert!(
916 !longhand_id.is_logical(),
917 "That could require more book-keeping"
918 );
919 loop {
920 let decl = decls.longhand_declarations[index as usize];
921 self.apply_one_longhand(
922 context,
923 longhand_id,
924 decl.decl,
925 decl.priority,
926 cache,
927 attr_provider,
928 );
929 if self.seen.longhands.contains(longhand_id) {
930 self.did_apply_prioritary_property(context, id);
932 return;
933 }
934 debug_assert!(
935 decl.next_index == 0 || decl.next_index > index,
936 "should make progress! {} -> {}",
937 index,
938 decl.next_index,
939 );
940 index = decl.next_index;
941 if index == 0 {
942 return;
943 }
944 }
945 }
946
947 fn did_apply_prioritary_property(
949 &mut self,
950 context: &mut computed::Context,
951 id: PrioritaryPropertyId,
952 ) {
953 use crate::properties::PrioritaryPropertyId::*;
954 match id {
955 Appearance => {
956 if is_base_appearance(context) {
957 context
958 .style()
959 .add_flags(ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE);
960 context
961 .included_cascade_flags
962 .insert(RuleCascadeFlags::APPEARANCE_BASE);
963 }
964 },
965 WritingMode | Direction | TextOrientation => {
966 context.builder.writing_mode =
967 crate::logical_geometry::WritingMode::new(context.builder.get_inherited_box());
968 },
969 Zoom => {
970 context.builder.recompute_effective_zooms();
971 if !context.builder.effective_zoom_for_inheritance.is_one() {
972 self.recompute_font_size_for_zoom_change(&mut context.builder);
978 }
979 },
980 XLang => {
981 #[cfg(feature = "gecko")]
982 self.recompute_initial_font_family_if_needed(&mut context.builder);
983 self.recompute_keyword_font_size_if_needed(context);
984 },
985 FontFamily => {
986 #[cfg(feature = "gecko")]
987 self.prioritize_user_fonts_if_needed(&mut context.builder);
988 self.recompute_keyword_font_size_if_needed(context);
989 },
990 FontSize => {
991 if self.seen.longhands.contains(LonghandId::MathDepth) {
992 #[cfg(feature = "gecko")]
993 Self::recompute_math_font_size_if_needed(context);
994 }
995 if self.seen.longhands.contains(LonghandId::XLang)
996 || self.seen.longhands.contains(LonghandId::FontFamily)
997 {
998 self.recompute_keyword_font_size_if_needed(context);
999 }
1000 #[cfg(feature = "gecko")]
1001 self.constrain_font_size_if_needed(&mut context.builder);
1002 },
1003 XTextScale => {
1004 #[cfg(feature = "gecko")]
1005 self.unzoom_fonts_if_needed(&mut context.builder);
1006 },
1007 MozMinFontSizeRatio => {
1008 #[cfg(feature = "gecko")]
1009 self.constrain_font_size_if_needed(&mut context.builder);
1010 },
1011 ColorScheme => {
1012 context.builder.color_scheme =
1013 context.builder.get_inherited_ui().color_scheme_bits();
1014 },
1015 MozDefaultAppearance | MathDepth | FontWeight | FontWidth | FontStyle
1016 | FontSizeAdjust | ForcedColorAdjust | LineHeight => {},
1017 }
1018 }
1019
1020 fn apply_non_prioritary_properties(
1021 &mut self,
1022 context: &mut computed::Context,
1023 longhand_declarations: &[Declaration],
1024 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
1025 properties_to_apply: &LonghandIdSet,
1026 attribute_tracker: &mut AttributeTracker,
1027 ) {
1028 debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
1029 debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
1030 for declaration in longhand_declarations {
1031 let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
1032 if !properties_to_apply.contains(longhand_id) {
1033 continue;
1034 }
1035 debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
1036 let is_logical = longhand_id.is_logical();
1037 if is_logical {
1038 let wm = context.builder.writing_mode;
1039 context
1040 .rule_cache_conditions
1041 .borrow_mut()
1042 .set_writing_mode_dependency(wm);
1043 longhand_id = longhand_id.to_physical(wm);
1044 }
1045 self.apply_one_longhand(
1046 context,
1047 longhand_id,
1048 declaration.decl,
1049 declaration.priority,
1050 shorthand_cache,
1051 attribute_tracker,
1052 );
1053 }
1054 if !self.declarations_to_apply_unless_overridden.is_empty() {
1055 debug_assert!(self.ignore_colors);
1056 for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
1057 let longhand_id = declaration.id().as_longhand().unwrap();
1058 debug_assert!(!longhand_id.is_logical());
1059 if !self.seen.longhands.contains(longhand_id) {
1060 unsafe {
1061 self.do_apply_declaration(context, longhand_id, &declaration);
1062 }
1063 }
1064 }
1065 }
1066
1067 if !context.builder.effective_zoom_for_inheritance.is_one() {
1068 self.recompute_zoom_dependent_inherited_lengths(context);
1069 }
1070 }
1071
1072 #[cold]
1073 fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {
1074 debug_assert!(self.seen.longhands.contains(LonghandId::Zoom));
1075 for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {
1076 if self.seen.longhands.contains(prop) {
1077 continue;
1078 }
1079 let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);
1080 unsafe {
1081 self.do_apply_declaration(context, prop, &declaration);
1082 }
1083 }
1084 }
1085
1086 fn apply_one_longhand(
1087 &mut self,
1088 context: &mut computed::Context,
1089 longhand_id: LonghandId,
1090 declaration: &PropertyDeclaration,
1091 priority: CascadePriority,
1092 cache: &mut ShorthandsWithPropertyReferencesCache,
1093 attribute_tracker: &mut AttributeTracker,
1094 ) {
1095 debug_assert!(!longhand_id.is_logical());
1096 if self.seen.longhands.contains(longhand_id) {
1097 return;
1098 }
1099
1100 if !(priority.flags() - context.included_cascade_flags).is_empty() {
1101 return;
1102 }
1103
1104 if self.reverted.longhands_set.contains(longhand_id) {
1105 if let Some(&(reverted_priority, revert_kind)) =
1106 self.reverted.longhands.get(&longhand_id)
1107 {
1108 if !reverted_priority.allows_when_reverted(&priority, revert_kind) {
1109 return;
1110 }
1111 }
1112 }
1113
1114 let mut declaration =
1115 self.substitute_variables_if_needed(context, cache, declaration, attribute_tracker);
1116
1117 let origin = priority.cascade_level().origin();
1120 if self.ignore_colors {
1121 tweak_when_ignoring_colors(
1122 context,
1123 longhand_id,
1124 origin,
1125 &mut declaration,
1126 &mut self.declarations_to_apply_unless_overridden,
1127 );
1128 }
1129 let can_skip_apply = match declaration.get_css_wide_keyword() {
1130 Some(keyword) => {
1131 if let Some(revert_kind) = keyword.revert_kind() {
1132 self.reverted.longhands_set.insert(longhand_id);
1136 self.reverted
1137 .longhands
1138 .insert(longhand_id, (priority, revert_kind));
1139 return;
1140 }
1141
1142 let inherited = longhand_id.inherited();
1143 let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()
1144 && longhand_id.zoom_dependent();
1145 match keyword {
1146 CSSWideKeyword::Revert
1147 | CSSWideKeyword::RevertLayer
1148 | CSSWideKeyword::RevertRule => unreachable!(),
1149 CSSWideKeyword::Unset => !zoomed || !inherited,
1150 CSSWideKeyword::Inherit => inherited && !zoomed,
1151 CSSWideKeyword::Initial => !inherited,
1152 }
1153 },
1154 None => false,
1155 };
1156
1157 self.seen.longhands.insert(longhand_id);
1158 if origin.is_author_origin() {
1159 self.author_specified.insert(longhand_id);
1160 }
1161
1162 if !can_skip_apply {
1163 let old_scope = context.scope;
1167 let cascade_level = priority.cascade_level();
1168 context.scope = cascade_level;
1169 unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
1170 context.scope = old_scope;
1171 }
1172 }
1173
1174 #[inline]
1175 unsafe fn do_apply_declaration(
1176 &self,
1177 context: &mut computed::Context,
1178 longhand_id: LonghandId,
1179 declaration: &PropertyDeclaration,
1180 ) {
1181 debug_assert!(!longhand_id.is_logical());
1182 unsafe {
1183 (CASCADE_PROPERTY[longhand_id as usize])(declaration, context);
1189 }
1190 }
1191
1192 fn compute_visited_style_if_needed<E>(
1193 &self,
1194 context: &mut computed::Context,
1195 element: Option<E>,
1196 parent_style: Option<&ComputedValues>,
1197 layout_parent_style: Option<&ComputedValues>,
1198 try_tactic: &PositionTryFallbacksTryTactic,
1199 visited_rules: &StrongRuleNode,
1200 guards: &StylesheetGuards,
1201 ) where
1202 E: TElement,
1203 {
1204 let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
1205
1206 macro_rules! visited_parent {
1207 ($parent:expr) => {
1208 if is_link {
1209 $parent
1210 } else {
1211 $parent.map(|p| p.visited_style().unwrap_or(p))
1212 }
1213 };
1214 }
1215
1216 let style = cascade_rules(
1219 context.builder.stylist.unwrap(),
1220 context.builder.pseudo,
1221 visited_rules,
1222 guards,
1223 visited_parent!(parent_style),
1224 visited_parent!(layout_parent_style),
1225 self.first_line_reparenting,
1226 try_tactic,
1227 CascadeMode::Visited {
1228 unvisited_context: &*context,
1229 unvisited_properties: &self.seen.longhands,
1230 },
1231 Default::default(),
1234 context.included_cascade_flags,
1235 None, &mut *context.rule_cache_conditions.borrow_mut(),
1241 element,
1242 &mut *context.tree_counting_caches.borrow_mut(),
1243 );
1244 context.builder.visited_style = Some(style);
1245 }
1246
1247 fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
1248 #[cfg(feature = "gecko")]
1249 {
1250 if let Some(bg) = builder.get_background_if_mutated() {
1251 bg.fill_arrays();
1252 }
1253
1254 if let Some(svg) = builder.get_svg_if_mutated() {
1255 svg.fill_arrays();
1256 }
1257 }
1258
1259 if self
1260 .author_specified
1261 .contains_any(LonghandIdSet::border_background_properties())
1262 {
1263 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
1264 }
1265
1266 if self.author_specified.contains(LonghandId::Color) {
1267 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);
1268 }
1269
1270 if self.author_specified.contains(LonghandId::TextShadow) {
1271 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_SHADOW);
1272 }
1273
1274 if self.author_specified.contains(LonghandId::GridAutoFlow) {
1275 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW);
1276 }
1277 #[cfg(feature = "servo")]
1278 {
1279 if let Some(font) = builder.get_font_if_mutated() {
1280 font.compute_font_hash();
1281 }
1282 }
1283 }
1284
1285 fn try_to_use_cached_reset_properties(
1286 &self,
1287 context: &mut computed::Context<'a>,
1288 cache: Option<&'a RuleCache>,
1289 guards: &StylesheetGuards,
1290 ) -> bool {
1291 let style = match self.first_line_reparenting {
1292 FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
1293 FirstLineReparenting::No => {
1294 let Some(cache) = cache else { return false };
1295 let Some(style) = cache.find(guards, context) else {
1296 return false;
1297 };
1298 style
1299 },
1300 };
1301
1302 context.builder.copy_reset_from(style);
1303
1304 let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND
1316 | ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW
1317 | ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS
1318 | ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS
1319 | ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE
1320 | ComputedValueFlags::USES_CONTAINER_UNITS
1321 | ComputedValueFlags::USES_VIEWPORT_UNITS
1322 | ComputedValueFlags::USES_FONT_OR_WM_RELATIVE_UNITS
1323 | ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY
1324 | ComputedValueFlags::USES_SIBLING_COUNT
1325 | ComputedValueFlags::USES_SIBLING_INDEX;
1326 context.builder.add_flags(style.flags & bits_to_copy);
1327
1328 true
1329 }
1330
1331 #[inline]
1334 #[cfg(feature = "gecko")]
1335 fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
1336 use crate::gecko_bindings::bindings;
1337 use crate::values::computed::font::FontFamily;
1338
1339 let default_font_type = {
1340 let font = builder.get_font();
1341
1342 if !font.mFont.family.is_initial {
1343 return;
1344 }
1345
1346 let default_font_type = unsafe {
1347 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1348 builder.device.document(),
1349 font.mLanguage.mRawPtr,
1350 )
1351 };
1352
1353 let initial_generic = font.mFont.family.families.single_generic();
1354 debug_assert!(
1355 initial_generic.is_some(),
1356 "Initial font should be just one generic font"
1357 );
1358 if initial_generic == Some(default_font_type) {
1359 return;
1360 }
1361
1362 default_font_type
1363 };
1364
1365 builder.mutate_font().mFont.family.families =
1367 FontFamily::generic(default_font_type).families.clone();
1368 }
1369
1370 #[inline]
1372 #[cfg(feature = "gecko")]
1373 fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1374 use crate::gecko_bindings::bindings;
1375
1376 if crate::pref!("browser.display.use_document_fonts") != 0
1379 || builder.device.chrome_rules_enabled_for_document()
1380 {
1381 return;
1382 }
1383
1384 let default_font_type = {
1385 let font = builder.get_font();
1386
1387 if font.mFont.family.is_system_font {
1388 return;
1389 }
1390
1391 if !font.mFont.family.families.needs_user_font_prioritization() {
1392 return;
1393 }
1394
1395 unsafe {
1396 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1397 builder.device.document(),
1398 font.mLanguage.mRawPtr,
1399 )
1400 }
1401 };
1402
1403 let font = builder.mutate_font();
1404 font.mFont
1405 .family
1406 .families
1407 .prioritize_first_generic_or_prepend(default_font_type);
1408 }
1409
1410 fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
1412 use crate::values::computed::ToComputedValue;
1413
1414 if !self.seen.longhands.contains(LonghandId::XLang)
1415 && !self.seen.longhands.contains(LonghandId::FontFamily)
1416 {
1417 return;
1418 }
1419
1420 let new_size = {
1421 let font = context.builder.get_font();
1422 let info = font.clone_font_size().keyword_info;
1423 let new_size = match info.kw {
1424 specified::FontSizeKeyword::None => return,
1425 _ => {
1426 context.for_non_inherited_property = false;
1427 specified::FontSize::Keyword(info).to_computed_value(context)
1428 },
1429 };
1430
1431 #[cfg(feature = "gecko")]
1432 if font.mScriptUnconstrainedSize == new_size.computed_size {
1433 return;
1434 }
1435
1436 new_size
1437 };
1438
1439 context.builder.mutate_font().set_font_size(new_size);
1440 }
1441
1442 #[cfg(feature = "gecko")]
1445 fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
1446 use crate::gecko_bindings::bindings;
1447 use crate::values::generics::NonNegative;
1448
1449 let min_font_size = {
1450 let font = builder.get_font();
1451 let min_font_size = unsafe {
1452 bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
1453 };
1454
1455 if font.mFont.size.0 >= min_font_size {
1456 return;
1457 }
1458
1459 NonNegative(min_font_size)
1460 };
1461
1462 builder.mutate_font().mFont.size = min_font_size;
1463 }
1464
1465 #[cfg(feature = "gecko")]
1469 fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1470 debug_assert!(self.seen.longhands.contains(LonghandId::XTextScale));
1471
1472 let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
1473 let text_scale = builder.get_font().clone__x_text_scale();
1474 if parent_text_scale == text_scale {
1475 return;
1476 }
1477 debug_assert_ne!(
1478 parent_text_scale.text_zoom_enabled(),
1479 text_scale.text_zoom_enabled(),
1480 "There's only one value that disables it"
1481 );
1482 debug_assert!(
1483 !text_scale.text_zoom_enabled(),
1484 "We only ever disable text zoom never enable it"
1485 );
1486 let device = builder.device;
1487 builder.mutate_font().unzoom_fonts(device);
1488 }
1489
1490 fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
1491 debug_assert!(self.seen.longhands.contains(LonghandId::Zoom));
1492 let old_size = builder.get_font().clone_font_size();
1495 let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);
1496 if old_size == new_size {
1497 return;
1498 }
1499 builder.mutate_font().set_font_size(new_size);
1500 }
1501
1502 #[cfg(feature = "gecko")]
1507 fn recompute_math_font_size_if_needed(context: &mut computed::Context) {
1508 use crate::values::generics::NonNegative;
1509
1510 if context.builder.get_font().clone_font_size().keyword_info.kw
1512 != specified::FontSizeKeyword::Math
1513 {
1514 return;
1515 }
1516
1517 const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
1518
1519 fn scale_factor_for_math_depth_change(
1529 parent_math_depth: i32,
1530 computed_math_depth: i32,
1531 parent_script_percent_scale_down: Option<f32>,
1532 parent_script_script_percent_scale_down: Option<f32>,
1533 ) -> f32 {
1534 let mut a = parent_math_depth;
1535 let mut b = computed_math_depth;
1536 let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
1537 let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or(c);
1538 let scale_between_0_and_2 = parent_script_script_percent_scale_down.unwrap_or(c * c);
1539 let mut s = 1.0;
1540 let mut invert_scale_factor = false;
1541 if a == b {
1542 return s;
1543 }
1544 if b < a {
1545 std::mem::swap(&mut a, &mut b);
1546 invert_scale_factor = true;
1547 }
1548 let mut e = b - a;
1549 if a <= 0 && b >= 2 {
1550 s *= scale_between_0_and_2;
1551 e -= 2;
1552 } else if a == 1 {
1553 s *= scale_between_0_and_2 / scale_between_0_and_1;
1554 e -= 1;
1555 } else if b == 1 {
1556 s *= scale_between_0_and_1;
1557 e -= 1;
1558 }
1559 s *= c.powi(e);
1560 if invert_scale_factor {
1561 1.0 / s.max(f32::MIN_POSITIVE)
1562 } else {
1563 s
1564 }
1565 }
1566
1567 let (new_size, new_unconstrained_size) = {
1568 use crate::values::specified::font::QueryFontMetricsFlags;
1569
1570 let builder = &context.builder;
1571 let font = builder.get_font();
1572 let parent_font = builder.get_parent_font();
1573
1574 let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1575
1576 if delta == 0 {
1577 return;
1578 }
1579
1580 let mut min = parent_font.mScriptMinSize;
1581 if font.mXTextScale.text_zoom_enabled() {
1582 min = builder.device.zoom_text(min);
1583 }
1584
1585 let scale = {
1587 let font_metrics = context.query_font_metrics(
1589 FontBaseSize::InheritedStyle,
1590 FontMetricsOrientation::Horizontal,
1591 QueryFontMetricsFlags::NEEDS_MATH_SCALES,
1592 );
1593 scale_factor_for_math_depth_change(
1594 parent_font.mMathDepth as i32,
1595 font.mMathDepth as i32,
1596 font_metrics.script_percent_scale_down,
1597 font_metrics.script_script_percent_scale_down,
1598 )
1599 };
1600
1601 let parent_size = parent_font.mSize.0;
1602 let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1603 let new_size = parent_size.scale_by(scale);
1604 let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1605
1606 if scale <= 1. {
1607 if parent_size <= min {
1612 (parent_size, new_unconstrained_size)
1613 } else {
1614 (min.max(new_size), new_unconstrained_size)
1615 }
1616 } else {
1617 (
1623 new_size.min(new_unconstrained_size.max(min)),
1624 new_unconstrained_size,
1625 )
1626 }
1627 };
1628 let font = context.builder.mutate_font();
1629 font.mFont.size = NonNegative(new_size);
1630 font.mSize = NonNegative(new_size);
1631 font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1632 }
1633
1634 fn init_custom_properties(&mut self, context: &mut computed::Context) {
1637 let is_root_element = context.is_root_element();
1638 let initial_values = self.stylist.get_custom_property_initial_values();
1639 let inherited = if is_root_element {
1640 debug_assert!(context.inherited_custom_properties().is_empty());
1641 initial_values.inherited.clone()
1642 } else {
1643 context.inherited_custom_properties().inherited.clone()
1644 };
1645 let properties = ComputedCustomProperties {
1646 inherited,
1647 non_inherited: initial_values.non_inherited.clone(),
1648 };
1649 context.builder.substitution_functions =
1650 ComputedSubstitutionFunctions::new(Some(properties), None);
1651 }
1652
1653 fn apply_custom_and_prioritary_properties(
1659 &mut self,
1660 context: &mut computed::Context,
1661 decls: &Declarations,
1662 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
1663 attribute_tracker: &mut AttributeTracker,
1664 ) {
1665 if self.may_have_custom_property_cycles {
1666 let stylist = self.stylist;
1667 let seen = std::mem::take(&mut self.seen.custom);
1668 let references = std::mem::take(&mut self.references_from_non_custom_properties);
1669 substitute_all(
1670 &seen,
1671 &references,
1672 stylist,
1673 context,
1674 self,
1675 decls,
1676 shorthand_cache,
1677 attribute_tracker,
1678 );
1679 }
1680 if decls.has_prioritary_properties {
1682 for id in PrioritaryPropertyId::each() {
1683 self.ensure_prioritary_property(
1684 context,
1685 decls,
1686 shorthand_cache,
1687 attribute_tracker,
1688 id,
1689 );
1690 }
1691 }
1692 self.finish_cascade_custom_properties(context);
1693 }
1694
1695 fn ensure_prioritary_property(
1697 &mut self,
1698 context: &mut computed::Context,
1699 decls: &Declarations,
1700 cache: &mut ShorthandsWithPropertyReferencesCache,
1701 attribute_tracker: &mut AttributeTracker,
1702 id: PrioritaryPropertyId,
1703 ) {
1704 if self.ensured_prioritary.contains(id) {
1705 return;
1706 }
1707 self.ensured_prioritary.insert(id);
1708 let deps = id.dependencies();
1709 if !self.ensured_prioritary.contains_all(deps) {
1710 for dep in deps.iter() {
1711 self.ensure_prioritary_property(context, decls, cache, attribute_tracker, dep);
1712 }
1713 }
1714 self.apply_one_prioritary_property(context, decls, cache, id, attribute_tracker);
1715 }
1716
1717 fn cascade_custom_property(
1719 &mut self,
1720 context: &mut computed::Context,
1721 declaration: &'a CustomDeclaration,
1722 priority: CascadePriority,
1723 ) {
1724 let CustomDeclaration {
1725 ref name,
1726 ref value,
1727 } = *declaration;
1728
1729 if let Some(&(reverted_priority, revert_kind)) = self.reverted.custom.get(name) {
1730 if !reverted_priority.allows_when_reverted(&priority, revert_kind) {
1731 return;
1732 }
1733 }
1734
1735 if !(priority.flags() - context.included_cascade_flags).is_empty() {
1736 return;
1737 }
1738
1739 let entry = match self.seen.custom.var.entry(name) {
1740 Entry::Occupied(..) => return,
1741 Entry::Vacant(v) => v,
1742 };
1743
1744 let registration = self.stylist.get_custom_property_registration(name);
1745 let initial_values = self.stylist.get_custom_property_initial_values();
1746 if !Self::value_may_affect_style(context, name, registration, initial_values, value) {
1747 entry.insert(false);
1748 return;
1749 }
1750
1751 let has_references = match value {
1752 CustomDeclarationValue::Unparsed(unparsed_value) => {
1753 unparsed_value
1756 .references
1757 .flags
1758 .intersects(ReferenceFlags::ATTR | ReferenceFlags::VAR)
1759 || !find_non_custom_references(
1760 registration,
1761 unparsed_value,
1762 context.is_root_element(),
1763 )
1764 .is_empty()
1765 },
1766 CustomDeclarationValue::Parsed(..) => false,
1770 CustomDeclarationValue::CSSWideKeyword(..) => false,
1774 };
1775 self.may_have_custom_property_cycles |= has_references;
1776 entry.insert(has_references);
1777
1778 match value {
1779 CustomDeclarationValue::Unparsed(unparsed_value) => {
1780 if !has_references {
1781 substitute_references_if_needed_and_apply(
1785 name,
1786 SubstitutionFunctionKind::Var,
1787 unparsed_value,
1788 self.stylist,
1789 context,
1790 &mut AttributeTracker::new_dummy(),
1792 );
1793 return;
1794 }
1795 let value = ComputedRegisteredValue::universal(Arc::clone(unparsed_value));
1796 context
1797 .builder
1798 .substitution_functions
1799 .insert_var(registration, name, value);
1800 },
1801 CustomDeclarationValue::Parsed(parsed_value) => {
1802 let value = parsed_value.to_computed_value(context);
1803 context
1804 .builder
1805 .substitution_functions
1806 .insert_var(registration, name, value);
1807 },
1808 CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword.revert_kind() {
1809 Some(revert_kind) => {
1810 self.seen.custom.var.remove(name);
1811 self.reverted
1812 .custom
1813 .insert(name.clone(), (priority, revert_kind));
1814 },
1815 None => match keyword {
1816 CSSWideKeyword::Initial => {
1817 debug_assert!(registration.inherits(), "Should've been handled earlier");
1819 remove_and_insert_initial_value(
1820 name,
1821 registration,
1822 &mut context.builder.substitution_functions,
1823 );
1824 },
1825 CSSWideKeyword::Inherit => {
1826 debug_assert!(!registration.inherits(), "Should've been handled earlier");
1828 context
1829 .style()
1830 .add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
1831 let inherited_value = context
1832 .inherited_custom_properties()
1833 .non_inherited
1834 .get(name)
1835 .cloned();
1836 if let Some(inherited_value) = inherited_value {
1837 context.builder.substitution_functions.insert_var(
1838 registration,
1839 name,
1840 inherited_value,
1841 );
1842 }
1843 },
1844 CSSWideKeyword::Revert
1846 | CSSWideKeyword::RevertLayer
1847 | CSSWideKeyword::RevertRule
1848 | CSSWideKeyword::Unset => unreachable!(),
1849 },
1850 },
1851 }
1852 }
1853
1854 #[inline]
1856 pub fn might_have_non_custom_or_attr_dependency(
1857 id: LonghandId,
1858 decl: &PropertyDeclaration,
1859 ) -> bool {
1860 if let PropertyDeclaration::WithVariables(v) = decl {
1861 return matches!(id, LonghandId::LineHeight | LonghandId::FontSize)
1862 || v.value
1863 .variable_value
1864 .references
1865 .flags
1866 .intersects(ReferenceFlags::ATTR);
1867 }
1868 false
1869 }
1870
1871 pub fn maybe_note_non_custom_dependency(
1874 &mut self,
1875 context: &mut computed::Context,
1876 id: LonghandId,
1877 decl: &'a PropertyDeclaration,
1878 attribute_tracker: &mut AttributeTracker,
1879 ) {
1880 debug_assert!(Self::might_have_non_custom_or_attr_dependency(id, decl));
1881 let PropertyDeclaration::WithVariables(v) = decl else {
1882 return;
1883 };
1884 let value = &v.value.variable_value;
1885 let refs = &value.references;
1886
1887 if !refs
1888 .flags
1889 .intersects(ReferenceFlags::VAR | ReferenceFlags::ATTR)
1890 {
1891 return;
1892 }
1893
1894 if refs.flags.intersects(ReferenceFlags::ATTR) {
1897 self.update_attributes_map(context, value, attribute_tracker);
1898 if !refs.flags.intersects(ReferenceFlags::VAR) {
1899 return;
1900 }
1901 }
1902
1903 let references = match id {
1914 LonghandId::FontSize => ReferenceFlags::FONT_UNITS,
1915 LonghandId::LineHeight => ReferenceFlags::LH_UNITS | ReferenceFlags::FONT_UNITS,
1916 LonghandId::ColorScheme => ReferenceFlags::COLOR_SCHEME,
1917 _ => return,
1918 };
1919
1920 references.for_each_non_custom(context.is_root_element(), |idx| {
1921 self.references_from_non_custom_properties[idx]
1922 .get_or_insert_with(Vec::new)
1923 .push(v.value.clone());
1924 });
1925 }
1926
1927 fn value_may_affect_style(
1928 context: &computed::Context,
1929 name: &Name,
1930 registration: &PropertyDescriptors,
1931 initial_values: &ComputedCustomProperties,
1932 value: &CustomDeclarationValue,
1933 ) -> bool {
1934 match *value {
1935 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
1936 if registration.inherits() {
1940 return false;
1941 }
1942 },
1943 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial) => {
1944 if !registration.inherits() {
1947 return false;
1948 }
1949 },
1950 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) => {
1951 return false;
1955 },
1956 _ => {},
1957 }
1958
1959 let existing_value = context
1960 .builder
1961 .substitution_functions
1962 .get_var(registration, name);
1963 let Some(existing_value) = existing_value else {
1964 if matches!(
1965 value,
1966 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial)
1967 ) {
1968 debug_assert!(registration.inherits(), "Should've been handled earlier");
1969 if registration.initial_value.is_none() {
1973 return false;
1974 }
1975 }
1976 return true;
1977 };
1978 match value {
1979 CustomDeclarationValue::Unparsed(value) => {
1980 if let Some(existing_value) = existing_value.as_universal() {
1983 return existing_value != value;
1984 }
1985 },
1986 CustomDeclarationValue::Parsed(..) => {
1987 },
1990 CustomDeclarationValue::CSSWideKeyword(kw) => {
1991 match kw {
1992 CSSWideKeyword::Inherit => {
1993 debug_assert!(!registration.inherits(), "Should've been handled earlier");
1994 if context
1998 .inherited_custom_properties()
1999 .non_inherited
2000 .get(name)
2001 .is_none()
2002 {
2003 return false;
2004 }
2005 },
2006 CSSWideKeyword::Initial => {
2007 debug_assert!(registration.inherits(), "Should've been handled earlier");
2008 if let Some(initial_value) = initial_values.get(registration, name) {
2011 return existing_value != initial_value;
2012 }
2013 },
2014 CSSWideKeyword::Unset => {
2015 debug_assert!(false, "Should've been handled earlier");
2016 },
2017 CSSWideKeyword::Revert
2018 | CSSWideKeyword::RevertLayer
2019 | CSSWideKeyword::RevertRule => {},
2020 }
2021 },
2022 };
2023
2024 true
2025 }
2026
2027 pub fn update_attributes_map(
2029 &mut self,
2030 context: &mut computed::Context,
2031 value: &'a VariableValue,
2032 attribute_tracker: &mut AttributeTracker,
2033 ) {
2034 let refs = &value.references;
2035 if !refs.flags.intersects(ReferenceFlags::ATTR) {
2036 return;
2037 }
2038 self.may_have_custom_property_cycles = true;
2039
2040 for next in &refs.refs {
2041 if !next.is_attr_with_type() || !self.seen.custom.attr.insert(&next.name) {
2042 continue;
2045 }
2046 if let Ok(v) = get_attr_value_for_cycle_resolution(
2047 &next.name,
2048 &next.attribute_data,
2049 &value.url_data,
2050 attribute_tracker,
2051 ) {
2052 context
2053 .builder
2054 .substitution_functions
2055 .insert_attr(&next.name, v);
2056 }
2057 }
2058 }
2059
2060 pub fn finish_cascade_custom_properties(&mut self, context: &mut computed::Context) {
2063 context
2064 .builder
2065 .substitution_functions
2066 .custom_properties
2067 .shrink_to_fit();
2068
2069 let initial_values = self.stylist.get_custom_property_initial_values();
2074 let reuse_inherited = context.inherited_custom_properties().inherited
2075 == context
2076 .builder
2077 .substitution_functions
2078 .custom_properties
2079 .inherited;
2080 if reuse_inherited {
2081 let inherited = context.inherited_custom_properties().inherited.clone();
2082 context
2083 .builder
2084 .substitution_functions
2085 .custom_properties
2086 .inherited = inherited;
2087 }
2088 if initial_values.non_inherited
2089 == context
2090 .builder
2091 .substitution_functions
2092 .custom_properties
2093 .non_inherited
2094 {
2095 let non_inherited = initial_values.non_inherited.clone();
2096 context
2097 .builder
2098 .substitution_functions
2099 .custom_properties
2100 .non_inherited = non_inherited;
2101 }
2102 }
2103}
2104
2105fn substitute_all(
2106 seen: &SeenSubstitutionFunctions,
2107 references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2108 stylist: &Stylist,
2109 computed_context: &mut computed::Context,
2110 cascade: &mut Cascade,
2111 decls: &Declarations,
2112 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
2113 attr_tracker: &mut AttributeTracker,
2114) {
2115 #[derive(Clone, Eq, PartialEq, Debug)]
2122 enum VarType {
2123 Attr(Name),
2124 Custom(Name),
2125 NonCustom(SingleNonCustomReference),
2126 }
2127
2128 #[derive(Debug)]
2130 struct VarInfo {
2131 var: Option<VarType>,
2135 lowlink: usize,
2139 }
2140
2141 #[derive(Debug, Default)]
2142 struct OrderIndexMap {
2143 var: PrecomputedHashMap<Name, usize>,
2145 attr: PrecomputedHashMap<Name, usize>,
2147 }
2148
2149 impl OrderIndexMap {
2150 fn clear(&mut self) {
2151 self.var.clear();
2152 self.attr.clear();
2153 }
2154 }
2155
2156 struct Context<'a, 'b: 'a, 'c, 'd> {
2159 count: usize,
2162 index_map: OrderIndexMap,
2164 non_custom_index_map: NonCustomReferenceMap<usize>,
2166 var_info: SmallVec<[VarInfo; 5]>,
2168 stack: SmallVec<[usize; 5]>,
2171 stylist: &'a Stylist,
2174 computed_context: &'a mut computed::Context<'b>,
2177 cascade: &'a mut Cascade<'c>,
2181 decls: &'a Declarations<'d>,
2183 cache: &'a mut ShorthandsWithPropertyReferencesCache,
2185 }
2186
2187 impl<'a, 'b: 'a, 'c, 'd> Context<'a, 'b, 'c, 'd> {
2188 fn reset(&mut self) {
2189 self.count = 0;
2190 self.index_map.clear();
2191 self.non_custom_index_map = Default::default();
2192 self.var_info.clear();
2193 self.stack.clear();
2194 }
2195
2196 fn map(&self) -> &ComputedSubstitutionFunctions {
2197 &self.computed_context.builder.substitution_functions
2198 }
2199
2200 fn map_mut(&mut self) -> &mut ComputedSubstitutionFunctions {
2201 &mut self.computed_context.builder.substitution_functions
2202 }
2203
2204 fn handle_loop(&mut self, name: &VarType) {
2206 match name {
2207 VarType::Attr(name) => {
2208 self.computed_context
2209 .builder
2210 .substitution_functions
2211 .remove_attr(name);
2212 },
2213 VarType::Custom(name) => {
2214 handle_invalid_at_computed_value_time(
2216 name,
2217 self.stylist.get_custom_property_registration(name),
2218 self.computed_context,
2219 );
2220 },
2221 VarType::NonCustom(non_custom) => {
2222 self.computed_context
2223 .builder
2224 .invalid_non_custom_properties
2225 .insert(non_custom.to_prioritary_id().to_longhand());
2226 },
2227 }
2228 }
2229
2230 fn apply_prioritary_property(
2236 &mut self,
2237 id: PrioritaryPropertyId,
2238 attr_tracker: &mut AttributeTracker,
2239 ) {
2240 self.cascade.ensure_prioritary_property(
2241 self.computed_context,
2242 self.decls,
2243 self.cache,
2244 attr_tracker,
2245 id,
2246 );
2247 }
2248 }
2249
2250 fn visit_value_references<'a, 'b, 'c, 'd>(
2261 var: &VarType,
2262 root: &References,
2263 url_data: &UrlExtraData,
2264 index: usize,
2265 references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2266 context: &mut Context<'a, 'b, 'c, 'd>,
2267 lowlink: &mut usize,
2268 self_ref: &mut bool,
2269 attribute_tracker: &mut AttributeTracker,
2270 non_custom_references: &mut ReferenceFlags,
2271 ) {
2272 let mut refs_stack = SmallVec::<[&References; 5]>::new();
2274 refs_stack.push(root);
2275 while let Some(refs) = refs_stack.pop() {
2276 *non_custom_references |= refs.flags;
2277 for next in &refs.refs {
2278 if next.substitution_kind == SubstitutionFunctionKind::Env {
2279 let device = context.stylist.device();
2283 let present = device
2284 .environment()
2285 .get(&next.name, device, url_data)
2286 .is_some();
2287 if !present {
2288 if let Some(ref fallback) = next.fallback {
2289 refs_stack.push(&fallback.references);
2290 }
2291 }
2292 continue;
2293 }
2294
2295 let next_var = if next.substitution_kind == SubstitutionFunctionKind::Attr {
2296 let can_chain = next.is_attr_with_type() || matches!(var, VarType::Attr(..));
2299 if !can_chain {
2300 continue;
2301 }
2302 if context.map().get_attr(&next.name).is_none() {
2303 if let Ok(val) = get_attr_value_for_cycle_resolution(
2304 &next.name,
2305 &next.attribute_data,
2306 url_data,
2307 attribute_tracker,
2308 ) {
2309 context.map_mut().insert_attr(&next.name, val);
2310 }
2311 }
2312 VarType::Attr(next.name.clone())
2313 } else {
2314 VarType::Custom(next.name.clone())
2315 };
2316
2317 visit_link(
2318 next_var,
2319 index,
2320 references_from_non_custom_properties,
2321 context,
2322 lowlink,
2323 self_ref,
2324 attribute_tracker,
2325 );
2326
2327 let kind = next.substitution_kind;
2330 let resolved = match kind {
2331 SubstitutionFunctionKind::Var => {
2332 let registration =
2333 context.stylist.get_custom_property_registration(&next.name);
2334 context.map().get_var(registration, &next.name)
2335 },
2336 SubstitutionFunctionKind::Attr => context.map().get_attr(&next.name),
2337 SubstitutionFunctionKind::Env => unreachable!("Handled above"),
2338 };
2339 let mut primary_valid = false;
2342 if let Some(resolved) = resolved {
2343 if let Some(v) = resolved.as_universal() {
2344 primary_valid = !v.has_references();
2345 *non_custom_references |= v.references.flags;
2346 } else {
2347 primary_valid = true;
2349 }
2350 }
2351
2352 if !primary_valid {
2353 if let Some(ref fallback) = next.fallback {
2354 refs_stack.push(&fallback.references);
2355 }
2356 }
2357 }
2358 }
2359 }
2360
2361 fn visit_link<'a, 'b, 'c, 'd>(
2364 var: VarType,
2365 index: usize,
2366 non_custom_references: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2367 context: &mut Context<'a, 'b, 'c, 'd>,
2368 lowlink: &mut usize,
2369 self_ref: &mut bool,
2370 attr_tracker: &mut AttributeTracker,
2371 ) {
2372 let next_index = match traverse(var, non_custom_references, context, attr_tracker) {
2373 Some(index) => index,
2374 None => return,
2377 };
2378 let next_info = &context.var_info[next_index];
2379 if next_index > index {
2380 *lowlink = cmp::min(*lowlink, next_info.lowlink);
2384 } else if next_index == index {
2385 *self_ref = true;
2386 } else if next_info.var.is_some() {
2387 *lowlink = cmp::min(*lowlink, next_index);
2390 }
2391 }
2392
2393 fn traverse<'a, 'b, 'c, 'd>(
2412 var: VarType,
2413 references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2414 context: &mut Context<'a, 'b, 'c, 'd>,
2415 attribute_tracker: &mut AttributeTracker,
2416 ) -> Option<usize> {
2417 let mut value_non_custom_refs = ReferenceFlags::empty();
2421 let mut registered = false;
2422 let value = match var {
2423 VarType::Custom(ref name) | VarType::Attr(ref name) => {
2424 let map = &context.computed_context.builder.substitution_functions;
2425 let (registration, value, kind) = if matches!(var, VarType::Custom(..)) {
2426 let registration = context.stylist.get_custom_property_registration(name);
2427 (
2428 registration,
2429 map.get_var(registration, name)?.as_universal()?,
2430 SubstitutionFunctionKind::Var,
2431 )
2432 } else {
2433 (
2435 PropertyDescriptors::unregistered(),
2436 map.get_attr(name)?.as_universal()?,
2437 SubstitutionFunctionKind::Attr,
2438 )
2439 };
2440 let is_root = context.computed_context.is_root_element();
2441 value_non_custom_refs = find_non_custom_references(registration, value, is_root);
2442 registered = !registration.is_universal();
2443 let has_dependency = value
2444 .references
2445 .flags
2446 .intersects(ReferenceFlags::ATTR | ReferenceFlags::VAR)
2447 || !value_non_custom_refs.is_empty();
2448 if !has_dependency {
2450 debug_assert!(
2451 !value.references.flags.intersects(ReferenceFlags::ENV),
2452 "Should've been handled earlier"
2453 );
2454 if kind == SubstitutionFunctionKind::Attr || registered {
2455 let value = value.clone();
2460 substitute_references_if_needed_and_apply(
2461 name,
2462 kind,
2463 &value,
2464 context.stylist,
2465 context.computed_context,
2466 attribute_tracker,
2467 );
2468 }
2469 return None;
2470 }
2471
2472 let index_map = if kind == SubstitutionFunctionKind::Var {
2474 &mut context.index_map.var
2475 } else {
2476 &mut context.index_map.attr
2477 };
2478 match index_map.entry(name.clone()) {
2479 Entry::Occupied(entry) => {
2480 return Some(*entry.get());
2481 },
2482 Entry::Vacant(entry) => {
2483 entry.insert(context.count);
2484 },
2485 }
2486 Some(value.clone())
2489 },
2490 VarType::NonCustom(ref non_custom) => {
2491 let entry = &mut context.non_custom_index_map[*non_custom];
2492 if let Some(v) = entry {
2493 return Some(*v);
2494 }
2495 *entry = Some(context.count);
2496 None
2497 },
2498 };
2499
2500 let index = context.count;
2502 context.count += 1;
2503 debug_assert_eq!(index, context.var_info.len());
2504 context.var_info.push(VarInfo {
2505 var: Some(var.clone()),
2506 lowlink: index,
2507 });
2508 context.stack.push(index);
2509
2510 let mut self_ref = false;
2511 let mut lowlink = index;
2512 if let Some(v) = value.as_ref() {
2513 debug_assert!(
2514 matches!(var, VarType::Custom(_) | VarType::Attr(_)),
2515 "Non-custom property has references?"
2516 );
2517
2518 visit_value_references(
2520 &var,
2521 &v.references,
2522 &v.url_data,
2523 index,
2524 references_from_non_custom_properties,
2525 context,
2526 &mut lowlink,
2527 &mut self_ref,
2528 attribute_tracker,
2529 &mut value_non_custom_refs,
2530 );
2531
2532 let is_root = context.computed_context.is_root_element();
2535 if registered && !value_non_custom_refs.is_empty() {
2536 value_non_custom_refs.for_each_non_custom(is_root, |r| {
2537 visit_link(
2538 VarType::NonCustom(r),
2539 index,
2540 references_from_non_custom_properties,
2541 context,
2542 &mut lowlink,
2543 &mut self_ref,
2544 attribute_tracker,
2545 );
2546 });
2547 }
2548 } else if let VarType::NonCustom(non_custom) = var {
2549 if non_custom == SingleNonCustomReference::LhUnits {
2559 visit_link(
2560 VarType::NonCustom(SingleNonCustomReference::FontUnits),
2561 index,
2562 references_from_non_custom_properties,
2563 context,
2564 &mut lowlink,
2565 &mut self_ref,
2566 attribute_tracker,
2567 );
2568 }
2569 let entry = &references_from_non_custom_properties[non_custom];
2570 if let Some(values) = entry.as_ref() {
2571 for value in values {
2572 let value = &value.variable_value;
2579 visit_value_references(
2580 &var,
2581 &value.references,
2582 &value.url_data,
2583 index,
2584 references_from_non_custom_properties,
2585 context,
2586 &mut lowlink,
2587 &mut self_ref,
2588 attribute_tracker,
2589 &mut Default::default(),
2590 );
2591 }
2592 }
2593 }
2594
2595 context.var_info[index].lowlink = lowlink;
2596 if lowlink != index {
2597 return Some(index);
2603 }
2604
2605 let mut in_loop = self_ref;
2607 loop {
2608 let var_index = context
2609 .stack
2610 .pop()
2611 .expect("The current variable should still be in stack");
2612 let var_info = &mut context.var_info[var_index];
2613 let var_name = var_info
2615 .var
2616 .take()
2617 .expect("Variable should not be popped from stack twice");
2618 if var_index != index {
2619 in_loop = true;
2622 }
2623 if in_loop {
2624 context.handle_loop(&var_name);
2625 }
2626 if var_index == index {
2627 debug_assert_eq!(var_name, var);
2628 break;
2629 }
2630 }
2631
2632 if in_loop {
2633 return None;
2634 }
2635
2636 match var {
2638 VarType::Custom(ref name) | VarType::Attr(ref name) => {
2639 if let Some(ref v) = value {
2640 let kind = if matches!(var, VarType::Custom(..)) {
2641 SubstitutionFunctionKind::Var
2642 } else {
2643 SubstitutionFunctionKind::Attr
2644 };
2645 substitute_references_if_needed_and_apply(
2648 name,
2649 kind,
2650 v,
2651 context.stylist,
2652 context.computed_context,
2653 attribute_tracker,
2654 );
2655 }
2656 },
2657 VarType::NonCustom(non_custom) => {
2658 context.apply_prioritary_property(non_custom.to_prioritary_id(), attribute_tracker);
2659 },
2660 }
2661 None
2663 }
2664
2665 let mut context = Context {
2666 count: 0,
2667 index_map: OrderIndexMap::default(),
2668 non_custom_index_map: NonCustomReferenceMap::default(),
2669 stack: SmallVec::new(),
2670 var_info: SmallVec::new(),
2671 stylist,
2672 computed_context: &mut *computed_context,
2673 cascade: &mut *cascade,
2674 decls,
2675 cache: &mut *shorthand_cache,
2676 };
2677 let mut first = true;
2678 let mut run_one = |var: VarType| {
2679 if !first {
2680 context.reset();
2681 }
2682 first = false;
2683 traverse(
2684 var,
2685 references_from_non_custom_properties,
2686 &mut context,
2687 attr_tracker,
2688 );
2689 };
2690 for (var, has_refs) in &seen.var {
2694 if !has_refs {
2695 continue;
2696 }
2697 run_one(VarType::Custom((*var).clone()));
2698 }
2699 for attr in &seen.attr {
2702 run_one(VarType::Attr((*attr).clone()));
2703 }
2704}