1use crate::applicable_declarations::{CascadePriority, RevertKind};
8use crate::color::AbsoluteColor;
9use crate::computed_value_flags::ComputedValueFlags;
10use crate::custom_properties::{
11 CustomPropertiesBuilder, DeferFontRelativeCustomPropertyResolution,
12};
13use crate::dom::{AttributeTracker, TElement};
14#[cfg(feature = "gecko")]
15use crate::font_metrics::FontMetricsOrientation;
16use crate::logical_geometry::WritingMode;
17use crate::properties::{
18 property_counts, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, LonghandId,
19 LonghandIdSet, PrioritaryPropertyId, PropertyDeclaration, PropertyDeclarationId, PropertyFlags,
20 ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
21};
22use crate::rule_cache::{RuleCache, RuleCacheConditions};
23use crate::rule_tree::{CascadeLevel, CascadeOrigin, RuleCascadeFlags, StrongRuleNode};
24use crate::selector_parser::PseudoElement;
25use crate::shared_lock::StylesheetGuards;
26use crate::style_adjuster::StyleAdjuster;
27use crate::stylesheets::container_rule::ContainerSizeQuery;
28use crate::stylesheets::layer_rule::LayerOrder;
29use crate::stylist::Stylist;
30#[cfg(feature = "gecko")]
31use crate::values::specified::length::FontBaseSize;
32use crate::values::specified::position::PositionTryFallbacksTryTactic;
33use crate::values::{computed, specified};
34use rustc_hash::FxHashMap;
35use servo_arc::Arc;
36use smallvec::SmallVec;
37use std::borrow::Cow;
38
39#[derive(Copy, Clone)]
41#[allow(missing_docs)]
42pub enum FirstLineReparenting<'a> {
43 No,
44 Yes {
45 style_to_reparent: &'a ComputedValues,
49 },
50}
51
52pub fn cascade<E>(
67 stylist: &Stylist,
68 pseudo: Option<&PseudoElement>,
69 rule_node: &StrongRuleNode,
70 guards: &StylesheetGuards,
71 parent_style: Option<&ComputedValues>,
72 layout_parent_style: Option<&ComputedValues>,
73 first_line_reparenting: FirstLineReparenting,
74 try_tactic: &PositionTryFallbacksTryTactic,
75 visited_rules: Option<&StrongRuleNode>,
76 cascade_input_flags: ComputedValueFlags,
77 included_cascade_flags: RuleCascadeFlags,
78 rule_cache: Option<&RuleCache>,
79 rule_cache_conditions: &mut RuleCacheConditions,
80 element: Option<E>,
81) -> Arc<ComputedValues>
82where
83 E: TElement,
84{
85 cascade_rules(
86 stylist,
87 pseudo,
88 rule_node,
89 guards,
90 parent_style,
91 layout_parent_style,
92 first_line_reparenting,
93 try_tactic,
94 CascadeMode::Unvisited { visited_rules },
95 cascade_input_flags,
96 included_cascade_flags,
97 rule_cache,
98 rule_cache_conditions,
99 element,
100 )
101}
102
103struct DeclarationIterator<'a> {
104 guards: &'a StylesheetGuards<'a>,
106 restriction: Option<PropertyFlags>,
107 current_rule_node: Option<&'a StrongRuleNode>,
109 declarations: DeclarationImportanceIterator<'a>,
111 priority: CascadePriority,
112}
113
114impl<'a> DeclarationIterator<'a> {
115 #[inline]
116 fn new(
117 rule_node: &'a StrongRuleNode,
118 guards: &'a StylesheetGuards,
119 pseudo: Option<&PseudoElement>,
120 ) -> Self {
121 let restriction = pseudo.and_then(|p| p.property_restriction());
122 let mut iter = Self {
123 guards,
124 current_rule_node: Some(rule_node),
125 priority: CascadePriority::new(
126 CascadeLevel::new(CascadeOrigin::UA),
127 LayerOrder::root(),
128 RuleCascadeFlags::empty(),
129 ),
130 declarations: DeclarationImportanceIterator::default(),
131 restriction,
132 };
133 iter.update_for_node(rule_node);
134 iter
135 }
136
137 fn update_for_node(&mut self, node: &'a StrongRuleNode) {
138 self.priority = node.cascade_priority();
139 let guard = self.priority.cascade_level().origin().guard(&self.guards);
140 self.declarations = match node.style_source() {
141 Some(source) => source.read(guard).declaration_importance_iter(),
142 None => DeclarationImportanceIterator::default(),
143 };
144 }
145}
146
147impl<'a> Iterator for DeclarationIterator<'a> {
148 type Item = (&'a PropertyDeclaration, CascadePriority);
149
150 #[inline]
151 fn next(&mut self) -> Option<Self::Item> {
152 loop {
153 if let Some((decl, importance)) = self.declarations.next_back() {
154 if self.priority.cascade_level().is_important() != importance.important() {
155 continue;
156 }
157
158 if let Some(restriction) = self.restriction {
159 if let PropertyDeclarationId::Longhand(id) = decl.id() {
164 if !id.flags().contains(restriction)
165 && self.priority.cascade_level().origin() != CascadeOrigin::UA
166 {
167 continue;
168 }
169 }
170 }
171
172 return Some((decl, self.priority));
173 }
174
175 let next_node = self.current_rule_node.take()?.parent()?;
176 self.current_rule_node = Some(next_node);
177 self.update_for_node(next_node);
178 }
179 }
180}
181
182fn cascade_rules<E>(
183 stylist: &Stylist,
184 pseudo: Option<&PseudoElement>,
185 rule_node: &StrongRuleNode,
186 guards: &StylesheetGuards,
187 parent_style: Option<&ComputedValues>,
188 layout_parent_style: Option<&ComputedValues>,
189 first_line_reparenting: FirstLineReparenting,
190 try_tactic: &PositionTryFallbacksTryTactic,
191 cascade_mode: CascadeMode,
192 cascade_input_flags: ComputedValueFlags,
193 included_cascade_flags: RuleCascadeFlags,
194 rule_cache: Option<&RuleCache>,
195 rule_cache_conditions: &mut RuleCacheConditions,
196 element: Option<E>,
197) -> Arc<ComputedValues>
198where
199 E: TElement,
200{
201 apply_declarations(
202 stylist,
203 pseudo,
204 rule_node,
205 guards,
206 DeclarationIterator::new(rule_node, guards, pseudo),
207 parent_style,
208 layout_parent_style,
209 first_line_reparenting,
210 try_tactic,
211 cascade_mode,
212 cascade_input_flags,
213 included_cascade_flags,
214 rule_cache,
215 rule_cache_conditions,
216 element,
217 )
218}
219
220#[derive(Clone, Copy)]
222pub enum CascadeMode<'a, 'b> {
223 Unvisited {
225 visited_rules: Option<&'a StrongRuleNode>,
227 },
228 Visited {
230 unvisited_context: &'a computed::Context<'b>,
232 },
233}
234
235fn iter_declarations<'builder, 'decls: 'builder>(
236 iter: impl Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
237 declarations: &mut Declarations<'decls>,
238 mut custom_builder: Option<&mut CustomPropertiesBuilder<'builder, 'decls>>,
239 attribute_tracker: &mut AttributeTracker,
240) {
241 for (declaration, priority) in iter {
242 if let PropertyDeclaration::Custom(ref declaration) = *declaration {
243 if let Some(ref mut builder) = custom_builder {
244 builder.cascade(declaration, priority, attribute_tracker);
245 }
246 } else {
247 let id = declaration.id().as_longhand().unwrap();
248 declarations.note_declaration(declaration, priority, id);
249 if CustomPropertiesBuilder::might_have_non_custom_or_attr_dependency(id, declaration) {
250 if let Some(ref mut builder) = custom_builder {
251 builder.maybe_note_non_custom_dependency(id, declaration, attribute_tracker);
252 }
253 }
254 }
255 }
256}
257
258pub fn apply_declarations<'a, E, I>(
261 stylist: &'a Stylist,
262 pseudo: Option<&'a PseudoElement>,
263 rules: &StrongRuleNode,
264 guards: &StylesheetGuards,
265 iter: I,
266 parent_style: Option<&'a ComputedValues>,
267 layout_parent_style: Option<&ComputedValues>,
268 first_line_reparenting: FirstLineReparenting<'a>,
269 try_tactic: &'a PositionTryFallbacksTryTactic,
270 cascade_mode: CascadeMode,
271 cascade_input_flags: ComputedValueFlags,
272 included_cascade_flags: RuleCascadeFlags,
273 rule_cache: Option<&'a RuleCache>,
274 rule_cache_conditions: &'a mut RuleCacheConditions,
275 element: Option<E>,
276) -> Arc<ComputedValues>
277where
278 E: TElement + 'a,
279 I: Iterator<Item = (&'a PropertyDeclaration, CascadePriority)>,
280{
281 debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
282 let device = stylist.device();
283 let inherited_style = parent_style.unwrap_or(device.default_computed_values());
284 let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
285
286 let container_size_query =
287 ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());
288
289 let mut context = computed::Context::new(
290 StyleBuilder::new(
294 device,
295 Some(stylist),
296 parent_style,
297 pseudo,
298 Some(rules.clone()),
299 is_root_element,
300 ),
301 stylist.quirks_mode(),
302 rule_cache_conditions,
303 container_size_query,
304 included_cascade_flags,
305 );
306
307 context.style().add_flags(cascade_input_flags);
308
309 let using_cached_reset_properties;
310 let ignore_colors = context.builder.device.forced_colors().is_active();
311 let mut cascade = Cascade::new(first_line_reparenting, try_tactic, ignore_colors);
312 let mut declarations = Default::default();
313 let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
314 let attribute_provider = element.map(|e| e.ultimate_originating_element());
315 let mut attribute_tracker = match &attribute_provider {
316 Some(provider) => AttributeTracker::new(provider),
317 None => AttributeTracker::new_dummy(),
318 };
319
320 let properties_to_apply = match cascade_mode {
321 CascadeMode::Visited { unvisited_context } => {
322 context.builder.substitution_functions =
323 unvisited_context.builder.substitution_functions.clone();
324 context.builder.writing_mode = unvisited_context.builder.writing_mode;
325 context.builder.color_scheme = unvisited_context.builder.color_scheme;
326 using_cached_reset_properties = false;
330 iter_declarations(iter, &mut declarations, None, &mut attribute_tracker);
334
335 LonghandIdSet::visited_dependent()
336 },
337 CascadeMode::Unvisited { visited_rules } => {
338 let deferred_custom_properties = {
339 let mut builder = CustomPropertiesBuilder::new(stylist, &mut context);
340 iter_declarations(
341 iter,
342 &mut declarations,
343 Some(&mut builder),
344 &mut attribute_tracker,
345 );
346 builder.build(
351 DeferFontRelativeCustomPropertyResolution::Yes,
352 &mut attribute_tracker,
353 )
354 };
355
356 cascade.apply_prioritary_properties(
359 &mut context,
360 &declarations,
361 &mut shorthand_cache,
362 &mut attribute_tracker,
363 );
364
365 if let Some(deferred) = deferred_custom_properties {
367 CustomPropertiesBuilder::build_deferred(
368 deferred,
369 stylist,
370 &mut context,
371 &mut attribute_tracker,
372 );
373 }
374
375 if let Some(visited_rules) = visited_rules {
376 cascade.compute_visited_style_if_needed(
377 &mut context,
378 element,
379 parent_style,
380 layout_parent_style,
381 visited_rules,
382 guards,
383 );
384 }
385
386 using_cached_reset_properties =
387 cascade.try_to_use_cached_reset_properties(&mut context, rule_cache, guards);
388
389 if using_cached_reset_properties {
390 LonghandIdSet::late_group_only_inherited()
391 } else {
392 LonghandIdSet::late_group()
393 }
394 },
395 };
396
397 cascade.apply_non_prioritary_properties(
398 &mut context,
399 &declarations.longhand_declarations,
400 &mut shorthand_cache,
401 &properties_to_apply,
402 &mut attribute_tracker,
403 );
404
405 context.builder.attribute_references = attribute_tracker.finalize();
406
407 cascade.finished_applying_properties(&mut context.builder);
408
409 context.builder.clear_modified_reset();
410
411 if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
412 StyleAdjuster::new(&mut context.builder).adjust(
413 layout_parent_style.unwrap_or(inherited_style),
414 element,
415 try_tactic,
416 &cascade.author_specified,
417 );
418 }
419
420 if context.builder.modified_reset() || using_cached_reset_properties {
421 context.rule_cache_conditions.borrow_mut().set_uncacheable();
427 }
428
429 context.builder.build()
430}
431
432type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
438
439#[cfg(feature = "gecko")]
440fn is_base_appearance(context: &computed::Context) -> bool {
441 use computed::Appearance;
442 let box_style = context.builder.get_box();
443 match box_style.clone_appearance() {
444 Appearance::BaseSelect => {
445 matches!(
446 box_style.clone__moz_default_appearance(),
447 Appearance::Listbox | Appearance::Menulist
448 )
449 },
450 Appearance::Base => box_style.clone__moz_default_appearance() != Appearance::None,
451 _ => false,
452 }
453}
454
455fn tweak_when_ignoring_colors(
456 context: &computed::Context,
457 longhand_id: LonghandId,
458 origin: CascadeOrigin,
459 declaration: &mut Cow<PropertyDeclaration>,
460 declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
461) {
462 use crate::values::computed::ToComputedValue;
463 use crate::values::specified::Color;
464
465 if !longhand_id.ignored_when_document_colors_disabled() {
466 return;
467 }
468
469 let is_ua_or_user_rule = matches!(origin, CascadeOrigin::User | CascadeOrigin::UA);
470 if is_ua_or_user_rule {
471 return;
472 }
473
474 #[cfg(feature = "gecko")]
476 {
477 let forced = context
478 .builder
479 .get_inherited_text()
480 .clone_forced_color_adjust();
481 if forced == computed::ForcedColorAdjust::None {
482 return;
483 }
484 }
485
486 fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
487 color
489 .to_computed_value(context)
490 .resolve_to_absolute(&AbsoluteColor::BLACK)
491 .alpha
492 }
493
494 match **declaration {
496 PropertyDeclaration::CSSWideKeyword(..) => return,
498 PropertyDeclaration::BackgroundColor(ref color) => {
499 if color.honored_in_forced_colors_mode(context, true) {
509 return;
510 }
511 let alpha = alpha_channel(color, context);
515 if alpha == 0.0 {
516 return;
517 }
518 let mut color = context.builder.device.default_background_color();
519 color.alpha = alpha;
520 declarations_to_apply_unless_overridden
521 .push(PropertyDeclaration::BackgroundColor(color.into()))
522 },
523 PropertyDeclaration::Color(ref color) => {
524 if color
526 .0
527 .honored_in_forced_colors_mode(context, true)
528 {
529 return;
530 }
531 if context
535 .builder
536 .get_parent_inherited_text()
537 .clone_color()
538 .alpha
539 == 0.0
540 {
541 let color = context.builder.device.default_color();
542 declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
543 specified::ColorPropertyValue(color.into()),
544 ))
545 }
546 },
547 #[cfg(feature = "gecko")]
549 PropertyDeclaration::BackgroundImage(ref bkg) => {
550 use crate::values::generics::image::Image;
551 if static_prefs::pref!("browser.display.permit_backplate") {
552 if bkg
553 .0
554 .iter()
555 .all(|image| matches!(*image, Image::Url(..) | Image::None))
556 {
557 return;
558 }
559 }
560 },
561 _ => {
562 if let Some(color) = declaration.color_value() {
575 if color
576 .honored_in_forced_colors_mode(context, false)
577 {
578 return;
579 }
580 }
581 },
582 }
583
584 *declaration.to_mut() =
585 PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
586}
587
588type DeclarationIndex = u16;
590
591#[derive(Copy, Clone)]
596struct PrioritaryDeclarationPosition {
597 most_important: DeclarationIndex,
599 least_important: DeclarationIndex,
600}
601
602impl Default for PrioritaryDeclarationPosition {
603 fn default() -> Self {
604 Self {
605 most_important: DeclarationIndex::MAX,
606 least_important: DeclarationIndex::MAX,
607 }
608 }
609}
610
611#[derive(Copy, Clone)]
612struct Declaration<'a> {
613 decl: &'a PropertyDeclaration,
614 priority: CascadePriority,
615 next_index: DeclarationIndex,
616}
617
618#[derive(Default)]
620struct Declarations<'a> {
621 has_prioritary_properties: bool,
623 longhand_declarations: SmallVec<[Declaration<'a>; 64]>,
625 prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
627}
628
629impl<'a> Declarations<'a> {
630 fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
631 let new_index = self.longhand_declarations.len();
632 if new_index >= DeclarationIndex::MAX as usize {
633 return;
636 }
637
638 self.has_prioritary_properties = true;
639 let new_index = new_index as DeclarationIndex;
640 let position = &mut self.prioritary_positions[id as usize];
641 if position.most_important == DeclarationIndex::MAX {
642 position.most_important = new_index;
645 } else {
646 self.longhand_declarations[position.least_important as usize].next_index = new_index;
648 }
649 position.least_important = new_index;
650 }
651
652 fn note_declaration(
653 &mut self,
654 decl: &'a PropertyDeclaration,
655 priority: CascadePriority,
656 id: LonghandId,
657 ) {
658 if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
659 self.note_prioritary_property(id);
660 }
661 self.longhand_declarations.push(Declaration {
662 decl,
663 priority,
664 next_index: 0,
665 });
666 }
667}
668
669struct Cascade<'b> {
670 first_line_reparenting: FirstLineReparenting<'b>,
671 try_tactic: &'b PositionTryFallbacksTryTactic,
672 ignore_colors: bool,
673 seen: LonghandIdSet,
674 author_specified: LonghandIdSet,
675 reverted_set: LonghandIdSet,
676 reverted: FxHashMap<LonghandId, (CascadePriority, RevertKind)>,
677 declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
678}
679
680impl<'b> Cascade<'b> {
681 fn new(
682 first_line_reparenting: FirstLineReparenting<'b>,
683 try_tactic: &'b PositionTryFallbacksTryTactic,
684 ignore_colors: bool,
685 ) -> Self {
686 Self {
687 first_line_reparenting,
688 try_tactic,
689 ignore_colors,
690 seen: LonghandIdSet::default(),
691 author_specified: LonghandIdSet::default(),
692 reverted_set: Default::default(),
693 reverted: Default::default(),
694 declarations_to_apply_unless_overridden: Default::default(),
695 }
696 }
697
698 fn substitute_variables_if_needed<'cache, 'decl>(
699 &self,
700 context: &mut computed::Context,
701 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
702 declaration: &'decl PropertyDeclaration,
703 attribute_tracker: &mut AttributeTracker,
704 ) -> Cow<'decl, PropertyDeclaration>
705 where
706 'cache: 'decl,
707 {
708 let declaration = match *declaration {
709 PropertyDeclaration::WithVariables(ref declaration) => declaration,
710 ref d => return Cow::Borrowed(d),
711 };
712
713 if !declaration.id.inherited() {
714 context.rule_cache_conditions.borrow_mut().set_uncacheable();
715
716 if matches!(declaration.id, LonghandId::Display | LonghandId::Content) {
722 context
723 .builder
724 .add_flags(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE);
725 }
726 }
727
728 debug_assert!(
729 context.builder.stylist.is_some(),
730 "Need a Stylist to substitute variables!"
731 );
732 declaration.value.substitute_variables(
733 declaration.id,
734 &context.builder.substitution_functions(),
735 context.builder.stylist.unwrap(),
736 context,
737 shorthand_cache,
738 attribute_tracker,
739 )
740 }
741
742 fn apply_one_prioritary_property(
743 &mut self,
744 context: &mut computed::Context,
745 decls: &Declarations,
746 cache: &mut ShorthandsWithPropertyReferencesCache,
747 id: PrioritaryPropertyId,
748 attr_provider: &mut AttributeTracker,
749 ) -> bool {
750 let mut index = decls.prioritary_positions[id as usize].most_important;
751 if index == DeclarationIndex::MAX {
752 return false;
753 }
754
755 let longhand_id = id.to_longhand();
756 debug_assert!(
757 !longhand_id.is_logical(),
758 "That could require more book-keeping"
759 );
760 loop {
761 let decl = decls.longhand_declarations[index as usize];
762 self.apply_one_longhand(
763 context,
764 longhand_id,
765 decl.decl,
766 decl.priority,
767 cache,
768 attr_provider,
769 );
770 if self.seen.contains(longhand_id) {
771 return true; }
773 debug_assert!(
774 decl.next_index == 0 || decl.next_index > index,
775 "should make progress! {} -> {}",
776 index,
777 decl.next_index,
778 );
779 index = decl.next_index;
780 if index == 0 {
781 break;
782 }
783 }
784 false
785 }
786
787 fn apply_prioritary_properties(
788 &mut self,
789 context: &mut computed::Context,
790 decls: &Declarations,
791 cache: &mut ShorthandsWithPropertyReferencesCache,
792 attribute_tracker: &mut AttributeTracker,
793 ) {
794 macro_rules! apply {
797 ($prop:ident) => {
798 self.apply_one_prioritary_property(
799 context,
800 decls,
801 cache,
802 PrioritaryPropertyId::$prop,
803 attribute_tracker,
804 )
805 };
806 }
807
808 if !decls.has_prioritary_properties {
809 return;
810 }
811
812 #[cfg(feature = "gecko")]
813 apply!(MozDefaultAppearance);
814 #[cfg(feature = "gecko")]
815 if apply!(Appearance) && is_base_appearance(&context) {
816 context
817 .style()
818 .add_flags(ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE);
819 context
820 .included_cascade_flags
821 .insert(RuleCascadeFlags::APPEARANCE_BASE);
822 }
823
824 let has_writing_mode = apply!(WritingMode) | apply!(Direction);
825 #[cfg(feature = "gecko")]
826 let has_writing_mode = has_writing_mode | apply!(TextOrientation);
827
828 if has_writing_mode {
829 context.builder.writing_mode = WritingMode::new(context.builder.get_inherited_box())
830 }
831
832 if apply!(Zoom) {
833 context.builder.recompute_effective_zooms();
834 if !context.builder.effective_zoom_for_inheritance.is_one() {
835 self.recompute_font_size_for_zoom_change(&mut context.builder);
841 }
842 }
843
844 let has_font_family = apply!(FontFamily);
846 let has_lang = apply!(XLang);
847 #[cfg(feature = "gecko")]
848 {
849 if has_lang {
850 self.recompute_initial_font_family_if_needed(&mut context.builder);
851 }
852 if has_font_family {
853 self.prioritize_user_fonts_if_needed(&mut context.builder);
854 }
855
856 if apply!(XTextScale) {
858 self.unzoom_fonts_if_needed(&mut context.builder);
859 }
860 let has_font_size = apply!(FontSize);
861 let has_math_depth = apply!(MathDepth);
862 let has_min_font_size_ratio = apply!(MozMinFontSizeRatio);
863
864 if has_math_depth && has_font_size {
865 self.recompute_math_font_size_if_needed(context);
866 }
867 if has_lang || has_font_family {
868 self.recompute_keyword_font_size_if_needed(context);
869 }
870 if has_font_size || has_min_font_size_ratio || has_lang || has_font_family {
871 self.constrain_font_size_if_needed(&mut context.builder);
872 }
873 }
874
875 #[cfg(feature = "servo")]
876 {
877 apply!(FontSize);
878 if has_lang || has_font_family {
879 self.recompute_keyword_font_size_if_needed(context);
880 }
881 }
882
883 apply!(FontWeight);
885 apply!(FontStretch);
886 apply!(FontStyle);
887 #[cfg(feature = "gecko")]
888 apply!(FontSizeAdjust);
889
890 #[cfg(feature = "gecko")]
891 apply!(ForcedColorAdjust);
892 if apply!(ColorScheme) {
895 context.builder.color_scheme = context.builder.get_inherited_ui().color_scheme_bits();
896 }
897 apply!(LineHeight);
898 }
899
900 fn apply_non_prioritary_properties(
901 &mut self,
902 context: &mut computed::Context,
903 longhand_declarations: &[Declaration],
904 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
905 properties_to_apply: &LonghandIdSet,
906 attribute_tracker: &mut AttributeTracker,
907 ) {
908 debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
909 debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
910 for declaration in &*longhand_declarations {
911 let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
912 if !properties_to_apply.contains(longhand_id) {
913 continue;
914 }
915 debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
916 let is_logical = longhand_id.is_logical();
917 if is_logical {
918 let wm = context.builder.writing_mode;
919 context
920 .rule_cache_conditions
921 .borrow_mut()
922 .set_writing_mode_dependency(wm);
923 longhand_id = longhand_id.to_physical(wm);
924 }
925 self.apply_one_longhand(
926 context,
927 longhand_id,
928 declaration.decl,
929 declaration.priority,
930 shorthand_cache,
931 attribute_tracker,
932 );
933 }
934 if !self.declarations_to_apply_unless_overridden.is_empty() {
935 debug_assert!(self.ignore_colors);
936 for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
937 let longhand_id = declaration.id().as_longhand().unwrap();
938 debug_assert!(!longhand_id.is_logical());
939 if !self.seen.contains(longhand_id) {
940 unsafe {
941 self.do_apply_declaration(context, longhand_id, &declaration);
942 }
943 }
944 }
945 }
946
947 if !context.builder.effective_zoom_for_inheritance.is_one() {
948 self.recompute_zoom_dependent_inherited_lengths(context);
949 }
950 }
951
952 #[cold]
953 fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {
954 debug_assert!(self.seen.contains(LonghandId::Zoom));
955 for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {
956 if self.seen.contains(prop) {
957 continue;
958 }
959 let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);
960 unsafe {
961 self.do_apply_declaration(context, prop, &declaration);
962 }
963 }
964 }
965
966 fn apply_one_longhand(
967 &mut self,
968 context: &mut computed::Context,
969 longhand_id: LonghandId,
970 declaration: &PropertyDeclaration,
971 priority: CascadePriority,
972 cache: &mut ShorthandsWithPropertyReferencesCache,
973 attribute_tracker: &mut AttributeTracker,
974 ) {
975 debug_assert!(!longhand_id.is_logical());
976 if self.seen.contains(longhand_id) {
977 return;
978 }
979
980 if !(priority.flags() - context.included_cascade_flags).is_empty() {
981 return;
982 }
983
984 if self.reverted_set.contains(longhand_id) {
985 if let Some(&(reverted_priority, revert_kind)) = self.reverted.get(&longhand_id) {
986 if !reverted_priority.allows_when_reverted(&priority, revert_kind) {
987 return;
988 }
989 }
990 }
991
992 let mut declaration =
993 self.substitute_variables_if_needed(context, cache, declaration, attribute_tracker);
994
995 let origin = priority.cascade_level().origin();
998 if self.ignore_colors {
999 tweak_when_ignoring_colors(
1000 context,
1001 longhand_id,
1002 origin,
1003 &mut declaration,
1004 &mut self.declarations_to_apply_unless_overridden,
1005 );
1006 }
1007 let can_skip_apply = match declaration.get_css_wide_keyword() {
1008 Some(keyword) => {
1009 if let Some(revert_kind) = keyword.revert_kind() {
1010 self.reverted_set.insert(longhand_id);
1013 self.reverted.insert(longhand_id, (priority, revert_kind));
1014 return;
1015 }
1016
1017 let inherited = longhand_id.inherited();
1018 let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()
1019 && longhand_id.zoom_dependent();
1020 match keyword {
1021 CSSWideKeyword::Revert
1022 | CSSWideKeyword::RevertLayer
1023 | CSSWideKeyword::RevertRule => unreachable!(),
1024 CSSWideKeyword::Unset => !zoomed || !inherited,
1025 CSSWideKeyword::Inherit => inherited && !zoomed,
1026 CSSWideKeyword::Initial => !inherited,
1027 }
1028 },
1029 None => false,
1030 };
1031
1032 self.seen.insert(longhand_id);
1033 if origin.is_author_origin() {
1034 self.author_specified.insert(longhand_id);
1035 }
1036
1037 if !can_skip_apply {
1038 let old_scope = context.scope;
1042 let cascade_level = priority.cascade_level();
1043 context.scope = cascade_level;
1044 unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
1045 context.scope = old_scope;
1046 }
1047 }
1048
1049 #[inline]
1050 unsafe fn do_apply_declaration(
1051 &self,
1052 context: &mut computed::Context,
1053 longhand_id: LonghandId,
1054 declaration: &PropertyDeclaration,
1055 ) {
1056 debug_assert!(!longhand_id.is_logical());
1057 (CASCADE_PROPERTY[longhand_id as usize])(&declaration, context);
1063 }
1064
1065 fn compute_visited_style_if_needed<E>(
1066 &self,
1067 context: &mut computed::Context,
1068 element: Option<E>,
1069 parent_style: Option<&ComputedValues>,
1070 layout_parent_style: Option<&ComputedValues>,
1071 visited_rules: &StrongRuleNode,
1072 guards: &StylesheetGuards,
1073 ) where
1074 E: TElement,
1075 {
1076 let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
1077
1078 macro_rules! visited_parent {
1079 ($parent:expr) => {
1080 if is_link {
1081 $parent
1082 } else {
1083 $parent.map(|p| p.visited_style().unwrap_or(p))
1084 }
1085 };
1086 }
1087
1088 let style = cascade_rules(
1091 context.builder.stylist.unwrap(),
1092 context.builder.pseudo,
1093 visited_rules,
1094 guards,
1095 visited_parent!(parent_style),
1096 visited_parent!(layout_parent_style),
1097 self.first_line_reparenting,
1098 self.try_tactic,
1099 CascadeMode::Visited {
1100 unvisited_context: &*context,
1101 },
1102 Default::default(),
1105 context.included_cascade_flags,
1106 None, &mut *context.rule_cache_conditions.borrow_mut(),
1112 element,
1113 );
1114 context.builder.visited_style = Some(style);
1115 }
1116
1117 fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
1118 #[cfg(feature = "gecko")]
1119 {
1120 if let Some(bg) = builder.get_background_if_mutated() {
1121 bg.fill_arrays();
1122 }
1123
1124 if let Some(svg) = builder.get_svg_if_mutated() {
1125 svg.fill_arrays();
1126 }
1127 }
1128
1129 if self
1130 .author_specified
1131 .contains_any(LonghandIdSet::border_background_properties())
1132 {
1133 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
1134 }
1135
1136 if self.author_specified.contains(LonghandId::Color) {
1137 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);
1138 }
1139
1140 if self.author_specified.contains(LonghandId::TextShadow) {
1141 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_SHADOW);
1142 }
1143
1144 if self.author_specified.contains(LonghandId::GridAutoFlow) {
1145 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW);
1146 }
1147 #[cfg(feature = "servo")]
1148 {
1149 if let Some(font) = builder.get_font_if_mutated() {
1150 font.compute_font_hash();
1151 }
1152 }
1153 }
1154
1155 fn try_to_use_cached_reset_properties(
1156 &self,
1157 context: &mut computed::Context<'b>,
1158 cache: Option<&'b RuleCache>,
1159 guards: &StylesheetGuards,
1160 ) -> bool {
1161 let style = match self.first_line_reparenting {
1162 FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
1163 FirstLineReparenting::No => {
1164 let Some(cache) = cache else { return false };
1165 let Some(style) = cache.find(guards, &context) else {
1166 return false;
1167 };
1168 style
1169 },
1170 };
1171
1172 context.builder.copy_reset_from(style);
1173
1174 let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND
1186 | ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW
1187 | ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS
1188 | ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS
1189 | ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE
1190 | ComputedValueFlags::USES_CONTAINER_UNITS
1191 | ComputedValueFlags::USES_VIEWPORT_UNITS
1192 | ComputedValueFlags::USES_FONT_RELATIVE_UNITS
1193 | ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY;
1194 context.builder.add_flags(style.flags & bits_to_copy);
1195
1196 true
1197 }
1198
1199 #[inline]
1202 #[cfg(feature = "gecko")]
1203 fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
1204 use crate::gecko_bindings::bindings;
1205 use crate::values::computed::font::FontFamily;
1206
1207 let default_font_type = {
1208 let font = builder.get_font();
1209
1210 if !font.mFont.family.is_initial {
1211 return;
1212 }
1213
1214 let default_font_type = unsafe {
1215 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1216 builder.device.document(),
1217 font.mLanguage.mRawPtr,
1218 )
1219 };
1220
1221 let initial_generic = font.mFont.family.families.single_generic();
1222 debug_assert!(
1223 initial_generic.is_some(),
1224 "Initial font should be just one generic font"
1225 );
1226 if initial_generic == Some(default_font_type) {
1227 return;
1228 }
1229
1230 default_font_type
1231 };
1232
1233 builder.mutate_font().mFont.family.families =
1235 FontFamily::generic(default_font_type).families.clone();
1236 }
1237
1238 #[inline]
1240 #[cfg(feature = "gecko")]
1241 fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1242 use crate::gecko_bindings::bindings;
1243
1244 if static_prefs::pref!("browser.display.use_document_fonts") != 0
1247 || builder.device.chrome_rules_enabled_for_document()
1248 {
1249 return;
1250 }
1251
1252 let default_font_type = {
1253 let font = builder.get_font();
1254
1255 if font.mFont.family.is_system_font {
1256 return;
1257 }
1258
1259 if !font.mFont.family.families.needs_user_font_prioritization() {
1260 return;
1261 }
1262
1263 unsafe {
1264 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1265 builder.device.document(),
1266 font.mLanguage.mRawPtr,
1267 )
1268 }
1269 };
1270
1271 let font = builder.mutate_font();
1272 font.mFont
1273 .family
1274 .families
1275 .prioritize_first_generic_or_prepend(default_font_type);
1276 }
1277
1278 fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
1280 use crate::values::computed::ToComputedValue;
1281
1282 if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
1283 return;
1284 }
1285
1286 let new_size = {
1287 let font = context.builder.get_font();
1288 let info = font.clone_font_size().keyword_info;
1289 let new_size = match info.kw {
1290 specified::FontSizeKeyword::None => return,
1291 _ => {
1292 context.for_non_inherited_property = false;
1293 specified::FontSize::Keyword(info).to_computed_value(context)
1294 },
1295 };
1296
1297 #[cfg(feature = "gecko")]
1298 if font.mScriptUnconstrainedSize == new_size.computed_size {
1299 return;
1300 }
1301
1302 new_size
1303 };
1304
1305 context.builder.mutate_font().set_font_size(new_size);
1306 }
1307
1308 #[cfg(feature = "gecko")]
1311 fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
1312 use crate::gecko_bindings::bindings;
1313 use crate::values::generics::NonNegative;
1314
1315 let min_font_size = {
1316 let font = builder.get_font();
1317 let min_font_size = unsafe {
1318 bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
1319 };
1320
1321 if font.mFont.size.0 >= min_font_size {
1322 return;
1323 }
1324
1325 NonNegative(min_font_size)
1326 };
1327
1328 builder.mutate_font().mFont.size = min_font_size;
1329 }
1330
1331 #[cfg(feature = "gecko")]
1335 fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1336 debug_assert!(self.seen.contains(LonghandId::XTextScale));
1337
1338 let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
1339 let text_scale = builder.get_font().clone__x_text_scale();
1340 if parent_text_scale == text_scale {
1341 return;
1342 }
1343 debug_assert_ne!(
1344 parent_text_scale.text_zoom_enabled(),
1345 text_scale.text_zoom_enabled(),
1346 "There's only one value that disables it"
1347 );
1348 debug_assert!(
1349 !text_scale.text_zoom_enabled(),
1350 "We only ever disable text zoom never enable it"
1351 );
1352 let device = builder.device;
1353 builder.mutate_font().unzoom_fonts(device);
1354 }
1355
1356 fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
1357 debug_assert!(self.seen.contains(LonghandId::Zoom));
1358 let old_size = builder.get_font().clone_font_size();
1361 let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);
1362 if old_size == new_size {
1363 return;
1364 }
1365 builder.mutate_font().set_font_size(new_size);
1366 }
1367
1368 #[cfg(feature = "gecko")]
1373 fn recompute_math_font_size_if_needed(&self, context: &mut computed::Context) {
1374 use crate::values::generics::NonNegative;
1375
1376 if context.builder.get_font().clone_font_size().keyword_info.kw
1378 != specified::FontSizeKeyword::Math
1379 {
1380 return;
1381 }
1382
1383 const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
1384
1385 fn scale_factor_for_math_depth_change(
1395 parent_math_depth: i32,
1396 computed_math_depth: i32,
1397 parent_script_percent_scale_down: Option<f32>,
1398 parent_script_script_percent_scale_down: Option<f32>,
1399 ) -> f32 {
1400 let mut a = parent_math_depth;
1401 let mut b = computed_math_depth;
1402 let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
1403 let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c);
1404 let scale_between_0_and_2 =
1405 parent_script_script_percent_scale_down.unwrap_or_else(|| c * c);
1406 let mut s = 1.0;
1407 let mut invert_scale_factor = false;
1408 if a == b {
1409 return s;
1410 }
1411 if b < a {
1412 std::mem::swap(&mut a, &mut b);
1413 invert_scale_factor = true;
1414 }
1415 let mut e = b - a;
1416 if a <= 0 && b >= 2 {
1417 s *= scale_between_0_and_2;
1418 e -= 2;
1419 } else if a == 1 {
1420 s *= scale_between_0_and_2 / scale_between_0_and_1;
1421 e -= 1;
1422 } else if b == 1 {
1423 s *= scale_between_0_and_1;
1424 e -= 1;
1425 }
1426 s *= (c as f32).powi(e);
1427 if invert_scale_factor {
1428 1.0 / s.max(f32::MIN_POSITIVE)
1429 } else {
1430 s
1431 }
1432 }
1433
1434 let (new_size, new_unconstrained_size) = {
1435 use crate::values::specified::font::QueryFontMetricsFlags;
1436
1437 let builder = &context.builder;
1438 let font = builder.get_font();
1439 let parent_font = builder.get_parent_font();
1440
1441 let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1442
1443 if delta == 0 {
1444 return;
1445 }
1446
1447 let mut min = parent_font.mScriptMinSize;
1448 if font.mXTextScale.text_zoom_enabled() {
1449 min = builder.device.zoom_text(min);
1450 }
1451
1452 let scale = {
1454 let font_metrics = context.query_font_metrics(
1456 FontBaseSize::InheritedStyle,
1457 FontMetricsOrientation::Horizontal,
1458 QueryFontMetricsFlags::NEEDS_MATH_SCALES,
1459 );
1460 scale_factor_for_math_depth_change(
1461 parent_font.mMathDepth as i32,
1462 font.mMathDepth as i32,
1463 font_metrics.script_percent_scale_down,
1464 font_metrics.script_script_percent_scale_down,
1465 )
1466 };
1467
1468 let parent_size = parent_font.mSize.0;
1469 let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1470 let new_size = parent_size.scale_by(scale);
1471 let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1472
1473 if scale <= 1. {
1474 if parent_size <= min {
1479 (parent_size, new_unconstrained_size)
1480 } else {
1481 (min.max(new_size), new_unconstrained_size)
1482 }
1483 } else {
1484 (
1490 new_size.min(new_unconstrained_size.max(min)),
1491 new_unconstrained_size,
1492 )
1493 }
1494 };
1495 let font = context.builder.mutate_font();
1496 font.mFont.size = NonNegative(new_size);
1497 font.mSize = NonNegative(new_size);
1498 font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1499 }
1500}