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 CASCADE_PROPERTY, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, LonghandId,
19 LonghandIdSet, PrioritaryPropertyId, PropertyDeclaration, PropertyDeclarationId, PropertyFlags,
20 ShorthandsWithPropertyReferencesCache, StyleBuilder, property_counts,
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 mut attribute_tracker = match element {
315 Some(ref attr_provider) => AttributeTracker::new(attr_provider),
316 None => AttributeTracker::new_dummy(),
317 };
318
319 let properties_to_apply = match cascade_mode {
320 CascadeMode::Visited { unvisited_context } => {
321 context.builder.substitution_functions =
322 unvisited_context.builder.substitution_functions.clone();
323 context.builder.writing_mode = unvisited_context.builder.writing_mode;
324 context.builder.color_scheme = unvisited_context.builder.color_scheme;
325 using_cached_reset_properties = false;
329 iter_declarations(iter, &mut declarations, None, &mut attribute_tracker);
333
334 LonghandIdSet::visited_dependent()
335 },
336 CascadeMode::Unvisited { visited_rules } => {
337 let deferred_custom_properties = {
338 let mut builder = CustomPropertiesBuilder::new(stylist, &mut context);
339 iter_declarations(
340 iter,
341 &mut declarations,
342 Some(&mut builder),
343 &mut attribute_tracker,
344 );
345 builder.build(
350 DeferFontRelativeCustomPropertyResolution::Yes,
351 &mut attribute_tracker,
352 )
353 };
354
355 cascade.apply_prioritary_properties(
358 &mut context,
359 &declarations,
360 &mut shorthand_cache,
361 &mut attribute_tracker,
362 );
363
364 if let Some(deferred) = deferred_custom_properties {
366 CustomPropertiesBuilder::build_deferred(
367 deferred,
368 stylist,
369 &mut context,
370 &mut attribute_tracker,
371 );
372 }
373
374 if let Some(visited_rules) = visited_rules {
375 cascade.compute_visited_style_if_needed(
376 &mut context,
377 element,
378 parent_style,
379 layout_parent_style,
380 visited_rules,
381 guards,
382 );
383 }
384
385 using_cached_reset_properties =
386 cascade.try_to_use_cached_reset_properties(&mut context, rule_cache, guards);
387
388 if using_cached_reset_properties {
389 LonghandIdSet::late_group_only_inherited()
390 } else {
391 LonghandIdSet::late_group()
392 }
393 },
394 };
395
396 cascade.apply_non_prioritary_properties(
397 &mut context,
398 &declarations.longhand_declarations,
399 &mut shorthand_cache,
400 &properties_to_apply,
401 &mut attribute_tracker,
402 );
403
404 context.builder.attribute_references = attribute_tracker.finalize();
405
406 cascade.finished_applying_properties(&mut context.builder);
407
408 context.builder.clear_modified_reset();
409
410 if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
411 StyleAdjuster::new(&mut context.builder).adjust(
412 layout_parent_style.unwrap_or(inherited_style),
413 element,
414 try_tactic,
415 &cascade.author_specified,
416 );
417 }
418
419 if context.builder.modified_reset() || using_cached_reset_properties {
420 context.rule_cache_conditions.borrow_mut().set_uncacheable();
426 }
427
428 context.builder.build()
429}
430
431type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
437
438#[cfg(feature = "gecko")]
439fn is_base_appearance(context: &computed::Context) -> bool {
440 use computed::Appearance;
441 let box_style = context.builder.get_box();
442 match box_style.clone_appearance() {
443 Appearance::BaseSelect => {
444 matches!(
445 box_style.clone__moz_default_appearance(),
446 Appearance::Listbox | Appearance::Menulist
447 )
448 },
449 Appearance::Base => box_style.clone__moz_default_appearance() != Appearance::None,
450 _ => false,
451 }
452}
453
454fn tweak_when_ignoring_colors(
455 context: &computed::Context,
456 longhand_id: LonghandId,
457 origin: CascadeOrigin,
458 declaration: &mut Cow<PropertyDeclaration>,
459 declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
460) {
461 use crate::values::computed::ToComputedValue;
462 use crate::values::specified::Color;
463
464 if !longhand_id.ignored_when_document_colors_disabled() {
465 return;
466 }
467
468 let is_ua_or_user_rule = matches!(origin, CascadeOrigin::User | CascadeOrigin::UA);
469 if is_ua_or_user_rule {
470 return;
471 }
472
473 #[cfg(feature = "gecko")]
475 {
476 let forced = context
477 .builder
478 .get_inherited_text()
479 .clone_forced_color_adjust();
480 if forced == computed::ForcedColorAdjust::None {
481 return;
482 }
483 }
484
485 fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
486 color
488 .to_computed_value(context)
489 .resolve_to_absolute(&AbsoluteColor::BLACK)
490 .alpha
491 }
492
493 match **declaration {
495 PropertyDeclaration::CSSWideKeyword(..) => return,
497 PropertyDeclaration::BackgroundColor(ref color) => {
498 if color.honored_in_forced_colors_mode(true) {
508 return;
509 }
510 let alpha = alpha_channel(color, context);
514 if alpha == 0.0 {
515 return;
516 }
517 let mut color = context.builder.device.default_background_color();
518 color.alpha = alpha;
519 declarations_to_apply_unless_overridden
520 .push(PropertyDeclaration::BackgroundColor(color.into()))
521 },
522 PropertyDeclaration::Color(ref color) => {
523 if color
525 .0
526 .honored_in_forced_colors_mode(true)
527 {
528 return;
529 }
530 if context
534 .builder
535 .get_parent_inherited_text()
536 .clone_color()
537 .alpha
538 == 0.0
539 {
540 let color = context.builder.device.default_color();
541 declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
542 specified::ColorPropertyValue(color.into()),
543 ))
544 }
545 },
546 #[cfg(feature = "gecko")]
548 PropertyDeclaration::BackgroundImage(ref bkg) => {
549 use crate::values::generics::image::Image;
550 if static_prefs::pref!("browser.display.permit_backplate") {
551 if bkg
552 .0
553 .iter()
554 .all(|image| matches!(*image, Image::Url(..) | Image::None))
555 {
556 return;
557 }
558 }
559 },
560 _ => {
561 if let Some(color) = declaration.color_value() {
574 if color.honored_in_forced_colors_mode(false) {
575 return;
576 }
577 }
578 },
579 }
580
581 *declaration.to_mut() =
582 PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
583}
584
585type DeclarationIndex = u16;
587
588#[derive(Copy, Clone)]
593struct PrioritaryDeclarationPosition {
594 most_important: DeclarationIndex,
596 least_important: DeclarationIndex,
597}
598
599impl Default for PrioritaryDeclarationPosition {
600 fn default() -> Self {
601 Self {
602 most_important: DeclarationIndex::MAX,
603 least_important: DeclarationIndex::MAX,
604 }
605 }
606}
607
608#[derive(Copy, Clone)]
609struct Declaration<'a> {
610 decl: &'a PropertyDeclaration,
611 priority: CascadePriority,
612 next_index: DeclarationIndex,
613}
614
615#[derive(Default)]
617struct Declarations<'a> {
618 has_prioritary_properties: bool,
620 longhand_declarations: SmallVec<[Declaration<'a>; 64]>,
622 prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
624}
625
626impl<'a> Declarations<'a> {
627 fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
628 let new_index = self.longhand_declarations.len();
629 if new_index >= DeclarationIndex::MAX as usize {
630 return;
633 }
634
635 self.has_prioritary_properties = true;
636 let new_index = new_index as DeclarationIndex;
637 let position = &mut self.prioritary_positions[id as usize];
638 if position.most_important == DeclarationIndex::MAX {
639 position.most_important = new_index;
642 } else {
643 self.longhand_declarations[position.least_important as usize].next_index = new_index;
645 }
646 position.least_important = new_index;
647 }
648
649 fn note_declaration(
650 &mut self,
651 decl: &'a PropertyDeclaration,
652 priority: CascadePriority,
653 id: LonghandId,
654 ) {
655 if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
656 self.note_prioritary_property(id);
657 }
658 self.longhand_declarations.push(Declaration {
659 decl,
660 priority,
661 next_index: 0,
662 });
663 }
664}
665
666struct Cascade<'b> {
667 first_line_reparenting: FirstLineReparenting<'b>,
668 try_tactic: &'b PositionTryFallbacksTryTactic,
669 ignore_colors: bool,
670 seen: LonghandIdSet,
671 author_specified: LonghandIdSet,
672 reverted_set: LonghandIdSet,
673 reverted: FxHashMap<LonghandId, (CascadePriority, RevertKind)>,
674 declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
675}
676
677impl<'b> Cascade<'b> {
678 fn new(
679 first_line_reparenting: FirstLineReparenting<'b>,
680 try_tactic: &'b PositionTryFallbacksTryTactic,
681 ignore_colors: bool,
682 ) -> Self {
683 Self {
684 first_line_reparenting,
685 try_tactic,
686 ignore_colors,
687 seen: LonghandIdSet::default(),
688 author_specified: LonghandIdSet::default(),
689 reverted_set: Default::default(),
690 reverted: Default::default(),
691 declarations_to_apply_unless_overridden: Default::default(),
692 }
693 }
694
695 fn substitute_variables_if_needed<'cache, 'decl>(
696 &self,
697 context: &mut computed::Context,
698 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
699 declaration: &'decl PropertyDeclaration,
700 attribute_tracker: &mut AttributeTracker,
701 ) -> Cow<'decl, PropertyDeclaration>
702 where
703 'cache: 'decl,
704 {
705 let declaration = match *declaration {
706 PropertyDeclaration::WithVariables(ref declaration) => declaration,
707 ref d => return Cow::Borrowed(d),
708 };
709
710 if !declaration.id.inherited() {
711 context.rule_cache_conditions.borrow_mut().set_uncacheable();
712
713 if matches!(declaration.id, LonghandId::Display | LonghandId::Content) {
719 context
720 .builder
721 .add_flags(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE);
722 }
723 }
724
725 debug_assert!(
726 context.builder.stylist.is_some(),
727 "Need a Stylist to substitute variables!"
728 );
729 declaration.value.substitute_variables(
730 declaration.id,
731 &context.builder.substitution_functions(),
732 context.builder.stylist.unwrap(),
733 context,
734 shorthand_cache,
735 attribute_tracker,
736 )
737 }
738
739 fn apply_one_prioritary_property(
740 &mut self,
741 context: &mut computed::Context,
742 decls: &Declarations,
743 cache: &mut ShorthandsWithPropertyReferencesCache,
744 id: PrioritaryPropertyId,
745 attr_provider: &mut AttributeTracker,
746 ) -> bool {
747 let mut index = decls.prioritary_positions[id as usize].most_important;
748 if index == DeclarationIndex::MAX {
749 return false;
750 }
751
752 let longhand_id = id.to_longhand();
753 debug_assert!(
754 !longhand_id.is_logical(),
755 "That could require more book-keeping"
756 );
757 loop {
758 let decl = decls.longhand_declarations[index as usize];
759 self.apply_one_longhand(
760 context,
761 longhand_id,
762 decl.decl,
763 decl.priority,
764 cache,
765 attr_provider,
766 );
767 if self.seen.contains(longhand_id) {
768 return true; }
770 debug_assert!(
771 decl.next_index == 0 || decl.next_index > index,
772 "should make progress! {} -> {}",
773 index,
774 decl.next_index,
775 );
776 index = decl.next_index;
777 if index == 0 {
778 break;
779 }
780 }
781 false
782 }
783
784 fn apply_prioritary_properties(
785 &mut self,
786 context: &mut computed::Context,
787 decls: &Declarations,
788 cache: &mut ShorthandsWithPropertyReferencesCache,
789 attribute_tracker: &mut AttributeTracker,
790 ) {
791 macro_rules! apply {
794 ($prop:ident) => {
795 self.apply_one_prioritary_property(
796 context,
797 decls,
798 cache,
799 PrioritaryPropertyId::$prop,
800 attribute_tracker,
801 )
802 };
803 }
804
805 if !decls.has_prioritary_properties {
806 return;
807 }
808
809 #[cfg(feature = "gecko")]
810 apply!(MozDefaultAppearance);
811 #[cfg(feature = "gecko")]
812 if apply!(Appearance) && is_base_appearance(&context) {
813 context
814 .style()
815 .add_flags(ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE);
816 context
817 .included_cascade_flags
818 .insert(RuleCascadeFlags::APPEARANCE_BASE);
819 }
820
821 let has_writing_mode = apply!(WritingMode) | apply!(Direction);
822 #[cfg(feature = "gecko")]
823 let has_writing_mode = has_writing_mode | apply!(TextOrientation);
824
825 if has_writing_mode {
826 context.builder.writing_mode = WritingMode::new(context.builder.get_inherited_box())
827 }
828
829 if apply!(Zoom) {
830 context.builder.recompute_effective_zooms();
831 if !context.builder.effective_zoom_for_inheritance.is_one() {
832 self.recompute_font_size_for_zoom_change(&mut context.builder);
838 }
839 }
840
841 let has_font_family = apply!(FontFamily);
843 let has_lang = apply!(XLang);
844 #[cfg(feature = "gecko")]
845 {
846 if has_lang {
847 self.recompute_initial_font_family_if_needed(&mut context.builder);
848 }
849 if has_font_family {
850 self.prioritize_user_fonts_if_needed(&mut context.builder);
851 }
852
853 if apply!(XTextScale) {
855 self.unzoom_fonts_if_needed(&mut context.builder);
856 }
857 let has_font_size = apply!(FontSize);
858 let has_math_depth = apply!(MathDepth);
859 let has_min_font_size_ratio = apply!(MozMinFontSizeRatio);
860
861 if has_math_depth && has_font_size {
862 self.recompute_math_font_size_if_needed(context);
863 }
864 if has_lang || has_font_family {
865 self.recompute_keyword_font_size_if_needed(context);
866 }
867 if has_font_size || has_min_font_size_ratio || has_lang || has_font_family {
868 self.constrain_font_size_if_needed(&mut context.builder);
869 }
870 }
871
872 #[cfg(feature = "servo")]
873 {
874 apply!(FontSize);
875 if has_lang || has_font_family {
876 self.recompute_keyword_font_size_if_needed(context);
877 }
878 }
879
880 apply!(FontWeight);
882 apply!(FontStretch);
883 apply!(FontStyle);
884 #[cfg(feature = "gecko")]
885 apply!(FontSizeAdjust);
886
887 #[cfg(feature = "gecko")]
888 apply!(ForcedColorAdjust);
889 if apply!(ColorScheme) {
892 context.builder.color_scheme = context.builder.get_inherited_ui().color_scheme_bits();
893 }
894 apply!(LineHeight);
895 }
896
897 fn apply_non_prioritary_properties(
898 &mut self,
899 context: &mut computed::Context,
900 longhand_declarations: &[Declaration],
901 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
902 properties_to_apply: &LonghandIdSet,
903 attribute_tracker: &mut AttributeTracker,
904 ) {
905 debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
906 debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
907 for declaration in &*longhand_declarations {
908 let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
909 if !properties_to_apply.contains(longhand_id) {
910 continue;
911 }
912 debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
913 let is_logical = longhand_id.is_logical();
914 if is_logical {
915 let wm = context.builder.writing_mode;
916 context
917 .rule_cache_conditions
918 .borrow_mut()
919 .set_writing_mode_dependency(wm);
920 longhand_id = longhand_id.to_physical(wm);
921 }
922 self.apply_one_longhand(
923 context,
924 longhand_id,
925 declaration.decl,
926 declaration.priority,
927 shorthand_cache,
928 attribute_tracker,
929 );
930 }
931 if !self.declarations_to_apply_unless_overridden.is_empty() {
932 debug_assert!(self.ignore_colors);
933 for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
934 let longhand_id = declaration.id().as_longhand().unwrap();
935 debug_assert!(!longhand_id.is_logical());
936 if !self.seen.contains(longhand_id) {
937 unsafe {
938 self.do_apply_declaration(context, longhand_id, &declaration);
939 }
940 }
941 }
942 }
943
944 if !context.builder.effective_zoom_for_inheritance.is_one() {
945 self.recompute_zoom_dependent_inherited_lengths(context);
946 }
947 }
948
949 #[cold]
950 fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {
951 debug_assert!(self.seen.contains(LonghandId::Zoom));
952 for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {
953 if self.seen.contains(prop) {
954 continue;
955 }
956 let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);
957 unsafe {
958 self.do_apply_declaration(context, prop, &declaration);
959 }
960 }
961 }
962
963 fn apply_one_longhand(
964 &mut self,
965 context: &mut computed::Context,
966 longhand_id: LonghandId,
967 declaration: &PropertyDeclaration,
968 priority: CascadePriority,
969 cache: &mut ShorthandsWithPropertyReferencesCache,
970 attribute_tracker: &mut AttributeTracker,
971 ) {
972 debug_assert!(!longhand_id.is_logical());
973 if self.seen.contains(longhand_id) {
974 return;
975 }
976
977 if !(priority.flags() - context.included_cascade_flags).is_empty() {
978 return;
979 }
980
981 if self.reverted_set.contains(longhand_id) {
982 if let Some(&(reverted_priority, revert_kind)) = self.reverted.get(&longhand_id) {
983 if !reverted_priority.allows_when_reverted(&priority, revert_kind) {
984 return;
985 }
986 }
987 }
988
989 let mut declaration =
990 self.substitute_variables_if_needed(context, cache, declaration, attribute_tracker);
991
992 let origin = priority.cascade_level().origin();
995 if self.ignore_colors {
996 tweak_when_ignoring_colors(
997 context,
998 longhand_id,
999 origin,
1000 &mut declaration,
1001 &mut self.declarations_to_apply_unless_overridden,
1002 );
1003 }
1004 let can_skip_apply = match declaration.get_css_wide_keyword() {
1005 Some(keyword) => {
1006 if let Some(revert_kind) = keyword.revert_kind() {
1007 self.reverted_set.insert(longhand_id);
1010 self.reverted.insert(longhand_id, (priority, revert_kind));
1011 return;
1012 }
1013
1014 let inherited = longhand_id.inherited();
1015 let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()
1016 && longhand_id.zoom_dependent();
1017 match keyword {
1018 CSSWideKeyword::Revert
1019 | CSSWideKeyword::RevertLayer
1020 | CSSWideKeyword::RevertRule => unreachable!(),
1021 CSSWideKeyword::Unset => !zoomed || !inherited,
1022 CSSWideKeyword::Inherit => inherited && !zoomed,
1023 CSSWideKeyword::Initial => !inherited,
1024 }
1025 },
1026 None => false,
1027 };
1028
1029 self.seen.insert(longhand_id);
1030 if origin.is_author_origin() {
1031 self.author_specified.insert(longhand_id);
1032 }
1033
1034 if !can_skip_apply {
1035 let old_scope = context.scope;
1039 let cascade_level = priority.cascade_level();
1040 context.scope = cascade_level;
1041 unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
1042 context.scope = old_scope;
1043 }
1044 }
1045
1046 #[inline]
1047 unsafe fn do_apply_declaration(
1048 &self,
1049 context: &mut computed::Context,
1050 longhand_id: LonghandId,
1051 declaration: &PropertyDeclaration,
1052 ) {
1053 debug_assert!(!longhand_id.is_logical());
1054 (CASCADE_PROPERTY[longhand_id as usize])(&declaration, context);
1060 }
1061
1062 fn compute_visited_style_if_needed<E>(
1063 &self,
1064 context: &mut computed::Context,
1065 element: Option<E>,
1066 parent_style: Option<&ComputedValues>,
1067 layout_parent_style: Option<&ComputedValues>,
1068 visited_rules: &StrongRuleNode,
1069 guards: &StylesheetGuards,
1070 ) where
1071 E: TElement,
1072 {
1073 let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
1074
1075 macro_rules! visited_parent {
1076 ($parent:expr) => {
1077 if is_link {
1078 $parent
1079 } else {
1080 $parent.map(|p| p.visited_style().unwrap_or(p))
1081 }
1082 };
1083 }
1084
1085 let style = cascade_rules(
1088 context.builder.stylist.unwrap(),
1089 context.builder.pseudo,
1090 visited_rules,
1091 guards,
1092 visited_parent!(parent_style),
1093 visited_parent!(layout_parent_style),
1094 self.first_line_reparenting,
1095 self.try_tactic,
1096 CascadeMode::Visited {
1097 unvisited_context: &*context,
1098 },
1099 Default::default(),
1102 context.included_cascade_flags,
1103 None, &mut *context.rule_cache_conditions.borrow_mut(),
1109 element,
1110 );
1111 context.builder.visited_style = Some(style);
1112 }
1113
1114 fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
1115 #[cfg(feature = "gecko")]
1116 {
1117 if let Some(bg) = builder.get_background_if_mutated() {
1118 bg.fill_arrays();
1119 }
1120
1121 if let Some(svg) = builder.get_svg_if_mutated() {
1122 svg.fill_arrays();
1123 }
1124 }
1125
1126 if self
1127 .author_specified
1128 .contains_any(LonghandIdSet::border_background_properties())
1129 {
1130 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
1131 }
1132
1133 if self.author_specified.contains(LonghandId::Color) {
1134 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);
1135 }
1136
1137 if self.author_specified.contains(LonghandId::TextShadow) {
1138 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_SHADOW);
1139 }
1140
1141 if self.author_specified.contains(LonghandId::GridAutoFlow) {
1142 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW);
1143 }
1144 #[cfg(feature = "servo")]
1145 {
1146 if let Some(font) = builder.get_font_if_mutated() {
1147 font.compute_font_hash();
1148 }
1149 }
1150 }
1151
1152 fn try_to_use_cached_reset_properties(
1153 &self,
1154 context: &mut computed::Context<'b>,
1155 cache: Option<&'b RuleCache>,
1156 guards: &StylesheetGuards,
1157 ) -> bool {
1158 let style = match self.first_line_reparenting {
1159 FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
1160 FirstLineReparenting::No => {
1161 let Some(cache) = cache else { return false };
1162 let Some(style) = cache.find(guards, &context) else {
1163 return false;
1164 };
1165 style
1166 },
1167 };
1168
1169 context.builder.copy_reset_from(style);
1170
1171 let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND
1183 | ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW
1184 | ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS
1185 | ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS
1186 | ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE
1187 | ComputedValueFlags::USES_CONTAINER_UNITS
1188 | ComputedValueFlags::USES_VIEWPORT_UNITS
1189 | ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY;
1190 context.builder.add_flags(style.flags & bits_to_copy);
1191
1192 true
1193 }
1194
1195 #[inline]
1198 #[cfg(feature = "gecko")]
1199 fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
1200 use crate::gecko_bindings::bindings;
1201 use crate::values::computed::font::FontFamily;
1202
1203 let default_font_type = {
1204 let font = builder.get_font();
1205
1206 if !font.mFont.family.is_initial {
1207 return;
1208 }
1209
1210 let default_font_type = unsafe {
1211 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1212 builder.device.document(),
1213 font.mLanguage.mRawPtr,
1214 )
1215 };
1216
1217 let initial_generic = font.mFont.family.families.single_generic();
1218 debug_assert!(
1219 initial_generic.is_some(),
1220 "Initial font should be just one generic font"
1221 );
1222 if initial_generic == Some(default_font_type) {
1223 return;
1224 }
1225
1226 default_font_type
1227 };
1228
1229 builder.mutate_font().mFont.family.families =
1231 FontFamily::generic(default_font_type).families.clone();
1232 }
1233
1234 #[inline]
1236 #[cfg(feature = "gecko")]
1237 fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1238 use crate::gecko_bindings::bindings;
1239
1240 if static_prefs::pref!("browser.display.use_document_fonts") != 0
1243 || builder.device.chrome_rules_enabled_for_document()
1244 {
1245 return;
1246 }
1247
1248 let default_font_type = {
1249 let font = builder.get_font();
1250
1251 if font.mFont.family.is_system_font {
1252 return;
1253 }
1254
1255 if !font.mFont.family.families.needs_user_font_prioritization() {
1256 return;
1257 }
1258
1259 unsafe {
1260 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1261 builder.device.document(),
1262 font.mLanguage.mRawPtr,
1263 )
1264 }
1265 };
1266
1267 let font = builder.mutate_font();
1268 font.mFont
1269 .family
1270 .families
1271 .prioritize_first_generic_or_prepend(default_font_type);
1272 }
1273
1274 fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
1276 use crate::values::computed::ToComputedValue;
1277
1278 if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
1279 return;
1280 }
1281
1282 let new_size = {
1283 let font = context.builder.get_font();
1284 let info = font.clone_font_size().keyword_info;
1285 let new_size = match info.kw {
1286 specified::FontSizeKeyword::None => return,
1287 _ => {
1288 context.for_non_inherited_property = false;
1289 specified::FontSize::Keyword(info).to_computed_value(context)
1290 },
1291 };
1292
1293 #[cfg(feature = "gecko")]
1294 if font.mScriptUnconstrainedSize == new_size.computed_size {
1295 return;
1296 }
1297
1298 new_size
1299 };
1300
1301 context.builder.mutate_font().set_font_size(new_size);
1302 }
1303
1304 #[cfg(feature = "gecko")]
1307 fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
1308 use crate::gecko_bindings::bindings;
1309 use crate::values::generics::NonNegative;
1310
1311 let min_font_size = {
1312 let font = builder.get_font();
1313 let min_font_size = unsafe {
1314 bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
1315 };
1316
1317 if font.mFont.size.0 >= min_font_size {
1318 return;
1319 }
1320
1321 NonNegative(min_font_size)
1322 };
1323
1324 builder.mutate_font().mFont.size = min_font_size;
1325 }
1326
1327 #[cfg(feature = "gecko")]
1331 fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1332 debug_assert!(self.seen.contains(LonghandId::XTextScale));
1333
1334 let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
1335 let text_scale = builder.get_font().clone__x_text_scale();
1336 if parent_text_scale == text_scale {
1337 return;
1338 }
1339 debug_assert_ne!(
1340 parent_text_scale.text_zoom_enabled(),
1341 text_scale.text_zoom_enabled(),
1342 "There's only one value that disables it"
1343 );
1344 debug_assert!(
1345 !text_scale.text_zoom_enabled(),
1346 "We only ever disable text zoom never enable it"
1347 );
1348 let device = builder.device;
1349 builder.mutate_font().unzoom_fonts(device);
1350 }
1351
1352 fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
1353 debug_assert!(self.seen.contains(LonghandId::Zoom));
1354 let old_size = builder.get_font().clone_font_size();
1357 let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);
1358 if old_size == new_size {
1359 return;
1360 }
1361 builder.mutate_font().set_font_size(new_size);
1362 }
1363
1364 #[cfg(feature = "gecko")]
1369 fn recompute_math_font_size_if_needed(&self, context: &mut computed::Context) {
1370 use crate::values::generics::NonNegative;
1371
1372 if context.builder.get_font().clone_font_size().keyword_info.kw
1374 != specified::FontSizeKeyword::Math
1375 {
1376 return;
1377 }
1378
1379 const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
1380
1381 fn scale_factor_for_math_depth_change(
1391 parent_math_depth: i32,
1392 computed_math_depth: i32,
1393 parent_script_percent_scale_down: Option<f32>,
1394 parent_script_script_percent_scale_down: Option<f32>,
1395 ) -> f32 {
1396 let mut a = parent_math_depth;
1397 let mut b = computed_math_depth;
1398 let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
1399 let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c);
1400 let scale_between_0_and_2 =
1401 parent_script_script_percent_scale_down.unwrap_or_else(|| c * c);
1402 let mut s = 1.0;
1403 let mut invert_scale_factor = false;
1404 if a == b {
1405 return s;
1406 }
1407 if b < a {
1408 std::mem::swap(&mut a, &mut b);
1409 invert_scale_factor = true;
1410 }
1411 let mut e = b - a;
1412 if a <= 0 && b >= 2 {
1413 s *= scale_between_0_and_2;
1414 e -= 2;
1415 } else if a == 1 {
1416 s *= scale_between_0_and_2 / scale_between_0_and_1;
1417 e -= 1;
1418 } else if b == 1 {
1419 s *= scale_between_0_and_1;
1420 e -= 1;
1421 }
1422 s *= (c as f32).powi(e);
1423 if invert_scale_factor {
1424 1.0 / s.max(f32::MIN_POSITIVE)
1425 } else {
1426 s
1427 }
1428 }
1429
1430 let (new_size, new_unconstrained_size) = {
1431 use crate::values::specified::font::QueryFontMetricsFlags;
1432
1433 let builder = &context.builder;
1434 let font = builder.get_font();
1435 let parent_font = builder.get_parent_font();
1436
1437 let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1438
1439 if delta == 0 {
1440 return;
1441 }
1442
1443 let mut min = parent_font.mScriptMinSize;
1444 if font.mXTextScale.text_zoom_enabled() {
1445 min = builder.device.zoom_text(min);
1446 }
1447
1448 let scale = {
1450 let font_metrics = context.query_font_metrics(
1452 FontBaseSize::InheritedStyle,
1453 FontMetricsOrientation::Horizontal,
1454 QueryFontMetricsFlags::NEEDS_MATH_SCALES,
1455 );
1456 scale_factor_for_math_depth_change(
1457 parent_font.mMathDepth as i32,
1458 font.mMathDepth as i32,
1459 font_metrics.script_percent_scale_down,
1460 font_metrics.script_script_percent_scale_down,
1461 )
1462 };
1463
1464 let parent_size = parent_font.mSize.0;
1465 let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1466 let new_size = parent_size.scale_by(scale);
1467 let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1468
1469 if scale <= 1. {
1470 if parent_size <= min {
1475 (parent_size, new_unconstrained_size)
1476 } else {
1477 (min.max(new_size), new_unconstrained_size)
1478 }
1479 } else {
1480 (
1486 new_size.min(new_unconstrained_size.max(min)),
1487 new_unconstrained_size,
1488 )
1489 }
1490 };
1491 let font = context.builder.mutate_font();
1492 font.mFont.size = NonNegative(new_size);
1493 font.mSize = NonNegative(new_size);
1494 font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1495 }
1496}