1use crate::FxHashMap;
8use crate::applicable_declarations::{CascadePriority, RevertKind};
9use crate::color::AbsoluteColor;
10use crate::computed_value_flags::ComputedValueFlags;
11use crate::context::TreeCountingCaches;
12use crate::custom_properties::{
13 ComputedCustomProperties, ComputedSubstitutionFunctions, Name, NonCustomReferenceMap,
14 ReferenceFlags, References, SingleNonCustomReference, SubstitutionFunctionKind, VariableValue,
15 get_attr_value_for_cycle_resolution, handle_invalid_at_computed_value_time,
16 remove_and_insert_initial_value, substitute_references_if_needed_and_apply,
17};
18use crate::dom::{AttributeTracker, DummyElementContext, ElementContext, TElement};
19#[cfg(feature = "gecko")]
20use crate::font_metrics::FontMetricsOrientation;
21use crate::properties::{
22 CASCADE_PROPERTY, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, LonghandId,
23 LonghandIdSet, PrioritaryPropertyId, PrioritaryPropertyIdSet, PropertyDeclaration,
24 PropertyDeclarationId, PropertyFlags, ShorthandsWithPropertyReferencesCache, StyleBuilder,
25 property_counts,
26};
27use crate::properties::{CustomDeclaration, CustomDeclarationValue, UnparsedValue};
28use crate::properties_and_values::rule::Descriptors as PropertyDescriptors;
29use crate::properties_and_values::value::ComputedValue as ComputedRegisteredValue;
30use crate::rule_cache::{RuleCache, RuleCacheConditions};
31use crate::rule_tree::{CascadeLevel, CascadeOrigin, RuleCascadeFlags, StrongRuleNode};
32use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet};
33use crate::selector_parser::PseudoElement;
34use crate::shared_lock::StylesheetGuards;
35use crate::style_adjuster::StyleAdjuster;
36use crate::stylesheets::UrlExtraData;
37use crate::stylesheets::container_rule::ContainerSizeQuery;
38use crate::stylesheets::layer_rule::LayerOrder;
39use crate::stylist::Stylist;
40use crate::values::computed::ToComputedValue;
41#[cfg(feature = "gecko")]
42use crate::values::specified::length::FontBaseSize;
43use crate::values::specified::position::PositionTryFallbacksTryTactic;
44use crate::values::{computed, specified};
45use hashbrown::hash_map::{Entry, EntryRef};
46use selectors::matching::ElementSelectorFlags;
47use servo_arc::Arc;
48use smallvec::SmallVec;
49use std::borrow::Cow;
50use std::cmp;
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: 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.map_or(PropertyFlags::empty(), |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 !self.restriction.is_empty() {
174 if let PropertyDeclarationId::Longhand(id) = decl.id()
179 && !id.flags().contains(self.restriction)
180 && self.priority.cascade_level().origin() != CascadeOrigin::UA
181 {
182 continue;
183 }
184 }
185
186 return Some((decl, self.priority));
187 }
188
189 let next_node = self.current_rule_node.take()?.parent()?;
190 self.current_rule_node = Some(next_node);
191 self.update_for_node(next_node);
192 }
193 }
194}
195
196fn cascade_rules<E>(
197 stylist: &Stylist,
198 pseudo: Option<&PseudoElement>,
199 rule_node: &StrongRuleNode,
200 guards: &StylesheetGuards,
201 parent_style: Option<&ComputedValues>,
202 layout_parent_style: Option<&ComputedValues>,
203 first_line_reparenting: FirstLineReparenting,
204 try_tactic: &PositionTryFallbacksTryTactic,
205 cascade_mode: CascadeMode,
206 cascade_input_flags: ComputedValueFlags,
207 included_cascade_flags: RuleCascadeFlags,
208 rule_cache: Option<&RuleCache>,
209 rule_cache_conditions: &mut RuleCacheConditions,
210 element: Option<E>,
211 tree_counting_caches: &mut TreeCountingCaches,
212) -> Arc<ComputedValues>
213where
214 E: TElement,
215{
216 apply_declarations(
217 stylist,
218 pseudo,
219 rule_node,
220 guards,
221 DeclarationIterator::new(rule_node, guards, pseudo),
222 parent_style,
223 layout_parent_style,
224 first_line_reparenting,
225 try_tactic,
226 cascade_mode,
227 cascade_input_flags,
228 included_cascade_flags,
229 rule_cache,
230 rule_cache_conditions,
231 element,
232 tree_counting_caches,
233 )
234}
235
236#[derive(Clone, Copy)]
238pub enum CascadeMode<'a, 'b> {
239 Unvisited {
241 visited_rules: Option<&'a StrongRuleNode>,
243 },
244 Visited {
246 unvisited_context: &'a computed::Context<'b>,
248 },
249}
250
251fn iter_declarations<'c, 'decls: 'c>(
252 iter: impl Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
253 declarations: &mut Declarations<'decls>,
254 mut custom: Option<(&mut Cascade<'c>, &mut computed::Context)>,
255 attribute_tracker: &mut AttributeTracker,
256) {
257 for (declaration, priority) in iter {
258 if let PropertyDeclaration::Custom(ref declaration) = *declaration {
259 if let Some((ref mut cascade, ref mut context)) = custom {
260 cascade.cascade_custom_property(context, declaration, priority);
261 }
262 } else {
263 let id = declaration.id().as_longhand().unwrap();
264 declarations.note_declaration(declaration, priority, id);
265 if Cascade::might_have_non_custom_or_attr_dependency(id, declaration)
266 && let Some((ref mut cascade, ref mut context)) = custom
267 {
268 cascade.maybe_note_non_custom_dependency(
269 context,
270 id,
271 declaration,
272 attribute_tracker,
273 );
274 }
275 }
276 }
277}
278
279pub fn apply_declarations<'decls, E, I>(
282 stylist: &Stylist,
283 pseudo: Option<&PseudoElement>,
284 rules: &StrongRuleNode,
285 guards: &StylesheetGuards,
286 iter: I,
287 parent_style: Option<&ComputedValues>,
288 layout_parent_style: Option<&ComputedValues>,
289 first_line_reparenting: FirstLineReparenting<'_>,
290 try_tactic: &PositionTryFallbacksTryTactic,
291 cascade_mode: CascadeMode,
292 cascade_input_flags: ComputedValueFlags,
293 included_cascade_flags: RuleCascadeFlags,
294 rule_cache: Option<&RuleCache>,
295 rule_cache_conditions: &mut RuleCacheConditions,
296 element: Option<E>,
297 tree_counting_caches: &mut TreeCountingCaches,
298) -> Arc<ComputedValues>
299where
300 E: TElement,
301 I: Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
302{
303 debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
304 let device = stylist.device();
305 let inherited_style = parent_style.unwrap_or(device.default_computed_values());
306 let is_root_element = pseudo.is_none() && element.is_some_and(|e| e.is_root());
307 let container_size_query =
308 ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());
309
310 let originating_element = element.map(|e| e.ultimate_originating_element());
311 let element_context = match originating_element {
312 Some(ref e) => e as &dyn ElementContext,
313 None => &DummyElementContext {},
314 };
315
316 let mut context = computed::Context::new(
317 StyleBuilder::new(
321 device,
322 Some(stylist),
323 parent_style,
324 pseudo,
325 Some(rules.clone()),
326 is_root_element,
327 ),
328 stylist.quirks_mode(),
329 rule_cache_conditions,
330 container_size_query,
331 included_cascade_flags,
332 element_context,
333 tree_counting_caches,
334 );
335
336 context.style().add_flags(cascade_input_flags);
337 context
340 .style()
341 .add_flags(stylist.get_custom_property_initial_values_flags());
342
343 let using_cached_reset_properties;
344 let ignore_colors = context.builder.device.forced_colors().is_active();
345 let mut cascade = Cascade::new(first_line_reparenting, stylist, ignore_colors);
346 let mut declarations = Default::default();
347 let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
348 let mut attribute_tracker = AttributeTracker::new(element_context);
349
350 let properties_to_apply = match cascade_mode {
351 CascadeMode::Visited { unvisited_context } => {
352 context.builder.substitution_functions =
353 unvisited_context.builder.substitution_functions.clone();
354 context.builder.writing_mode = unvisited_context.builder.writing_mode;
355 context.builder.color_scheme = unvisited_context.builder.color_scheme;
356 using_cached_reset_properties = false;
360 if unvisited_context.builder.rules.as_ref() != Some(rules)
364 || unvisited_context
365 .builder
366 .flags()
367 .intersects(ComputedValueFlags::USES_VISITED_DEPENDENT_PROPERTIES)
368 {
369 iter_declarations(iter, &mut declarations, None, &mut attribute_tracker);
370 }
371 LonghandIdSet::visited_dependent()
372 },
373 CascadeMode::Unvisited { .. } => {
374 cascade.init_custom_properties(&mut context);
375 iter_declarations(
376 iter,
377 &mut declarations,
378 Some((&mut cascade, &mut context)),
379 &mut attribute_tracker,
380 );
381 cascade.apply_custom_and_prioritary_properties(
386 &mut context,
387 &declarations,
388 &mut shorthand_cache,
389 &mut attribute_tracker,
390 );
391
392 using_cached_reset_properties =
393 cascade.try_to_use_cached_reset_properties(&mut context, rule_cache, guards);
394
395 if using_cached_reset_properties {
396 LonghandIdSet::late_group_only_inherited()
397 } else {
398 LonghandIdSet::late_group()
399 }
400 },
401 };
402
403 cascade.apply_non_prioritary_properties(
404 &mut context,
405 &declarations.longhand_declarations,
406 &mut shorthand_cache,
407 properties_to_apply,
408 &mut attribute_tracker,
409 );
410
411 context.builder.attribute_references = attribute_tracker.finalize();
412
413 cascade.finished_applying_properties(&mut context.builder);
414
415 context.builder.clear_modified_reset();
416
417 if let CascadeMode::Unvisited { visited_rules } = cascade_mode {
418 if let Some(visited_rules) = visited_rules {
420 cascade.compute_visited_style_if_needed(
421 &mut context,
422 element,
423 parent_style,
424 layout_parent_style,
425 try_tactic,
426 visited_rules,
427 guards,
428 );
429 }
430
431 StyleAdjuster::new(&mut context.builder).adjust(
432 layout_parent_style.unwrap_or(inherited_style),
433 element,
434 try_tactic,
435 &cascade.author_or_user_specified,
436 );
437 }
438
439 if context.builder.modified_reset() || using_cached_reset_properties {
440 context.rule_cache_conditions.borrow_mut().set_uncacheable();
446 }
447
448 if context
449 .builder
450 .flags()
451 .intersects(ComputedValueFlags::tree_counting_function_flags())
452 {
453 if let Some(el) = element {
454 el.apply_selector_flags(ElementSelectorFlags::MAY_HAVE_TREE_COUNTING_FUNCTION);
455 } else {
456 debug_assert!(
457 false,
458 "Tree counting function flag applied without an element?"
459 );
460 }
461 }
462
463 context.builder.build()
464}
465
466type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
472
473fn is_base_appearance(context: &computed::Context) -> bool {
474 use computed::Appearance;
475 let box_style = context.builder.get_box();
476 match *box_style.get_appearance() {
477 Appearance::BaseSelect => {
478 matches!(
479 box_style.get__moz_default_appearance(),
480 Appearance::Listbox | Appearance::Menulist
481 )
482 },
483 Appearance::Base => *box_style.get__moz_default_appearance() != Appearance::None,
484 _ => false,
485 }
486}
487
488fn tweak_when_ignoring_colors(
489 context: &computed::Context,
490 longhand_id: LonghandId,
491 origin: CascadeOrigin,
492 declaration: &mut Cow<PropertyDeclaration>,
493 declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
494) {
495 use crate::values::computed::ToComputedValue;
496 use crate::values::specified::Color;
497
498 if !longhand_id.ignored_when_document_colors_disabled() {
499 return;
500 }
501
502 let is_ua_or_user_rule = matches!(origin, CascadeOrigin::User | CascadeOrigin::UA);
503 if is_ua_or_user_rule {
504 return;
505 }
506
507 let forced = *context
509 .builder
510 .get_inherited_text()
511 .get_forced_color_adjust();
512 if forced == computed::ForcedColorAdjust::None {
513 return;
514 }
515
516 fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
517 color
519 .to_computed_value(context)
520 .resolve_to_absolute(&AbsoluteColor::BLACK)
521 .alpha
522 }
523
524 match **declaration {
526 PropertyDeclaration::CSSWideKeyword(..) => return,
528 PropertyDeclaration::BackgroundColor(ref color) => {
529 if color.honored_in_forced_colors_mode(context, true) {
539 return;
540 }
541 let alpha = alpha_channel(color, context);
545 if alpha == 0.0 {
546 return;
547 }
548 let mut color = context.builder.device.default_background_color();
549 color.alpha = alpha;
550 declarations_to_apply_unless_overridden
551 .push(PropertyDeclaration::BackgroundColor(color.into()))
552 },
553 PropertyDeclaration::Color(ref color) => {
554 if color
556 .0
557 .honored_in_forced_colors_mode(context, true)
558 {
559 return;
560 }
561 if context
565 .builder
566 .get_parent_inherited_text()
567 .get_color()
568 .alpha
569 == 0.0
570 {
571 let color = context.builder.device.default_color();
572 declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
573 specified::ColorPropertyValue(color.into()),
574 ))
575 }
576 },
577 #[cfg(feature = "gecko")]
579 PropertyDeclaration::BackgroundImage(ref bkg) => {
580 use crate::values::generics::image::Image;
581 if crate::pref!("browser.display.permit_backplate")
582 && bkg
583 .0
584 .iter()
585 .all(|image| matches!(*image, Image::Url(..) | Image::None))
586 {
587 return;
588 }
589 },
590 _ => {
591 if let Some(color) = declaration.color_value()
604 && color
605 .honored_in_forced_colors_mode(context, false)
606 {
607 return;
608 }
609 },
610 }
611
612 *declaration.to_mut() =
613 PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
614}
615
616type DeclarationIndex = u16;
618
619#[derive(Copy, Clone)]
624struct PrioritaryDeclarationPosition {
625 most_important: DeclarationIndex,
627 least_important: DeclarationIndex,
628}
629
630impl Default for PrioritaryDeclarationPosition {
631 fn default() -> Self {
632 Self {
633 most_important: DeclarationIndex::MAX,
634 least_important: DeclarationIndex::MAX,
635 }
636 }
637}
638
639#[derive(Copy, Clone)]
640struct Declaration<'a> {
641 decl: &'a PropertyDeclaration,
642 priority: CascadePriority,
643 next_index: DeclarationIndex,
644}
645
646#[derive(Default)]
648pub(crate) struct Declarations<'a> {
649 has_prioritary_properties: bool,
651 longhand_declarations: SmallVec<[Declaration<'a>; 64]>,
653 prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
655}
656
657impl<'a> Declarations<'a> {
658 fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
659 let new_index = self.longhand_declarations.len();
660 if new_index >= DeclarationIndex::MAX as usize {
661 return;
664 }
665
666 self.has_prioritary_properties = true;
667 let new_index = new_index as DeclarationIndex;
668 let position = &mut self.prioritary_positions[id as usize];
669 if position.most_important == DeclarationIndex::MAX {
670 position.most_important = new_index;
673 } else {
674 self.longhand_declarations[position.least_important as usize].next_index = new_index;
676 }
677 position.least_important = new_index;
678 }
679
680 fn note_declaration(
681 &mut self,
682 decl: &'a PropertyDeclaration,
683 priority: CascadePriority,
684 id: LonghandId,
685 ) {
686 if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
687 self.note_prioritary_property(id);
688 }
689 self.longhand_declarations.push(Declaration {
690 decl,
691 priority,
692 next_index: 0,
693 });
694 }
695}
696
697#[derive(Default)]
698struct RevertedSet {
699 longhands_set: LonghandIdSet,
701 longhands: FxHashMap<LonghandId, (CascadePriority, RevertKind)>,
702 custom: PrecomputedHashMap<Name, (CascadePriority, RevertKind)>,
703}
704
705#[derive(Default)]
706struct SeenSubstitutionFunctions<'a> {
707 var: PrecomputedHashMap<&'a Name, bool>,
710 attr: PrecomputedHashSet<&'a Name>,
711}
712
713#[derive(Default)]
714struct SeenSet<'a> {
715 longhands: LonghandIdSet,
716 custom: SeenSubstitutionFunctions<'a>,
717}
718
719fn find_non_custom_references(
721 registration: &PropertyDescriptors,
722 value: &VariableValue,
723 is_root_element: bool,
724) -> ReferenceFlags {
725 use crate::properties_and_values::syntax::data_type::DependentDataTypes;
726
727 let mut result = ReferenceFlags::empty();
728 let Some(syntax) = registration.syntax.as_ref() else {
729 return result;
730 };
731 let dependent_types = syntax.dependent_types();
732 let may_reference_length = dependent_types.intersects(DependentDataTypes::LENGTH);
733 if may_reference_length {
734 result |= value.references.non_custom_references(is_root_element);
735 }
736 if dependent_types.intersects(DependentDataTypes::COLOR) {
737 result |= ReferenceFlags::COLOR_SCHEME;
740 }
741 result
742}
743
744pub struct KeyframeCustomPropertiesBuilder<'a> {
753 cascade: Cascade<'a>,
754 decls: Declarations<'a>,
755 shorthand_cache: ShorthandsWithPropertyReferencesCache,
756}
757
758impl<'a> KeyframeCustomPropertiesBuilder<'a> {
759 pub fn new(
762 stylist: &'a Stylist,
763 context: &mut computed::Context,
764 base: ComputedCustomProperties,
765 ) -> Self {
766 context.builder.substitution_functions =
767 ComputedSubstitutionFunctions::new(Some(base), None);
768 Self {
769 cascade: Cascade::new_for_custom_properties_only(stylist),
770 decls: Declarations::default(),
771 shorthand_cache: ShorthandsWithPropertyReferencesCache::default(),
772 }
773 }
774
775 pub fn cascade(
777 &mut self,
778 context: &mut computed::Context,
779 declaration: &'a CustomDeclaration,
780 priority: CascadePriority,
781 ) {
782 self.cascade
783 .cascade_custom_property(context, declaration, priority);
784 }
785
786 pub fn build(
788 mut self,
789 context: &mut computed::Context,
790 attribute_tracker: &mut AttributeTracker,
791 ) {
792 self.cascade.apply_custom_and_prioritary_properties(
793 context,
794 &self.decls,
795 &mut self.shorthand_cache,
796 attribute_tracker,
797 );
798 }
799}
800
801pub(crate) struct Cascade<'a> {
802 first_line_reparenting: FirstLineReparenting<'a>,
803 stylist: &'a Stylist,
804 ignore_colors: bool,
805 seen: SeenSet<'a>,
806 reverted: RevertedSet,
807 author_or_user_specified: LonghandIdSet,
808 declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
809 may_have_custom_property_cycles: bool,
810 references_from_non_custom_properties: NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
811 ensured_prioritary: PrioritaryPropertyIdSet,
813}
814
815impl<'a> Cascade<'a> {
816 fn new(
817 first_line_reparenting: FirstLineReparenting<'a>,
818 stylist: &'a Stylist,
819 ignore_colors: bool,
820 ) -> Self {
821 Self {
822 first_line_reparenting,
823 stylist,
824 ignore_colors,
825 seen: Default::default(),
826 author_or_user_specified: Default::default(),
827 reverted: Default::default(),
828 declarations_to_apply_unless_overridden: Default::default(),
829 may_have_custom_property_cycles: false,
830 ensured_prioritary: PrioritaryPropertyIdSet::default(),
831 references_from_non_custom_properties: Default::default(),
832 }
833 }
834
835 fn new_for_custom_properties_only(stylist: &'a Stylist) -> Self {
838 Self {
839 first_line_reparenting: FirstLineReparenting::No,
840 stylist,
841 ignore_colors: false,
842 seen: Default::default(),
843 author_or_user_specified: Default::default(),
844 reverted: Default::default(),
845 declarations_to_apply_unless_overridden: Default::default(),
846 may_have_custom_property_cycles: false,
847 ensured_prioritary: PrioritaryPropertyIdSet::default(),
848 references_from_non_custom_properties: Default::default(),
849 }
850 }
851
852 fn substitute_variables_if_needed<'cache, 'decl>(
853 &self,
854 context: &mut computed::Context,
855 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
856 declaration: &'decl PropertyDeclaration,
857 attribute_tracker: &mut AttributeTracker,
858 ) -> Cow<'decl, PropertyDeclaration>
859 where
860 'cache: 'decl,
861 {
862 let declaration = match *declaration {
863 PropertyDeclaration::WithVariables(ref declaration) => declaration,
864 ref d => return Cow::Borrowed(d),
865 };
866
867 if !declaration.id.inherited() {
868 context.rule_cache_conditions.borrow_mut().set_uncacheable();
869
870 if matches!(declaration.id, LonghandId::Display | LonghandId::Content) {
876 context
877 .builder
878 .add_flags(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE);
879 }
880 }
881
882 debug_assert!(
883 context.builder.stylist.is_some(),
884 "Need a Stylist to substitute variables!"
885 );
886 declaration.value.substitute_variables(
887 declaration.id,
888 context.builder.substitution_functions(),
889 context.builder.stylist.unwrap(),
890 context,
891 shorthand_cache,
892 attribute_tracker,
893 )
894 }
895
896 fn apply_one_prioritary_property(
897 &mut self,
898 context: &mut computed::Context,
899 decls: &Declarations,
900 cache: &mut ShorthandsWithPropertyReferencesCache,
901 id: PrioritaryPropertyId,
902 attr_provider: &mut AttributeTracker,
903 ) {
904 let mut index = decls.prioritary_positions[id as usize].most_important;
905 if index == DeclarationIndex::MAX {
906 return;
907 }
908
909 let longhand_id = id.to_longhand();
910 debug_assert!(
911 !longhand_id.is_logical(),
912 "That could require more book-keeping"
913 );
914 loop {
915 let decl = decls.longhand_declarations[index as usize];
916 self.apply_one_longhand(
917 context,
918 longhand_id,
919 decl.decl,
920 decl.priority,
921 cache,
922 attr_provider,
923 );
924 if self.seen.longhands.contains(longhand_id) {
925 self.did_apply_prioritary_property(context, id);
927 return;
928 }
929 debug_assert!(
930 decl.next_index == 0 || decl.next_index > index,
931 "should make progress! {} -> {}",
932 index,
933 decl.next_index,
934 );
935 index = decl.next_index;
936 if index == 0 {
937 return;
938 }
939 }
940 }
941
942 fn did_apply_prioritary_property(
944 &mut self,
945 context: &mut computed::Context,
946 id: PrioritaryPropertyId,
947 ) {
948 use crate::properties::PrioritaryPropertyId::*;
949 match id {
950 Appearance => {
951 if is_base_appearance(context) {
952 context
953 .style()
954 .add_flags(ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE);
955 context
956 .included_cascade_flags
957 .insert(RuleCascadeFlags::APPEARANCE_BASE);
958 }
959 },
960 WritingMode | Direction | TextOrientation => {
961 context.builder.writing_mode =
962 crate::logical_geometry::WritingMode::new(context.builder.get_inherited_box());
963 },
964 Zoom => {
965 context.builder.recompute_effective_zooms();
966 if !context.builder.effective_zoom_for_inheritance.is_one() {
967 self.recompute_font_size_for_zoom_change(&mut context.builder);
973 }
974 },
975 XLang => {
976 #[cfg(feature = "gecko")]
977 self.recompute_initial_font_family_if_needed(&mut context.builder);
978 self.recompute_keyword_font_size_if_needed(context);
979 },
980 FontFamily => {
981 #[cfg(feature = "gecko")]
982 self.prioritize_user_fonts_if_needed(&mut context.builder);
983 self.recompute_keyword_font_size_if_needed(context);
984 },
985 FontSize => {
986 if self.seen.longhands.contains(LonghandId::MathDepth) {
987 #[cfg(feature = "gecko")]
988 Self::recompute_math_font_size_if_needed(context);
989 }
990 if self.seen.longhands.contains(LonghandId::XLang)
991 || self.seen.longhands.contains(LonghandId::FontFamily)
992 {
993 self.recompute_keyword_font_size_if_needed(context);
994 }
995 #[cfg(feature = "gecko")]
996 self.constrain_font_size_if_needed(&mut context.builder);
997 },
998 XTextScale => {
999 #[cfg(feature = "gecko")]
1000 self.unzoom_fonts_if_needed(&mut context.builder);
1001 },
1002 MozMinFontSizeRatio => {
1003 #[cfg(feature = "gecko")]
1004 self.constrain_font_size_if_needed(&mut context.builder);
1005 },
1006 ColorScheme => {
1007 context.builder.color_scheme =
1008 context.builder.get_inherited_ui().color_scheme_bits();
1009 },
1010 MozDefaultAppearance | MathDepth | FontWeight | FontWidth | FontStyle
1011 | FontSizeAdjust | ForcedColorAdjust | LineHeight => {},
1012 }
1013 }
1014
1015 fn apply_non_prioritary_properties(
1016 &mut self,
1017 context: &mut computed::Context,
1018 longhand_declarations: &[Declaration],
1019 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
1020 properties_to_apply: &LonghandIdSet,
1021 attribute_tracker: &mut AttributeTracker,
1022 ) {
1023 debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
1024 debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
1025 for declaration in longhand_declarations {
1026 let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
1027 if !properties_to_apply.contains(longhand_id) {
1028 continue;
1029 }
1030 debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
1031 let is_logical = longhand_id.is_logical();
1032 if is_logical {
1033 let wm = context.builder.writing_mode;
1034 context
1035 .rule_cache_conditions
1036 .borrow_mut()
1037 .set_writing_mode_dependency(wm);
1038 longhand_id = longhand_id.to_physical(wm);
1039 }
1040 self.apply_one_longhand(
1041 context,
1042 longhand_id,
1043 declaration.decl,
1044 declaration.priority,
1045 shorthand_cache,
1046 attribute_tracker,
1047 );
1048 }
1049 if !self.declarations_to_apply_unless_overridden.is_empty() {
1050 debug_assert!(self.ignore_colors);
1051 for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
1052 let longhand_id = declaration.id().as_longhand().unwrap();
1053 debug_assert!(!longhand_id.is_logical());
1054 if !self.seen.longhands.contains(longhand_id) {
1055 unsafe {
1056 self.do_apply_declaration(context, longhand_id, &declaration);
1057 }
1058 }
1059 }
1060 }
1061
1062 if !context.builder.effective_zoom_for_inheritance.is_one() {
1063 self.recompute_zoom_dependent_inherited_lengths(context);
1064 }
1065 }
1066
1067 #[cold]
1068 fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {
1069 debug_assert!(self.seen.longhands.contains(LonghandId::Zoom));
1070 for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {
1071 if self.seen.longhands.contains(prop) {
1072 continue;
1073 }
1074 let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);
1075 unsafe {
1076 self.do_apply_declaration(context, prop, &declaration);
1077 }
1078 }
1079 }
1080
1081 fn apply_one_longhand(
1082 &mut self,
1083 context: &mut computed::Context,
1084 longhand_id: LonghandId,
1085 declaration: &PropertyDeclaration,
1086 priority: CascadePriority,
1087 cache: &mut ShorthandsWithPropertyReferencesCache,
1088 attribute_tracker: &mut AttributeTracker,
1089 ) {
1090 debug_assert!(!longhand_id.is_logical());
1091 if self.seen.longhands.contains(longhand_id) {
1092 return;
1093 }
1094
1095 if !(priority.flags() - context.included_cascade_flags).is_empty() {
1096 return;
1097 }
1098
1099 if self.reverted.longhands_set.contains(longhand_id)
1100 && let Some(&(reverted_priority, revert_kind)) =
1101 self.reverted.longhands.get(&longhand_id)
1102 && !reverted_priority.allows_when_reverted(&priority, revert_kind)
1103 {
1104 return;
1105 }
1106
1107 let mut declaration =
1108 self.substitute_variables_if_needed(context, cache, declaration, attribute_tracker);
1109
1110 let origin = priority.cascade_level().origin();
1113 if self.ignore_colors {
1114 tweak_when_ignoring_colors(
1115 context,
1116 longhand_id,
1117 origin,
1118 &mut declaration,
1119 &mut self.declarations_to_apply_unless_overridden,
1120 );
1121 }
1122 let can_skip_apply = match declaration.get_css_wide_keyword() {
1123 Some(keyword) => {
1124 if let Some(revert_kind) = keyword.revert_kind() {
1125 self.reverted.longhands_set.insert(longhand_id);
1129 self.reverted
1130 .longhands
1131 .insert(longhand_id, (priority, revert_kind));
1132 return;
1133 }
1134
1135 let inherited = longhand_id.inherited();
1136 let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()
1137 && longhand_id.zoom_dependent();
1138 match keyword {
1139 CSSWideKeyword::Revert
1140 | CSSWideKeyword::RevertLayer
1141 | CSSWideKeyword::RevertRule => unreachable!(),
1142 CSSWideKeyword::Unset => !zoomed || !inherited,
1143 CSSWideKeyword::Inherit => inherited && !zoomed,
1144 CSSWideKeyword::Initial => !inherited,
1145 }
1146 },
1147 None => false,
1148 };
1149
1150 self.seen.longhands.insert(longhand_id);
1151 if origin != CascadeOrigin::UA {
1152 self.author_or_user_specified.insert(longhand_id);
1153 }
1154
1155 if !can_skip_apply {
1156 let old_scope = context.scope;
1160 let cascade_level = priority.cascade_level();
1161 context.scope = cascade_level;
1162 unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
1163 context.scope = old_scope;
1164 }
1165 }
1166
1167 #[inline]
1168 unsafe fn do_apply_declaration(
1169 &self,
1170 context: &mut computed::Context,
1171 longhand_id: LonghandId,
1172 declaration: &PropertyDeclaration,
1173 ) {
1174 debug_assert!(!longhand_id.is_logical());
1175 unsafe {
1176 (CASCADE_PROPERTY[longhand_id as usize])(declaration, context);
1182 }
1183 }
1184
1185 #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)]
1189 fn compute_visited_style_if_needed<E>(
1190 &self,
1191 context: &mut computed::Context,
1192 element: Option<E>,
1193 parent_style: Option<&ComputedValues>,
1194 layout_parent_style: Option<&ComputedValues>,
1195 try_tactic: &PositionTryFallbacksTryTactic,
1196 visited_rules: &StrongRuleNode,
1197 guards: &StylesheetGuards,
1198 ) where
1199 E: TElement,
1200 {
1201 let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
1202
1203 macro_rules! visited_parent {
1204 ($parent:expr) => {
1205 if is_link {
1206 $parent
1207 } else {
1208 $parent.map(|p| p.visited_style().unwrap_or(p))
1209 }
1210 };
1211 }
1212
1213 let style = cascade_rules(
1216 context.builder.stylist.unwrap(),
1217 context.builder.pseudo,
1218 visited_rules,
1219 guards,
1220 visited_parent!(parent_style),
1221 visited_parent!(layout_parent_style),
1222 self.first_line_reparenting,
1223 try_tactic,
1224 CascadeMode::Visited {
1225 unvisited_context: &*context,
1226 },
1227 Default::default(),
1230 context.included_cascade_flags,
1231 None, &mut *context.rule_cache_conditions.borrow_mut(),
1237 element,
1238 &mut *context.tree_counting_caches.borrow_mut(),
1239 );
1240 context.builder.visited_style = Some(style);
1241 }
1242
1243 fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
1244 #[cfg(feature = "gecko")]
1245 {
1246 if let Some(bg) = builder.get_background_if_mutated() {
1247 bg.fill_arrays();
1248 }
1249
1250 if let Some(svg) = builder.get_svg_if_mutated() {
1251 svg.fill_arrays();
1252 }
1253 }
1254
1255 if self
1258 .seen
1259 .longhands
1260 .contains_any(LonghandIdSet::visited_dependent())
1261 {
1262 builder.add_flags(ComputedValueFlags::USES_VISITED_DEPENDENT_PROPERTIES);
1263 }
1264
1265 if self
1266 .author_or_user_specified
1267 .contains_any(LonghandIdSet::border_background_properties())
1268 {
1269 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_OR_USER_SPECIFIED_BORDER_BACKGROUND);
1270 }
1271
1272 if self.author_or_user_specified.contains(LonghandId::Color) {
1273 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_OR_USER_SPECIFIED_TEXT_COLOR);
1274 }
1275
1276 if self
1277 .author_or_user_specified
1278 .contains(LonghandId::TextShadow)
1279 {
1280 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_OR_USER_SPECIFIED_TEXT_SHADOW);
1281 }
1282
1283 if self
1284 .author_or_user_specified
1285 .contains(LonghandId::GridAutoFlow)
1286 {
1287 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_OR_USER_SPECIFIED_GRID_AUTO_FLOW);
1288 }
1289
1290 #[cfg(feature = "servo")]
1291 {
1292 if let Some(font) = builder.get_font_if_mutated() {
1293 font.compute_font_hash();
1294 }
1295 }
1296 }
1297
1298 fn try_to_use_cached_reset_properties(
1299 &self,
1300 context: &mut computed::Context<'a>,
1301 cache: Option<&'a RuleCache>,
1302 guards: &StylesheetGuards,
1303 ) -> bool {
1304 let style = match self.first_line_reparenting {
1305 FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
1306 FirstLineReparenting::No => {
1307 let Some(cache) = cache else { return false };
1308 let Some(style) = cache.find(guards, context) else {
1309 return false;
1310 };
1311 style
1312 },
1313 };
1314
1315 context.builder.copy_reset_from(style);
1316
1317 let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_OR_USER_SPECIFIED_BORDER_BACKGROUND
1329 | ComputedValueFlags::HAS_AUTHOR_OR_USER_SPECIFIED_GRID_AUTO_FLOW
1330 | ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS
1331 | ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS
1332 | ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE
1333 | ComputedValueFlags::USES_CONTAINER_UNITS
1334 | ComputedValueFlags::USES_VIEWPORT_UNITS
1335 | ComputedValueFlags::USES_FONT_OR_WM_RELATIVE_UNITS
1336 | ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY
1337 | ComputedValueFlags::USES_SIBLING_COUNT
1338 | ComputedValueFlags::USES_SIBLING_INDEX
1339 | ComputedValueFlags::USES_VISITED_DEPENDENT_PROPERTIES
1340 | ComputedValueFlags::USES_ELEMENT_SCOPED_RANDOM;
1341 context.builder.add_flags(style.flags & bits_to_copy);
1342
1343 true
1344 }
1345
1346 #[inline]
1349 #[cfg(feature = "gecko")]
1350 fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
1351 use crate::gecko_bindings::bindings;
1352 use crate::values::computed::font::FontFamily;
1353
1354 let default_font_type = {
1355 let font = builder.get_font();
1356
1357 if !font.mFont.family.is_initial {
1358 return;
1359 }
1360
1361 let default_font_type = unsafe {
1362 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1363 builder.device.document(),
1364 font.mLanguage.0.as_ptr(),
1365 )
1366 };
1367
1368 let initial_generic = font.mFont.family.families.single_generic();
1369 debug_assert!(
1370 initial_generic.is_some(),
1371 "Initial font should be just one generic font"
1372 );
1373 if initial_generic == Some(default_font_type) {
1374 return;
1375 }
1376
1377 default_font_type
1378 };
1379
1380 builder.mutate_font().mFont.family.families =
1382 FontFamily::generic(default_font_type).families.clone();
1383 }
1384
1385 #[inline]
1387 #[cfg(feature = "gecko")]
1388 fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1389 use crate::gecko_bindings::bindings;
1390
1391 if crate::pref!("browser.display.use_document_fonts") != 0
1394 || builder.device.chrome_rules_enabled_for_document()
1395 {
1396 return;
1397 }
1398
1399 let default_font_type = {
1400 let font = builder.get_font();
1401
1402 if font.mFont.family.is_system_font {
1403 return;
1404 }
1405
1406 if !font.mFont.family.families.needs_user_font_prioritization() {
1407 return;
1408 }
1409
1410 unsafe {
1411 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1412 builder.device.document(),
1413 font.mLanguage.0.as_ptr(),
1414 )
1415 }
1416 };
1417
1418 let font = builder.mutate_font();
1419 font.mFont
1420 .family
1421 .families
1422 .prioritize_first_generic_or_prepend(default_font_type);
1423 }
1424
1425 fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
1427 use crate::values::computed::ToComputedValue;
1428
1429 if !self.seen.longhands.contains(LonghandId::XLang)
1430 && !self.seen.longhands.contains(LonghandId::FontFamily)
1431 {
1432 return;
1433 }
1434
1435 let new_size = {
1436 let font = context.builder.get_font();
1437 let info = font.slow_clone_font_size().keyword_info;
1438 let new_size = match info.kw {
1439 specified::FontSizeKeyword::None => return,
1440 _ => {
1441 context.for_non_inherited_property = false;
1442 specified::FontSize::Keyword(info).to_computed_value(context)
1443 },
1444 };
1445
1446 #[cfg(feature = "gecko")]
1447 if font.mScriptUnconstrainedSize == new_size.computed_size {
1448 return;
1449 }
1450
1451 new_size
1452 };
1453
1454 context.builder.mutate_font().set_font_size(new_size);
1455 }
1456
1457 #[cfg(feature = "gecko")]
1460 fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
1461 use crate::gecko_bindings::bindings;
1462 use crate::values::generics::NonNegative;
1463
1464 let min_font_size = {
1465 let font = builder.get_font();
1466 let min_font_size = unsafe {
1467 bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
1468 };
1469
1470 if font.mFont.size.0 >= min_font_size {
1471 return;
1472 }
1473
1474 NonNegative(min_font_size)
1475 };
1476
1477 builder.mutate_font().mFont.size = min_font_size;
1478 }
1479
1480 #[cfg(feature = "gecko")]
1484 fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1485 debug_assert!(self.seen.longhands.contains(LonghandId::XTextScale));
1486
1487 let parent_text_scale = *builder.get_parent_font().get__x_text_scale();
1488 let text_scale = *builder.get_font().get__x_text_scale();
1489 if parent_text_scale == text_scale {
1490 return;
1491 }
1492 debug_assert_ne!(
1493 parent_text_scale.text_zoom_enabled(),
1494 text_scale.text_zoom_enabled(),
1495 "There's only one value that disables it"
1496 );
1497 debug_assert!(
1498 !text_scale.text_zoom_enabled(),
1499 "We only ever disable text zoom never enable it"
1500 );
1501 let device = builder.device;
1502 builder.mutate_font().unzoom_fonts(device);
1503 }
1504
1505 fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
1506 debug_assert!(self.seen.longhands.contains(LonghandId::Zoom));
1507 let old_size = builder.get_font().slow_clone_font_size();
1510 let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);
1511 if old_size == new_size {
1512 return;
1513 }
1514 builder.mutate_font().set_font_size(new_size);
1515 }
1516
1517 #[cfg(feature = "gecko")]
1522 fn recompute_math_font_size_if_needed(context: &mut computed::Context) {
1523 use crate::values::generics::NonNegative;
1524
1525 if context
1527 .builder
1528 .get_font()
1529 .slow_clone_font_size()
1530 .keyword_info
1531 .kw
1532 != specified::FontSizeKeyword::Math
1533 {
1534 return;
1535 }
1536
1537 const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
1538
1539 fn scale_factor_for_math_depth_change(
1549 parent_math_depth: i32,
1550 computed_math_depth: i32,
1551 parent_script_percent_scale_down: Option<f32>,
1552 parent_script_script_percent_scale_down: Option<f32>,
1553 ) -> f32 {
1554 let mut a = parent_math_depth;
1555 let mut b = computed_math_depth;
1556 let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
1557 let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or(c);
1558 let scale_between_0_and_2 = parent_script_script_percent_scale_down.unwrap_or(c * c);
1559 let mut s = 1.0;
1560 let mut invert_scale_factor = false;
1561 if a == b {
1562 return s;
1563 }
1564 if b < a {
1565 std::mem::swap(&mut a, &mut b);
1566 invert_scale_factor = true;
1567 }
1568 let mut e = b - a;
1569 if a <= 0 && b >= 2 {
1570 s *= scale_between_0_and_2;
1571 e -= 2;
1572 } else if a == 1 {
1573 s *= scale_between_0_and_2 / scale_between_0_and_1;
1574 e -= 1;
1575 } else if b == 1 {
1576 s *= scale_between_0_and_1;
1577 e -= 1;
1578 }
1579 s *= c.powi(e);
1580 if invert_scale_factor {
1581 1.0 / s.max(f32::MIN_POSITIVE)
1582 } else {
1583 s
1584 }
1585 }
1586
1587 let (new_size, new_unconstrained_size) = {
1588 use crate::values::specified::font::QueryFontMetricsFlags;
1589
1590 let builder = &context.builder;
1591 let font = builder.get_font();
1592 let parent_font = builder.get_parent_font();
1593
1594 let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1595
1596 if delta == 0 {
1597 return;
1598 }
1599
1600 let mut min = parent_font.mScriptMinSize;
1601 if font.mXTextScale.text_zoom_enabled() {
1602 min = builder.device.zoom_text(min);
1603 }
1604
1605 let scale = {
1607 let font_metrics = context.query_font_metrics(
1609 FontBaseSize::InheritedStyle,
1610 FontMetricsOrientation::Horizontal,
1611 QueryFontMetricsFlags::NEEDS_MATH_SCALES,
1612 );
1613 scale_factor_for_math_depth_change(
1614 parent_font.mMathDepth as i32,
1615 font.mMathDepth as i32,
1616 font_metrics.script_percent_scale_down,
1617 font_metrics.script_script_percent_scale_down,
1618 )
1619 };
1620
1621 let parent_size = parent_font.mSize.0;
1622 let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1623 let new_size = parent_size.scale_by(scale);
1624 let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1625
1626 if scale <= 1. {
1627 if parent_size <= min {
1632 (parent_size, new_unconstrained_size)
1633 } else {
1634 (min.max(new_size), new_unconstrained_size)
1635 }
1636 } else {
1637 (
1643 new_size.min(new_unconstrained_size.max(min)),
1644 new_unconstrained_size,
1645 )
1646 }
1647 };
1648 let font = context.builder.mutate_font();
1649 font.mFont.size = NonNegative(new_size);
1650 font.mSize = NonNegative(new_size);
1651 font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1652 }
1653
1654 fn init_custom_properties(&mut self, context: &mut computed::Context) {
1657 let is_root_element = context.is_root_element();
1658 let initial_values = self.stylist.get_custom_property_initial_values();
1659 let inherited = if is_root_element {
1660 debug_assert!(context.inherited_custom_properties().is_empty());
1661 initial_values.inherited.clone()
1662 } else {
1663 context.inherited_custom_properties().inherited.clone()
1664 };
1665 let properties = ComputedCustomProperties {
1666 inherited,
1667 non_inherited: initial_values.non_inherited.clone(),
1668 };
1669 context.builder.substitution_functions =
1670 ComputedSubstitutionFunctions::new(Some(properties), None);
1671 }
1672
1673 fn apply_custom_and_prioritary_properties(
1679 &mut self,
1680 context: &mut computed::Context,
1681 decls: &Declarations,
1682 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
1683 attribute_tracker: &mut AttributeTracker,
1684 ) {
1685 if self.may_have_custom_property_cycles {
1686 let stylist = self.stylist;
1687 let seen = std::mem::take(&mut self.seen.custom);
1688 let references = std::mem::take(&mut self.references_from_non_custom_properties);
1689 substitute_all(
1690 &seen,
1691 &references,
1692 stylist,
1693 context,
1694 self,
1695 decls,
1696 shorthand_cache,
1697 attribute_tracker,
1698 );
1699 }
1700 if decls.has_prioritary_properties {
1702 for id in PrioritaryPropertyId::each() {
1703 self.ensure_prioritary_property(
1704 context,
1705 decls,
1706 shorthand_cache,
1707 attribute_tracker,
1708 id,
1709 );
1710 }
1711 }
1712 self.finish_cascade_custom_properties(context);
1713 }
1714
1715 fn ensure_prioritary_property(
1717 &mut self,
1718 context: &mut computed::Context,
1719 decls: &Declarations,
1720 cache: &mut ShorthandsWithPropertyReferencesCache,
1721 attribute_tracker: &mut AttributeTracker,
1722 id: PrioritaryPropertyId,
1723 ) {
1724 if self.ensured_prioritary.contains(id) {
1725 return;
1726 }
1727 self.ensured_prioritary.insert(id);
1728 let deps = id.dependencies();
1729 if !self.ensured_prioritary.contains_all(deps) {
1730 for dep in deps.iter() {
1731 self.ensure_prioritary_property(context, decls, cache, attribute_tracker, dep);
1732 }
1733 }
1734 self.apply_one_prioritary_property(context, decls, cache, id, attribute_tracker);
1735 }
1736
1737 fn cascade_custom_property(
1739 &mut self,
1740 context: &mut computed::Context,
1741 declaration: &'a CustomDeclaration,
1742 priority: CascadePriority,
1743 ) {
1744 let CustomDeclaration {
1745 ref name,
1746 ref value,
1747 } = *declaration;
1748
1749 if let Some(&(reverted_priority, revert_kind)) = self.reverted.custom.get(name)
1750 && !reverted_priority.allows_when_reverted(&priority, revert_kind)
1751 {
1752 return;
1753 }
1754
1755 if !(priority.flags() - context.included_cascade_flags).is_empty() {
1756 return;
1757 }
1758
1759 let entry = match self.seen.custom.var.entry(name) {
1760 Entry::Occupied(..) => return,
1761 Entry::Vacant(v) => v,
1762 };
1763
1764 let registration = self.stylist.get_custom_property_registration(name);
1765 let initial_values = self.stylist.get_custom_property_initial_values();
1766 if !Self::value_may_affect_style(context, name, registration, initial_values, value) {
1767 entry.insert(false);
1768 return;
1769 }
1770
1771 let has_references = match value {
1772 CustomDeclarationValue::Unparsed(unparsed_value) => {
1773 unparsed_value
1776 .references
1777 .flags
1778 .intersects(ReferenceFlags::ATTR | ReferenceFlags::VAR)
1779 || !find_non_custom_references(
1780 registration,
1781 unparsed_value,
1782 context.is_root_element(),
1783 )
1784 .is_empty()
1785 },
1786 CustomDeclarationValue::Parsed(..) => false,
1790 CustomDeclarationValue::CSSWideKeyword(..) => false,
1794 };
1795 self.may_have_custom_property_cycles |= has_references;
1796 entry.insert(has_references);
1797
1798 match value {
1799 CustomDeclarationValue::Unparsed(unparsed_value) => {
1800 if !has_references {
1801 substitute_references_if_needed_and_apply(
1805 name,
1806 SubstitutionFunctionKind::Var,
1807 unparsed_value,
1808 self.stylist,
1809 context,
1810 &mut AttributeTracker::new_dummy(),
1812 );
1813 return;
1814 }
1815 let value = ComputedRegisteredValue::universal(Arc::clone(unparsed_value));
1816 context
1817 .builder
1818 .substitution_functions
1819 .insert_var(registration, name, value);
1820 },
1821 CustomDeclarationValue::Parsed(parsed_value) => {
1822 let value = parsed_value.to_computed_value(context);
1823 context
1824 .builder
1825 .substitution_functions
1826 .insert_var(registration, name, value);
1827 },
1828 CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword.revert_kind() {
1829 Some(revert_kind) => {
1830 self.seen.custom.var.remove(name);
1831 self.reverted
1832 .custom
1833 .insert(name.clone(), (priority, revert_kind));
1834 },
1835 None => match keyword {
1836 CSSWideKeyword::Initial => {
1837 debug_assert!(registration.inherits(), "Should've been handled earlier");
1839 remove_and_insert_initial_value(
1840 name,
1841 registration,
1842 &mut context.builder.substitution_functions,
1843 );
1844 },
1845 CSSWideKeyword::Inherit => {
1846 debug_assert!(!registration.inherits(), "Should've been handled earlier");
1848 context
1849 .style()
1850 .add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
1851 let inherited_value = context
1852 .inherited_custom_properties()
1853 .non_inherited
1854 .get(name)
1855 .cloned();
1856 if let Some(inherited_value) = inherited_value {
1857 context.builder.substitution_functions.insert_var(
1858 registration,
1859 name,
1860 inherited_value,
1861 );
1862 }
1863 },
1864 CSSWideKeyword::Revert
1866 | CSSWideKeyword::RevertLayer
1867 | CSSWideKeyword::RevertRule
1868 | CSSWideKeyword::Unset => unreachable!(),
1869 },
1870 },
1871 }
1872 }
1873
1874 #[inline]
1876 pub fn might_have_non_custom_or_attr_dependency(
1877 id: LonghandId,
1878 decl: &PropertyDeclaration,
1879 ) -> bool {
1880 if let PropertyDeclaration::WithVariables(v) = decl {
1881 return matches!(id, LonghandId::LineHeight | LonghandId::FontSize)
1882 || v.value
1883 .variable_value
1884 .references
1885 .flags
1886 .intersects(ReferenceFlags::ATTR);
1887 }
1888 false
1889 }
1890
1891 pub fn maybe_note_non_custom_dependency(
1894 &mut self,
1895 context: &mut computed::Context,
1896 id: LonghandId,
1897 decl: &'a PropertyDeclaration,
1898 attribute_tracker: &mut AttributeTracker,
1899 ) {
1900 debug_assert!(Self::might_have_non_custom_or_attr_dependency(id, decl));
1901 let PropertyDeclaration::WithVariables(v) = decl else {
1902 return;
1903 };
1904 let value = &v.value.variable_value;
1905 let refs = &value.references;
1906
1907 if !refs
1908 .flags
1909 .intersects(ReferenceFlags::VAR | ReferenceFlags::ATTR)
1910 {
1911 return;
1912 }
1913
1914 if refs.flags.intersects(ReferenceFlags::ATTR) {
1917 self.update_attributes_map(context, value, attribute_tracker);
1918 if !refs.flags.intersects(ReferenceFlags::VAR) {
1919 return;
1920 }
1921 }
1922
1923 let references = match id {
1934 LonghandId::FontSize => ReferenceFlags::FONT_UNITS,
1935 LonghandId::LineHeight => ReferenceFlags::LH_UNITS | ReferenceFlags::FONT_UNITS,
1936 LonghandId::ColorScheme => ReferenceFlags::COLOR_SCHEME,
1937 _ => return,
1938 };
1939
1940 references.for_each_non_custom(context.is_root_element(), |idx| {
1941 self.references_from_non_custom_properties[idx]
1942 .get_or_insert_with(Vec::new)
1943 .push(v.value.clone());
1944 });
1945 }
1946
1947 fn value_may_affect_style(
1948 context: &computed::Context,
1949 name: &Name,
1950 registration: &PropertyDescriptors,
1951 initial_values: &ComputedCustomProperties,
1952 value: &CustomDeclarationValue,
1953 ) -> bool {
1954 match *value {
1955 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
1956 if registration.inherits() {
1960 return false;
1961 }
1962 },
1963 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial) => {
1964 if !registration.inherits() {
1967 return false;
1968 }
1969 },
1970 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) => {
1971 return false;
1975 },
1976 _ => {},
1977 }
1978
1979 let existing_value = context
1980 .builder
1981 .substitution_functions
1982 .get_var(registration, name);
1983 let Some(existing_value) = existing_value else {
1984 if matches!(
1985 value,
1986 CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial)
1987 ) {
1988 debug_assert!(registration.inherits(), "Should've been handled earlier");
1989 if registration.initial_value.is_none() {
1993 return false;
1994 }
1995 }
1996 return true;
1997 };
1998 match value {
1999 CustomDeclarationValue::Unparsed(value) => {
2000 if let Some(existing_value) = existing_value.as_universal() {
2003 return existing_value != value;
2004 }
2005 },
2006 CustomDeclarationValue::Parsed(..) => {
2007 },
2010 CustomDeclarationValue::CSSWideKeyword(kw) => {
2011 match kw {
2012 CSSWideKeyword::Inherit => {
2013 debug_assert!(!registration.inherits(), "Should've been handled earlier");
2014 if context
2018 .inherited_custom_properties()
2019 .non_inherited
2020 .get(name)
2021 .is_none()
2022 {
2023 return false;
2024 }
2025 },
2026 CSSWideKeyword::Initial => {
2027 debug_assert!(registration.inherits(), "Should've been handled earlier");
2028 if let Some(initial_value) = initial_values.get(registration, name) {
2031 return existing_value.attr_tainted || existing_value != initial_value;
2032 }
2033 },
2034 CSSWideKeyword::Unset => {
2035 debug_assert!(false, "Should've been handled earlier");
2036 },
2037 CSSWideKeyword::Revert
2038 | CSSWideKeyword::RevertLayer
2039 | CSSWideKeyword::RevertRule => {},
2040 }
2041 },
2042 };
2043
2044 true
2045 }
2046
2047 pub fn update_attributes_map(
2049 &mut self,
2050 context: &mut computed::Context,
2051 value: &'a VariableValue,
2052 attribute_tracker: &mut AttributeTracker,
2053 ) {
2054 let refs = &value.references;
2055 if !refs.flags.intersects(ReferenceFlags::ATTR) {
2056 return;
2057 }
2058 self.may_have_custom_property_cycles = true;
2059
2060 for next in &refs.refs {
2061 if !next.is_attr_with_type() || !self.seen.custom.attr.insert(&next.name) {
2062 continue;
2065 }
2066 if let Ok(v) = get_attr_value_for_cycle_resolution(
2067 &next.name,
2068 &next.attribute_data,
2069 &value.url_data,
2070 attribute_tracker,
2071 ) {
2072 context
2073 .builder
2074 .substitution_functions
2075 .insert_attr(&next.name, v);
2076 }
2077 }
2078 }
2079
2080 pub fn finish_cascade_custom_properties(&mut self, context: &mut computed::Context) {
2083 context
2084 .builder
2085 .substitution_functions
2086 .custom_properties
2087 .shrink_to_fit();
2088
2089 let initial_values = self.stylist.get_custom_property_initial_values();
2094 let reuse_inherited = context.inherited_custom_properties().inherited
2095 == context
2096 .builder
2097 .substitution_functions
2098 .custom_properties
2099 .inherited;
2100 if reuse_inherited {
2101 let inherited = context.inherited_custom_properties().inherited.clone();
2102 context
2103 .builder
2104 .substitution_functions
2105 .custom_properties
2106 .inherited = inherited;
2107 }
2108 if initial_values.non_inherited
2109 == context
2110 .builder
2111 .substitution_functions
2112 .custom_properties
2113 .non_inherited
2114 {
2115 let non_inherited = initial_values.non_inherited.clone();
2116 context
2117 .builder
2118 .substitution_functions
2119 .custom_properties
2120 .non_inherited = non_inherited;
2121 }
2122 }
2123}
2124
2125fn substitute_all(
2126 seen: &SeenSubstitutionFunctions,
2127 references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2128 stylist: &Stylist,
2129 computed_context: &mut computed::Context,
2130 cascade: &mut Cascade,
2131 decls: &Declarations,
2132 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
2133 attr_tracker: &mut AttributeTracker,
2134) {
2135 #[derive(Clone, Eq, PartialEq, Debug)]
2142 enum VarType {
2143 Attr(Name),
2144 Custom(Name),
2145 NonCustom(SingleNonCustomReference),
2146 }
2147
2148 #[derive(Debug)]
2150 struct VarInfo {
2151 var: Option<VarType>,
2155 lowlink: usize,
2159 }
2160
2161 #[derive(Debug, Default)]
2162 struct OrderIndexMap {
2163 var: PrecomputedHashMap<Name, usize>,
2165 attr: PrecomputedHashMap<Name, usize>,
2167 }
2168
2169 impl OrderIndexMap {
2170 fn clear(&mut self) {
2171 self.var.clear();
2172 self.attr.clear();
2173 }
2174 }
2175
2176 struct Context<'a, 'b: 'a, 'c, 'd> {
2179 count: usize,
2182 index_map: OrderIndexMap,
2184 non_custom_index_map: NonCustomReferenceMap<usize>,
2186 var_info: SmallVec<[VarInfo; 5]>,
2188 stack: SmallVec<[usize; 5]>,
2191 stylist: &'a Stylist,
2194 computed_context: &'a mut computed::Context<'b>,
2197 cascade: &'a mut Cascade<'c>,
2201 decls: &'a Declarations<'d>,
2203 cache: &'a mut ShorthandsWithPropertyReferencesCache,
2205 }
2206
2207 impl<'a, 'b: 'a, 'c, 'd> Context<'a, 'b, 'c, 'd> {
2208 fn reset(&mut self) {
2209 self.count = 0;
2210 self.index_map.clear();
2211 self.non_custom_index_map = Default::default();
2212 self.var_info.clear();
2213 self.stack.clear();
2214 }
2215
2216 fn map(&self) -> &ComputedSubstitutionFunctions {
2217 &self.computed_context.builder.substitution_functions
2218 }
2219
2220 fn map_mut(&mut self) -> &mut ComputedSubstitutionFunctions {
2221 &mut self.computed_context.builder.substitution_functions
2222 }
2223
2224 fn handle_loop(&mut self, name: &VarType) {
2226 match name {
2227 VarType::Attr(name) => {
2228 self.computed_context
2229 .builder
2230 .substitution_functions
2231 .remove_attr(name);
2232 },
2233 VarType::Custom(name) => {
2234 handle_invalid_at_computed_value_time(
2236 name,
2237 self.stylist.get_custom_property_registration(name),
2238 self.computed_context,
2239 );
2240 },
2241 VarType::NonCustom(non_custom) => {
2242 self.computed_context
2243 .builder
2244 .invalid_non_custom_properties
2245 .insert(non_custom.to_prioritary_id().to_longhand());
2246 },
2247 }
2248 }
2249
2250 fn apply_prioritary_property(
2256 &mut self,
2257 id: PrioritaryPropertyId,
2258 attr_tracker: &mut AttributeTracker,
2259 ) {
2260 self.cascade.ensure_prioritary_property(
2261 self.computed_context,
2262 self.decls,
2263 self.cache,
2264 attr_tracker,
2265 id,
2266 );
2267 }
2268 }
2269
2270 fn visit_value_references<'a, 'b, 'c, 'd>(
2281 var: &VarType,
2282 root: &References,
2283 url_data: &UrlExtraData,
2284 index: usize,
2285 references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2286 context: &mut Context<'a, 'b, 'c, 'd>,
2287 lowlink: &mut usize,
2288 self_ref: &mut bool,
2289 attribute_tracker: &mut AttributeTracker,
2290 non_custom_references: &mut ReferenceFlags,
2291 ) {
2292 let mut refs_stack = SmallVec::<[&References; 5]>::new();
2294 refs_stack.push(root);
2295 while let Some(refs) = refs_stack.pop() {
2296 *non_custom_references |= refs.flags;
2297 for next in &refs.refs {
2298 if next.substitution_kind == SubstitutionFunctionKind::Env {
2299 let device = context.stylist.device();
2303 let present = device
2304 .environment()
2305 .get(&next.name, device, url_data)
2306 .is_some();
2307 if !present && let Some(ref fallback) = next.fallback {
2308 refs_stack.push(&fallback.references);
2309 }
2310 continue;
2311 }
2312
2313 let next_var = if next.substitution_kind == SubstitutionFunctionKind::Attr {
2314 let can_chain = next.is_attr_with_type() || matches!(var, VarType::Attr(..));
2317 if !can_chain {
2318 continue;
2319 }
2320 if context.map().get_attr(&next.name).is_none()
2321 && let Ok(val) = get_attr_value_for_cycle_resolution(
2322 &next.name,
2323 &next.attribute_data,
2324 url_data,
2325 attribute_tracker,
2326 )
2327 {
2328 context.map_mut().insert_attr(&next.name, val);
2329 }
2330 VarType::Attr(next.name.clone())
2331 } else {
2332 VarType::Custom(next.name.clone())
2333 };
2334
2335 visit_link(
2336 next_var,
2337 index,
2338 references_from_non_custom_properties,
2339 context,
2340 lowlink,
2341 self_ref,
2342 attribute_tracker,
2343 );
2344
2345 let kind = next.substitution_kind;
2348 let resolved = match kind {
2349 SubstitutionFunctionKind::Var => {
2350 let registration =
2351 context.stylist.get_custom_property_registration(&next.name);
2352 context.map().get_var(registration, &next.name)
2353 },
2354 SubstitutionFunctionKind::Attr => context.map().get_attr(&next.name),
2355 SubstitutionFunctionKind::Env => unreachable!("Handled above"),
2356 };
2357 let mut primary_valid = false;
2360 if let Some(resolved) = resolved {
2361 if let Some(v) = resolved.as_universal() {
2362 primary_valid = !v.has_references();
2363 *non_custom_references |= v.references.flags;
2364 } else {
2365 primary_valid = true;
2367 }
2368 }
2369
2370 if !primary_valid && let Some(ref fallback) = next.fallback {
2371 refs_stack.push(&fallback.references);
2372 }
2373 }
2374 }
2375 }
2376
2377 fn visit_link<'a, 'b, 'c, 'd>(
2380 var: VarType,
2381 index: usize,
2382 non_custom_references: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2383 context: &mut Context<'a, 'b, 'c, 'd>,
2384 lowlink: &mut usize,
2385 self_ref: &mut bool,
2386 attr_tracker: &mut AttributeTracker,
2387 ) {
2388 let next_index = match traverse(var, non_custom_references, context, attr_tracker) {
2389 Some(index) => index,
2390 None => return,
2393 };
2394 let next_info = &context.var_info[next_index];
2395 if next_index > index {
2396 *lowlink = cmp::min(*lowlink, next_info.lowlink);
2400 } else if next_index == index {
2401 *self_ref = true;
2402 } else if next_info.var.is_some() {
2403 *lowlink = cmp::min(*lowlink, next_index);
2406 }
2407 }
2408
2409 fn traverse<'a, 'b, 'c, 'd>(
2428 var: VarType,
2429 references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
2430 context: &mut Context<'a, 'b, 'c, 'd>,
2431 attribute_tracker: &mut AttributeTracker,
2432 ) -> Option<usize> {
2433 let mut value_non_custom_refs = ReferenceFlags::empty();
2437 let mut registered = false;
2438 let value = match var {
2439 VarType::Custom(ref name) | VarType::Attr(ref name) => {
2440 let map = &context.computed_context.builder.substitution_functions;
2441 let (registration, value, kind) = if matches!(var, VarType::Custom(..)) {
2442 let registration = context.stylist.get_custom_property_registration(name);
2443 (
2444 registration,
2445 map.get_var(registration, name)?.as_universal()?,
2446 SubstitutionFunctionKind::Var,
2447 )
2448 } else {
2449 (
2451 PropertyDescriptors::unregistered(),
2452 map.get_attr(name)?.as_universal()?,
2453 SubstitutionFunctionKind::Attr,
2454 )
2455 };
2456 let is_root = context.computed_context.is_root_element();
2457 value_non_custom_refs = find_non_custom_references(registration, value, is_root);
2458 registered = !registration.is_universal();
2459 let has_dependency = value
2460 .references
2461 .flags
2462 .intersects(ReferenceFlags::ATTR | ReferenceFlags::VAR)
2463 || !value_non_custom_refs.is_empty();
2464 if !has_dependency {
2466 debug_assert!(
2467 !value.references.flags.intersects(ReferenceFlags::ENV),
2468 "Should've been handled earlier"
2469 );
2470 if kind == SubstitutionFunctionKind::Attr || registered {
2471 let value = value.clone();
2476 substitute_references_if_needed_and_apply(
2477 name,
2478 kind,
2479 &value,
2480 context.stylist,
2481 context.computed_context,
2482 attribute_tracker,
2483 );
2484 }
2485 return None;
2486 }
2487
2488 let index_map = if kind == SubstitutionFunctionKind::Var {
2490 &mut context.index_map.var
2491 } else {
2492 &mut context.index_map.attr
2493 };
2494 match index_map.entry_ref(name) {
2495 EntryRef::Occupied(entry) => {
2496 return Some(*entry.get());
2497 },
2498 EntryRef::Vacant(entry) => {
2499 entry.insert(context.count);
2500 },
2501 }
2502 Some(value.clone())
2505 },
2506 VarType::NonCustom(ref non_custom) => {
2507 let entry = &mut context.non_custom_index_map[*non_custom];
2508 if let Some(v) = entry {
2509 return Some(*v);
2510 }
2511 *entry = Some(context.count);
2512 None
2513 },
2514 };
2515
2516 let index = context.count;
2518 context.count += 1;
2519 debug_assert_eq!(index, context.var_info.len());
2520 context.var_info.push(VarInfo {
2521 var: Some(var.clone()),
2522 lowlink: index,
2523 });
2524 context.stack.push(index);
2525
2526 let mut self_ref = false;
2527 let mut lowlink = index;
2528 if let Some(v) = value.as_ref() {
2529 debug_assert!(
2530 matches!(var, VarType::Custom(_) | VarType::Attr(_)),
2531 "Non-custom property has references?"
2532 );
2533
2534 visit_value_references(
2536 &var,
2537 &v.references,
2538 &v.url_data,
2539 index,
2540 references_from_non_custom_properties,
2541 context,
2542 &mut lowlink,
2543 &mut self_ref,
2544 attribute_tracker,
2545 &mut value_non_custom_refs,
2546 );
2547
2548 let is_root = context.computed_context.is_root_element();
2551 if registered && !value_non_custom_refs.is_empty() {
2552 value_non_custom_refs.for_each_non_custom(is_root, |r| {
2553 visit_link(
2554 VarType::NonCustom(r),
2555 index,
2556 references_from_non_custom_properties,
2557 context,
2558 &mut lowlink,
2559 &mut self_ref,
2560 attribute_tracker,
2561 );
2562 });
2563 }
2564 } else if let VarType::NonCustom(non_custom) = var {
2565 if non_custom == SingleNonCustomReference::LhUnits {
2575 visit_link(
2576 VarType::NonCustom(SingleNonCustomReference::FontUnits),
2577 index,
2578 references_from_non_custom_properties,
2579 context,
2580 &mut lowlink,
2581 &mut self_ref,
2582 attribute_tracker,
2583 );
2584 }
2585 let entry = &references_from_non_custom_properties[non_custom];
2586 if let Some(values) = entry.as_ref() {
2587 for value in values {
2588 let value = &value.variable_value;
2595 visit_value_references(
2596 &var,
2597 &value.references,
2598 &value.url_data,
2599 index,
2600 references_from_non_custom_properties,
2601 context,
2602 &mut lowlink,
2603 &mut self_ref,
2604 attribute_tracker,
2605 &mut Default::default(),
2606 );
2607 }
2608 }
2609 }
2610
2611 context.var_info[index].lowlink = lowlink;
2612 if lowlink != index {
2613 return Some(index);
2619 }
2620
2621 let mut in_loop = self_ref;
2623 loop {
2624 let var_index = context
2625 .stack
2626 .pop()
2627 .expect("The current variable should still be in stack");
2628 let var_info = &mut context.var_info[var_index];
2629 let var_name = var_info
2631 .var
2632 .take()
2633 .expect("Variable should not be popped from stack twice");
2634 if var_index != index {
2635 in_loop = true;
2638 }
2639 if in_loop {
2640 context.handle_loop(&var_name);
2641 }
2642 if var_index == index {
2643 debug_assert_eq!(var_name, var);
2644 break;
2645 }
2646 }
2647
2648 if in_loop {
2649 return None;
2650 }
2651
2652 match var {
2654 VarType::Custom(ref name) | VarType::Attr(ref name) => {
2655 if let Some(ref v) = value {
2656 let kind = if matches!(var, VarType::Custom(..)) {
2657 SubstitutionFunctionKind::Var
2658 } else {
2659 SubstitutionFunctionKind::Attr
2660 };
2661 substitute_references_if_needed_and_apply(
2664 name,
2665 kind,
2666 v,
2667 context.stylist,
2668 context.computed_context,
2669 attribute_tracker,
2670 );
2671 }
2672 },
2673 VarType::NonCustom(non_custom) => {
2674 context.apply_prioritary_property(non_custom.to_prioritary_id(), attribute_tracker);
2675 },
2676 }
2677 None
2679 }
2680
2681 let mut context = Context {
2682 count: 0,
2683 index_map: OrderIndexMap::default(),
2684 non_custom_index_map: NonCustomReferenceMap::default(),
2685 stack: SmallVec::new(),
2686 var_info: SmallVec::new(),
2687 stylist,
2688 computed_context: &mut *computed_context,
2689 cascade: &mut *cascade,
2690 decls,
2691 cache: &mut *shorthand_cache,
2692 };
2693 let mut first = true;
2694 let mut run_one = |var: VarType| {
2695 if !first {
2696 context.reset();
2697 }
2698 first = false;
2699 traverse(
2700 var,
2701 references_from_non_custom_properties,
2702 &mut context,
2703 attr_tracker,
2704 );
2705 };
2706 for (var, has_refs) in &seen.var {
2710 if !has_refs {
2711 continue;
2712 }
2713 run_one(VarType::Custom((*var).clone()));
2714 }
2715 for attr in &seen.attr {
2718 run_one(VarType::Attr((*attr).clone()));
2719 }
2720}