1use crate::applicable_declarations::CascadePriority;
8use crate::color::AbsoluteColor;
9use crate::computed_value_flags::ComputedValueFlags;
10use crate::custom_properties::{
11 CustomPropertiesBuilder, DeferFontRelativeCustomPropertyResolution,
12};
13use crate::dom::TElement;
14#[cfg(feature = "gecko")]
15use crate::font_metrics::FontMetricsOrientation;
16use crate::logical_geometry::WritingMode;
17use crate::properties::{
18 property_counts, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, Importance,
19 LonghandId, LonghandIdSet, PrioritaryPropertyId, PropertyDeclaration, PropertyDeclarationId,
20 PropertyFlags, ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
21};
22use crate::rule_cache::{RuleCache, RuleCacheConditions};
23use crate::rule_tree::{CascadeLevel, 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, Origin};
29use crate::stylist::Stylist;
30#[cfg(feature = "gecko")]
31use crate::values::specified::length::FontBaseSize;
32use crate::values::{computed, specified};
33use fxhash::FxHashMap;
34use servo_arc::Arc;
35use smallvec::SmallVec;
36use std::borrow::Cow;
37
38#[derive(Copy, Clone)]
40#[allow(missing_docs)]
41pub enum FirstLineReparenting<'a> {
42 No,
43 Yes {
44 style_to_reparent: &'a ComputedValues,
48 },
49}
50
51pub fn cascade<E>(
66 stylist: &Stylist,
67 pseudo: Option<&PseudoElement>,
68 rule_node: &StrongRuleNode,
69 guards: &StylesheetGuards,
70 parent_style: Option<&ComputedValues>,
71 layout_parent_style: Option<&ComputedValues>,
72 first_line_reparenting: FirstLineReparenting,
73 visited_rules: Option<&StrongRuleNode>,
74 cascade_input_flags: ComputedValueFlags,
75 rule_cache: Option<&RuleCache>,
76 rule_cache_conditions: &mut RuleCacheConditions,
77 element: Option<E>,
78) -> Arc<ComputedValues>
79where
80 E: TElement,
81{
82 cascade_rules(
83 stylist,
84 pseudo,
85 rule_node,
86 guards,
87 parent_style,
88 layout_parent_style,
89 first_line_reparenting,
90 CascadeMode::Unvisited { visited_rules },
91 cascade_input_flags,
92 rule_cache,
93 rule_cache_conditions,
94 element,
95 )
96}
97
98struct DeclarationIterator<'a> {
99 guards: &'a StylesheetGuards<'a>,
101 restriction: Option<PropertyFlags>,
102 current_rule_node: Option<&'a StrongRuleNode>,
104 declarations: DeclarationImportanceIterator<'a>,
106 origin: Origin,
107 importance: Importance,
108 priority: CascadePriority,
109}
110
111impl<'a> DeclarationIterator<'a> {
112 #[inline]
113 fn new(
114 rule_node: &'a StrongRuleNode,
115 guards: &'a StylesheetGuards,
116 pseudo: Option<&PseudoElement>,
117 ) -> Self {
118 let restriction = pseudo.and_then(|p| p.property_restriction());
119 let mut iter = Self {
120 guards,
121 current_rule_node: Some(rule_node),
122 origin: Origin::UserAgent,
123 importance: Importance::Normal,
124 priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()),
125 declarations: DeclarationImportanceIterator::default(),
126 restriction,
127 };
128 iter.update_for_node(rule_node);
129 iter
130 }
131
132 fn update_for_node(&mut self, node: &'a StrongRuleNode) {
133 self.priority = node.cascade_priority();
134 let level = self.priority.cascade_level();
135 self.origin = level.origin();
136 self.importance = level.importance();
137 let guard = match self.origin {
138 Origin::Author => self.guards.author,
139 Origin::User | Origin::UserAgent => self.guards.ua_or_user,
140 };
141 self.declarations = match node.style_source() {
142 Some(source) => source.read(guard).declaration_importance_iter(),
143 None => DeclarationImportanceIterator::default(),
144 };
145 }
146}
147
148impl<'a> Iterator for DeclarationIterator<'a> {
149 type Item = (&'a PropertyDeclaration, CascadePriority);
150
151 #[inline]
152 fn next(&mut self) -> Option<Self::Item> {
153 loop {
154 if let Some((decl, importance)) = self.declarations.next_back() {
155 if self.importance != importance {
156 continue;
157 }
158
159 if let Some(restriction) = self.restriction {
160 if let PropertyDeclarationId::Longhand(id) = decl.id() {
165 if !id.flags().contains(restriction) && self.origin != Origin::UserAgent {
166 continue;
167 }
168 }
169 }
170
171 return Some((decl, self.priority));
172 }
173
174 let next_node = self.current_rule_node.take()?.parent()?;
175 self.current_rule_node = Some(next_node);
176 self.update_for_node(next_node);
177 }
178 }
179}
180
181fn cascade_rules<E>(
182 stylist: &Stylist,
183 pseudo: Option<&PseudoElement>,
184 rule_node: &StrongRuleNode,
185 guards: &StylesheetGuards,
186 parent_style: Option<&ComputedValues>,
187 layout_parent_style: Option<&ComputedValues>,
188 first_line_reparenting: FirstLineReparenting,
189 cascade_mode: CascadeMode,
190 cascade_input_flags: ComputedValueFlags,
191 rule_cache: Option<&RuleCache>,
192 rule_cache_conditions: &mut RuleCacheConditions,
193 element: Option<E>,
194) -> Arc<ComputedValues>
195where
196 E: TElement,
197{
198 apply_declarations(
199 stylist,
200 pseudo,
201 rule_node,
202 guards,
203 DeclarationIterator::new(rule_node, guards, pseudo),
204 parent_style,
205 layout_parent_style,
206 first_line_reparenting,
207 cascade_mode,
208 cascade_input_flags,
209 rule_cache,
210 rule_cache_conditions,
211 element,
212 )
213}
214
215#[derive(Clone, Copy)]
217pub enum CascadeMode<'a, 'b> {
218 Unvisited {
220 visited_rules: Option<&'a StrongRuleNode>,
222 },
223 Visited {
225 unvisited_context: &'a computed::Context<'b>,
227 },
228}
229
230fn iter_declarations<'builder, 'decls: 'builder>(
231 iter: impl Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
232 declarations: &mut Declarations<'decls>,
233 mut custom_builder: Option<&mut CustomPropertiesBuilder<'builder, 'decls>>,
234) {
235 for (declaration, priority) in iter {
236 if let PropertyDeclaration::Custom(ref declaration) = *declaration {
237 if let Some(ref mut builder) = custom_builder {
238 builder.cascade(declaration, priority);
239 }
240 } else {
241 let id = declaration.id().as_longhand().unwrap();
242 declarations.note_declaration(declaration, priority, id);
243 if CustomPropertiesBuilder::might_have_non_custom_dependency(id, declaration) {
244 if let Some(ref mut builder) = custom_builder {
245 builder.maybe_note_non_custom_dependency(id, declaration);
246 }
247 }
248 }
249 }
250}
251
252pub fn apply_declarations<'a, E, I>(
255 stylist: &'a Stylist,
256 pseudo: Option<&'a PseudoElement>,
257 rules: &StrongRuleNode,
258 guards: &StylesheetGuards,
259 iter: I,
260 parent_style: Option<&'a ComputedValues>,
261 layout_parent_style: Option<&ComputedValues>,
262 first_line_reparenting: FirstLineReparenting<'a>,
263 cascade_mode: CascadeMode,
264 cascade_input_flags: ComputedValueFlags,
265 rule_cache: Option<&'a RuleCache>,
266 rule_cache_conditions: &'a mut RuleCacheConditions,
267 element: Option<E>,
268) -> Arc<ComputedValues>
269where
270 E: TElement + 'a,
271 I: Iterator<Item = (&'a PropertyDeclaration, CascadePriority)>,
272{
273 debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
274 let device = stylist.device();
275 let inherited_style = parent_style.unwrap_or(device.default_computed_values());
276 let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
277
278 let container_size_query =
279 ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());
280
281 let mut context = computed::Context::new(
282 StyleBuilder::new(
286 device,
287 Some(stylist),
288 parent_style,
289 pseudo,
290 Some(rules.clone()),
291 is_root_element,
292 ),
293 stylist.quirks_mode(),
294 rule_cache_conditions,
295 container_size_query,
296 );
297
298 context.style().add_flags(cascade_input_flags);
299
300 let using_cached_reset_properties;
301 let ignore_colors = context.builder.device.forced_colors().is_active();
302 let mut cascade = Cascade::new(first_line_reparenting, ignore_colors);
303 let mut declarations = Default::default();
304 let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
305 let properties_to_apply = match cascade_mode {
306 CascadeMode::Visited { unvisited_context } => {
307 context.builder.custom_properties = unvisited_context.builder.custom_properties.clone();
308 context.builder.writing_mode = unvisited_context.builder.writing_mode;
309 context.builder.color_scheme = unvisited_context.builder.color_scheme;
310 using_cached_reset_properties = false;
314 iter_declarations(iter, &mut declarations, None);
318
319 LonghandIdSet::visited_dependent()
320 },
321 CascadeMode::Unvisited { visited_rules } => {
322 let deferred_custom_properties = {
323 let mut builder = CustomPropertiesBuilder::new(stylist, &mut context);
324 iter_declarations(iter, &mut declarations, Some(&mut builder));
325 builder.build(DeferFontRelativeCustomPropertyResolution::Yes)
330 };
331
332 cascade.apply_prioritary_properties(&mut context, &declarations, &mut shorthand_cache);
335
336 if let Some(deferred) = deferred_custom_properties {
338 CustomPropertiesBuilder::build_deferred(deferred, stylist, &mut context);
339 }
340
341 if let Some(visited_rules) = visited_rules {
342 cascade.compute_visited_style_if_needed(
343 &mut context,
344 element,
345 parent_style,
346 layout_parent_style,
347 visited_rules,
348 guards,
349 );
350 }
351
352 using_cached_reset_properties = cascade.try_to_use_cached_reset_properties(
353 &mut context.builder,
354 rule_cache,
355 guards,
356 );
357
358 if using_cached_reset_properties {
359 LonghandIdSet::late_group_only_inherited()
360 } else {
361 LonghandIdSet::late_group()
362 }
363 },
364 };
365
366 cascade.apply_non_prioritary_properties(
367 &mut context,
368 &declarations.longhand_declarations,
369 &mut shorthand_cache,
370 &properties_to_apply,
371 );
372
373 cascade.finished_applying_properties(&mut context.builder);
374
375 std::mem::drop(cascade);
376
377 context.builder.clear_modified_reset();
378
379 if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
380 StyleAdjuster::new(&mut context.builder)
381 .adjust(layout_parent_style.unwrap_or(inherited_style), element);
382 }
383
384 if context.builder.modified_reset() || using_cached_reset_properties {
385 context.rule_cache_conditions.borrow_mut().set_uncacheable();
391 }
392
393 context.builder.build()
394}
395
396type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
402
403fn tweak_when_ignoring_colors(
404 context: &computed::Context,
405 longhand_id: LonghandId,
406 origin: Origin,
407 declaration: &mut Cow<PropertyDeclaration>,
408 declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
409) {
410 use crate::values::computed::ToComputedValue;
411 use crate::values::specified::Color;
412
413 if !longhand_id.ignored_when_document_colors_disabled() {
414 return;
415 }
416
417 let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
418 if is_ua_or_user_rule {
419 return;
420 }
421
422 #[cfg(feature = "gecko")]
424 {
425 let forced = context
426 .builder
427 .get_inherited_text()
428 .clone_forced_color_adjust();
429 if forced == computed::ForcedColorAdjust::None {
430 return;
431 }
432 }
433
434 if context
438 .builder
439 .pseudo
440 .map_or(false, |p| p.is_color_swatch())
441 && longhand_id == LonghandId::BackgroundColor
442 {
443 return;
444 }
445
446 fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
447 color
449 .to_computed_value(context)
450 .resolve_to_absolute(&AbsoluteColor::BLACK)
451 .alpha
452 }
453
454 match **declaration {
456 PropertyDeclaration::CSSWideKeyword(..) => return,
458 PropertyDeclaration::BackgroundColor(ref color) => {
459 if color.honored_in_forced_colors_mode(true) {
469 return;
470 }
471 let alpha = alpha_channel(color, context);
475 if alpha == 0.0 {
476 return;
477 }
478 let mut color = context.builder.device.default_background_color();
479 color.alpha = alpha;
480 declarations_to_apply_unless_overridden
481 .push(PropertyDeclaration::BackgroundColor(color.into()))
482 },
483 PropertyDeclaration::Color(ref color) => {
484 if color
486 .0
487 .honored_in_forced_colors_mode(true)
488 {
489 return;
490 }
491 if context
495 .builder
496 .get_parent_inherited_text()
497 .clone_color()
498 .alpha
499 == 0.0
500 {
501 let color = context.builder.device.default_color();
502 declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
503 specified::ColorPropertyValue(color.into()),
504 ))
505 }
506 },
507 #[cfg(feature = "gecko")]
509 PropertyDeclaration::BackgroundImage(ref bkg) => {
510 use crate::values::generics::image::Image;
511 if static_prefs::pref!("browser.display.permit_backplate") {
512 if bkg
513 .0
514 .iter()
515 .all(|image| matches!(*image, Image::Url(..) | Image::None))
516 {
517 return;
518 }
519 }
520 },
521 _ => {
522 if let Some(color) = declaration.color_value() {
535 if color.honored_in_forced_colors_mode(false) {
536 return;
537 }
538 }
539 },
540 }
541
542 *declaration.to_mut() =
543 PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
544}
545
546type DeclarationIndex = u16;
548
549#[derive(Copy, Clone)]
554struct PrioritaryDeclarationPosition {
555 most_important: DeclarationIndex,
557 least_important: DeclarationIndex,
558}
559
560impl Default for PrioritaryDeclarationPosition {
561 fn default() -> Self {
562 Self {
563 most_important: DeclarationIndex::MAX,
564 least_important: DeclarationIndex::MAX,
565 }
566 }
567}
568
569#[derive(Copy, Clone)]
570struct Declaration<'a> {
571 decl: &'a PropertyDeclaration,
572 priority: CascadePriority,
573 next_index: DeclarationIndex,
574}
575
576#[derive(Default)]
578struct Declarations<'a> {
579 has_prioritary_properties: bool,
581 longhand_declarations: SmallVec<[Declaration<'a>; 64]>,
583 prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
585}
586
587impl<'a> Declarations<'a> {
588 fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
589 let new_index = self.longhand_declarations.len();
590 if new_index >= DeclarationIndex::MAX as usize {
591 return;
594 }
595
596 self.has_prioritary_properties = true;
597 let new_index = new_index as DeclarationIndex;
598 let position = &mut self.prioritary_positions[id as usize];
599 if position.most_important == DeclarationIndex::MAX {
600 position.most_important = new_index;
603 } else {
604 self.longhand_declarations[position.least_important as usize].next_index = new_index;
606 }
607 position.least_important = new_index;
608 }
609
610 fn note_declaration(
611 &mut self,
612 decl: &'a PropertyDeclaration,
613 priority: CascadePriority,
614 id: LonghandId,
615 ) {
616 if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
617 self.note_prioritary_property(id);
618 }
619 self.longhand_declarations.push(Declaration {
620 decl,
621 priority,
622 next_index: 0,
623 });
624 }
625}
626
627struct Cascade<'b> {
628 first_line_reparenting: FirstLineReparenting<'b>,
629 ignore_colors: bool,
630 seen: LonghandIdSet,
631 author_specified: LonghandIdSet,
632 reverted_set: LonghandIdSet,
633 reverted: FxHashMap<LonghandId, (CascadePriority, bool)>,
634 declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
635}
636
637impl<'b> Cascade<'b> {
638 fn new(first_line_reparenting: FirstLineReparenting<'b>, ignore_colors: bool) -> Self {
639 Self {
640 first_line_reparenting,
641 ignore_colors,
642 seen: LonghandIdSet::default(),
643 author_specified: LonghandIdSet::default(),
644 reverted_set: Default::default(),
645 reverted: Default::default(),
646 declarations_to_apply_unless_overridden: Default::default(),
647 }
648 }
649
650 fn substitute_variables_if_needed<'cache, 'decl>(
651 &self,
652 context: &mut computed::Context,
653 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
654 declaration: &'decl PropertyDeclaration,
655 ) -> Cow<'decl, PropertyDeclaration>
656 where
657 'cache: 'decl,
658 {
659 let declaration = match *declaration {
660 PropertyDeclaration::WithVariables(ref declaration) => declaration,
661 ref d => return Cow::Borrowed(d),
662 };
663
664 if !declaration.id.inherited() {
665 context.rule_cache_conditions.borrow_mut().set_uncacheable();
666
667 match declaration.id {
673 LonghandId::Display => {
674 context
675 .builder
676 .add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
677 },
678 LonghandId::Content => {
679 context
680 .builder
681 .add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
682 },
683 _ => {},
684 }
685 }
686
687 debug_assert!(
688 context.builder.stylist.is_some(),
689 "Need a Stylist to substitute variables!"
690 );
691 declaration.value.substitute_variables(
692 declaration.id,
693 context.builder.custom_properties(),
694 context.builder.stylist.unwrap(),
695 context,
696 shorthand_cache,
697 )
698 }
699
700 fn apply_one_prioritary_property(
701 &mut self,
702 context: &mut computed::Context,
703 decls: &Declarations,
704 cache: &mut ShorthandsWithPropertyReferencesCache,
705 id: PrioritaryPropertyId,
706 ) -> bool {
707 let mut index = decls.prioritary_positions[id as usize].most_important;
708 if index == DeclarationIndex::MAX {
709 return false;
710 }
711
712 let longhand_id = id.to_longhand();
713 debug_assert!(
714 !longhand_id.is_logical(),
715 "That could require more book-keeping"
716 );
717 loop {
718 let decl = decls.longhand_declarations[index as usize];
719 self.apply_one_longhand(context, longhand_id, decl.decl, decl.priority, cache);
720 if self.seen.contains(longhand_id) {
721 return true; }
723 debug_assert!(
724 self.reverted_set.contains(longhand_id),
725 "How else can we fail to apply a prioritary property?"
726 );
727 debug_assert!(
728 decl.next_index == 0 || decl.next_index > index,
729 "should make progress! {} -> {}",
730 index,
731 decl.next_index,
732 );
733 index = decl.next_index;
734 if index == 0 {
735 break;
736 }
737 }
738 false
739 }
740
741 fn apply_prioritary_properties(
742 &mut self,
743 context: &mut computed::Context,
744 decls: &Declarations,
745 cache: &mut ShorthandsWithPropertyReferencesCache,
746 ) {
747 macro_rules! apply {
750 ($prop:ident) => {
751 self.apply_one_prioritary_property(
752 context,
753 decls,
754 cache,
755 PrioritaryPropertyId::$prop,
756 )
757 };
758 }
759
760 if !decls.has_prioritary_properties {
761 return;
762 }
763
764 let has_writing_mode = apply!(WritingMode) | apply!(Direction);
765 #[cfg(feature = "gecko")]
766 let has_writing_mode = has_writing_mode | apply!(TextOrientation);
767
768 if has_writing_mode {
769 context.builder.writing_mode = WritingMode::new(context.builder.get_inherited_box())
770 }
771
772 if apply!(Zoom) {
773 context.builder.recompute_effective_zooms();
774 if !context.builder.effective_zoom_for_inheritance.is_one() {
775 self.recompute_font_size_for_zoom_change(&mut context.builder);
781 }
782 }
783
784 let has_font_family = apply!(FontFamily);
786 let has_lang = apply!(XLang);
787 #[cfg(feature = "gecko")]
788 {
789 if has_lang {
790 self.recompute_initial_font_family_if_needed(&mut context.builder);
791 }
792 if has_font_family {
793 self.prioritize_user_fonts_if_needed(&mut context.builder);
794 }
795
796 if apply!(XTextScale) {
798 self.unzoom_fonts_if_needed(&mut context.builder);
799 }
800 let has_font_size = apply!(FontSize);
801 let has_math_depth = apply!(MathDepth);
802 let has_min_font_size_ratio = apply!(MozMinFontSizeRatio);
803
804 if has_math_depth && has_font_size {
805 self.recompute_math_font_size_if_needed(context);
806 }
807 if has_lang || has_font_family {
808 self.recompute_keyword_font_size_if_needed(context);
809 }
810 if has_font_size || has_min_font_size_ratio || has_lang || has_font_family {
811 self.constrain_font_size_if_needed(&mut context.builder);
812 }
813 }
814
815 #[cfg(feature = "servo")]
816 {
817 apply!(FontSize);
818 if has_lang || has_font_family {
819 self.recompute_keyword_font_size_if_needed(context);
820 }
821 }
822
823 apply!(FontWeight);
825 apply!(FontStretch);
826 apply!(FontStyle);
827 #[cfg(feature = "gecko")]
828 apply!(FontSizeAdjust);
829
830 #[cfg(feature = "gecko")]
831 apply!(ForcedColorAdjust);
832 if apply!(ColorScheme) {
835 context.builder.color_scheme = context.builder.get_inherited_ui().color_scheme_bits();
836 }
837 apply!(LineHeight);
838 }
839
840 fn apply_non_prioritary_properties(
841 &mut self,
842 context: &mut computed::Context,
843 longhand_declarations: &[Declaration],
844 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
845 properties_to_apply: &LonghandIdSet,
846 ) {
847 debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
848 debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
849 for declaration in &*longhand_declarations {
850 let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
851 if !properties_to_apply.contains(longhand_id) {
852 continue;
853 }
854 debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
855 let is_logical = longhand_id.is_logical();
856 if is_logical {
857 let wm = context.builder.writing_mode;
858 context
859 .rule_cache_conditions
860 .borrow_mut()
861 .set_writing_mode_dependency(wm);
862 longhand_id = longhand_id.to_physical(wm);
863 }
864 self.apply_one_longhand(
865 context,
866 longhand_id,
867 declaration.decl,
868 declaration.priority,
869 shorthand_cache,
870 );
871 }
872 if !self.declarations_to_apply_unless_overridden.is_empty() {
873 debug_assert!(self.ignore_colors);
874 for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
875 let longhand_id = declaration.id().as_longhand().unwrap();
876 debug_assert!(!longhand_id.is_logical());
877 if !self.seen.contains(longhand_id) {
878 unsafe {
879 self.do_apply_declaration(context, longhand_id, &declaration);
880 }
881 }
882 }
883 }
884
885 if !context.builder.effective_zoom_for_inheritance.is_one() {
886 self.recompute_zoom_dependent_inherited_lengths(context);
887 }
888 }
889
890 #[cold]
891 fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {
892 debug_assert!(self.seen.contains(LonghandId::Zoom));
893 for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {
894 if self.seen.contains(prop) {
895 continue;
896 }
897 let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);
898 unsafe {
899 self.do_apply_declaration(context, prop, &declaration);
900 }
901 }
902 }
903
904 fn apply_one_longhand(
905 &mut self,
906 context: &mut computed::Context,
907 longhand_id: LonghandId,
908 declaration: &PropertyDeclaration,
909 priority: CascadePriority,
910 cache: &mut ShorthandsWithPropertyReferencesCache,
911 ) {
912 debug_assert!(!longhand_id.is_logical());
913 let origin = priority.cascade_level().origin();
914 if self.seen.contains(longhand_id) {
915 return;
916 }
917
918 if self.reverted_set.contains(longhand_id) {
919 if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&longhand_id) {
920 if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
921 return;
922 }
923 }
924 }
925
926 let mut declaration = self.substitute_variables_if_needed(context, cache, declaration);
927
928 if self.ignore_colors {
931 tweak_when_ignoring_colors(
932 context,
933 longhand_id,
934 origin,
935 &mut declaration,
936 &mut self.declarations_to_apply_unless_overridden,
937 );
938 }
939 let can_skip_apply = match declaration.get_css_wide_keyword() {
940 Some(keyword) => {
941 if matches!(
942 keyword,
943 CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert
944 ) {
945 let origin_revert = keyword == CSSWideKeyword::Revert;
946 self.reverted_set.insert(longhand_id);
949 self.reverted.insert(longhand_id, (priority, origin_revert));
950 return;
951 }
952
953 let inherited = longhand_id.inherited();
954 let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()
955 && longhand_id.zoom_dependent();
956 match keyword {
957 CSSWideKeyword::Revert | CSSWideKeyword::RevertLayer => unreachable!(),
958 CSSWideKeyword::Unset => !zoomed || !inherited,
959 CSSWideKeyword::Inherit => inherited && !zoomed,
960 CSSWideKeyword::Initial => !inherited,
961 }
962 },
963 None => false,
964 };
965
966 self.seen.insert(longhand_id);
967 if origin == Origin::Author {
968 self.author_specified.insert(longhand_id);
969 }
970
971 if !can_skip_apply {
972 unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
973 }
974 }
975
976 #[inline]
977 unsafe fn do_apply_declaration(
978 &self,
979 context: &mut computed::Context,
980 longhand_id: LonghandId,
981 declaration: &PropertyDeclaration,
982 ) {
983 debug_assert!(!longhand_id.is_logical());
984 (CASCADE_PROPERTY[longhand_id as usize])(&declaration, context);
990 }
991
992 fn compute_visited_style_if_needed<E>(
993 &self,
994 context: &mut computed::Context,
995 element: Option<E>,
996 parent_style: Option<&ComputedValues>,
997 layout_parent_style: Option<&ComputedValues>,
998 visited_rules: &StrongRuleNode,
999 guards: &StylesheetGuards,
1000 ) where
1001 E: TElement,
1002 {
1003 let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
1004
1005 macro_rules! visited_parent {
1006 ($parent:expr) => {
1007 if is_link {
1008 $parent
1009 } else {
1010 $parent.map(|p| p.visited_style().unwrap_or(p))
1011 }
1012 };
1013 }
1014
1015 let style = cascade_rules(
1018 context.builder.stylist.unwrap(),
1019 context.builder.pseudo,
1020 visited_rules,
1021 guards,
1022 visited_parent!(parent_style),
1023 visited_parent!(layout_parent_style),
1024 self.first_line_reparenting,
1025 CascadeMode::Visited {
1026 unvisited_context: &*context,
1027 },
1028 Default::default(),
1031 None, &mut *context.rule_cache_conditions.borrow_mut(),
1037 element,
1038 );
1039 context.builder.visited_style = Some(style);
1040 }
1041
1042 fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
1043 #[cfg(feature = "gecko")]
1044 {
1045 if let Some(bg) = builder.get_background_if_mutated() {
1046 bg.fill_arrays();
1047 }
1048
1049 if let Some(svg) = builder.get_svg_if_mutated() {
1050 svg.fill_arrays();
1051 }
1052 }
1053
1054 if self
1055 .author_specified
1056 .contains_any(LonghandIdSet::border_background_properties())
1057 {
1058 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
1059 }
1060
1061 if self.author_specified.contains(LonghandId::FontFamily) {
1062 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY);
1063 }
1064
1065 if self.author_specified.contains(LonghandId::Color) {
1066 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);
1067 }
1068
1069 if self.author_specified.contains(LonghandId::LetterSpacing) {
1070 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING);
1071 }
1072
1073 if self.author_specified.contains(LonghandId::WordSpacing) {
1074 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING);
1075 }
1076
1077 #[cfg(feature = "gecko")]
1078 if self
1079 .author_specified
1080 .contains(LonghandId::FontSynthesisWeight)
1081 {
1082 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT);
1083 }
1084
1085 #[cfg(feature = "gecko")]
1086 if self
1087 .author_specified
1088 .contains(LonghandId::FontSynthesisStyle)
1089 {
1090 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE);
1091 }
1092
1093 #[cfg(feature = "servo")]
1094 {
1095 if let Some(font) = builder.get_font_if_mutated() {
1096 font.compute_font_hash();
1097 }
1098 }
1099 }
1100
1101 fn try_to_use_cached_reset_properties(
1102 &self,
1103 builder: &mut StyleBuilder<'b>,
1104 cache: Option<&'b RuleCache>,
1105 guards: &StylesheetGuards,
1106 ) -> bool {
1107 let style = match self.first_line_reparenting {
1108 FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
1109 FirstLineReparenting::No => {
1110 let Some(cache) = cache else { return false };
1111 let Some(style) = cache.find(guards, builder) else {
1112 return false;
1113 };
1114 style
1115 },
1116 };
1117
1118 builder.copy_reset_from(style);
1119
1120 let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND
1132 | ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS
1133 | ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS
1134 | ComputedValueFlags::USES_CONTAINER_UNITS
1135 | ComputedValueFlags::USES_VIEWPORT_UNITS;
1136 builder.add_flags(style.flags & bits_to_copy);
1137
1138 true
1139 }
1140
1141 #[inline]
1144 #[cfg(feature = "gecko")]
1145 fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
1146 use crate::gecko_bindings::bindings;
1147 use crate::values::computed::font::FontFamily;
1148
1149 let default_font_type = {
1150 let font = builder.get_font();
1151
1152 if !font.mFont.family.is_initial {
1153 return;
1154 }
1155
1156 let default_font_type = unsafe {
1157 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1158 builder.device.document(),
1159 font.mLanguage.mRawPtr,
1160 )
1161 };
1162
1163 let initial_generic = font.mFont.family.families.single_generic();
1164 debug_assert!(
1165 initial_generic.is_some(),
1166 "Initial font should be just one generic font"
1167 );
1168 if initial_generic == Some(default_font_type) {
1169 return;
1170 }
1171
1172 default_font_type
1173 };
1174
1175 builder.mutate_font().mFont.family.families =
1177 FontFamily::generic(default_font_type).families.clone();
1178 }
1179
1180 #[inline]
1182 #[cfg(feature = "gecko")]
1183 fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1184 use crate::gecko_bindings::bindings;
1185
1186 if static_prefs::pref!("browser.display.use_document_fonts") != 0
1189 || builder.device.chrome_rules_enabled_for_document()
1190 {
1191 return;
1192 }
1193
1194 let default_font_type = {
1195 let font = builder.get_font();
1196
1197 if font.mFont.family.is_system_font {
1198 return;
1199 }
1200
1201 if !font.mFont.family.families.needs_user_font_prioritization() {
1202 return;
1203 }
1204
1205 unsafe {
1206 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1207 builder.device.document(),
1208 font.mLanguage.mRawPtr,
1209 )
1210 }
1211 };
1212
1213 let font = builder.mutate_font();
1214 font.mFont
1215 .family
1216 .families
1217 .prioritize_first_generic_or_prepend(default_font_type);
1218 }
1219
1220 fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
1222 use crate::values::computed::ToComputedValue;
1223
1224 if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
1225 return;
1226 }
1227
1228 let new_size = {
1229 let font = context.builder.get_font();
1230 let info = font.clone_font_size().keyword_info;
1231 let new_size = match info.kw {
1232 specified::FontSizeKeyword::None => return,
1233 _ => {
1234 context.for_non_inherited_property = false;
1235 specified::FontSize::Keyword(info).to_computed_value(context)
1236 },
1237 };
1238
1239 #[cfg(feature = "gecko")]
1240 if font.mScriptUnconstrainedSize == new_size.computed_size {
1241 return;
1242 }
1243
1244 new_size
1245 };
1246
1247 context.builder.mutate_font().set_font_size(new_size);
1248 }
1249
1250 #[cfg(feature = "gecko")]
1253 fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
1254 use crate::gecko_bindings::bindings;
1255 use crate::values::generics::NonNegative;
1256
1257 let min_font_size = {
1258 let font = builder.get_font();
1259 let min_font_size = unsafe {
1260 bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
1261 };
1262
1263 if font.mFont.size.0 >= min_font_size {
1264 return;
1265 }
1266
1267 NonNegative(min_font_size)
1268 };
1269
1270 builder.mutate_font().mFont.size = min_font_size;
1271 }
1272
1273 #[cfg(feature = "gecko")]
1277 fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1278 debug_assert!(self.seen.contains(LonghandId::XTextScale));
1279
1280 let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
1281 let text_scale = builder.get_font().clone__x_text_scale();
1282 if parent_text_scale == text_scale {
1283 return;
1284 }
1285 debug_assert_ne!(
1286 parent_text_scale.text_zoom_enabled(),
1287 text_scale.text_zoom_enabled(),
1288 "There's only one value that disables it"
1289 );
1290 debug_assert!(
1291 !text_scale.text_zoom_enabled(),
1292 "We only ever disable text zoom never enable it"
1293 );
1294 let device = builder.device;
1295 builder.mutate_font().unzoom_fonts(device);
1296 }
1297
1298 fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
1299 debug_assert!(self.seen.contains(LonghandId::Zoom));
1300 let old_size = builder.get_font().clone_font_size();
1303 let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);
1304 if old_size == new_size {
1305 return;
1306 }
1307 builder.mutate_font().set_font_size(new_size);
1308 }
1309
1310 #[cfg(feature = "gecko")]
1315 fn recompute_math_font_size_if_needed(&self, context: &mut computed::Context) {
1316 use crate::values::generics::NonNegative;
1317
1318 if context.builder.get_font().clone_font_size().keyword_info.kw
1320 != specified::FontSizeKeyword::Math
1321 {
1322 return;
1323 }
1324
1325 const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
1326
1327 fn scale_factor_for_math_depth_change(
1337 parent_math_depth: i32,
1338 computed_math_depth: i32,
1339 parent_script_percent_scale_down: Option<f32>,
1340 parent_script_script_percent_scale_down: Option<f32>,
1341 ) -> f32 {
1342 let mut a = parent_math_depth;
1343 let mut b = computed_math_depth;
1344 let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
1345 let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c);
1346 let scale_between_0_and_2 =
1347 parent_script_script_percent_scale_down.unwrap_or_else(|| c * c);
1348 let mut s = 1.0;
1349 let mut invert_scale_factor = false;
1350 if a == b {
1351 return s;
1352 }
1353 if b < a {
1354 std::mem::swap(&mut a, &mut b);
1355 invert_scale_factor = true;
1356 }
1357 let mut e = b - a;
1358 if a <= 0 && b >= 2 {
1359 s *= scale_between_0_and_2;
1360 e -= 2;
1361 } else if a == 1 {
1362 s *= scale_between_0_and_2 / scale_between_0_and_1;
1363 e -= 1;
1364 } else if b == 1 {
1365 s *= scale_between_0_and_1;
1366 e -= 1;
1367 }
1368 s *= (c as f32).powi(e);
1369 if invert_scale_factor {
1370 1.0 / s.max(f32::MIN_POSITIVE)
1371 } else {
1372 s
1373 }
1374 }
1375
1376 let (new_size, new_unconstrained_size) = {
1377 use crate::values::specified::font::QueryFontMetricsFlags;
1378
1379 let builder = &context.builder;
1380 let font = builder.get_font();
1381 let parent_font = builder.get_parent_font();
1382
1383 let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1384
1385 if delta == 0 {
1386 return;
1387 }
1388
1389 let mut min = parent_font.mScriptMinSize;
1390 if font.mXTextScale.text_zoom_enabled() {
1391 min = builder.device.zoom_text(min);
1392 }
1393
1394 let scale = {
1396 let font_metrics = context.query_font_metrics(
1398 FontBaseSize::InheritedStyle,
1399 FontMetricsOrientation::Horizontal,
1400 QueryFontMetricsFlags::NEEDS_MATH_SCALES,
1401 );
1402 scale_factor_for_math_depth_change(
1403 parent_font.mMathDepth as i32,
1404 font.mMathDepth as i32,
1405 font_metrics.script_percent_scale_down,
1406 font_metrics.script_script_percent_scale_down,
1407 )
1408 };
1409
1410 let parent_size = parent_font.mSize.0;
1411 let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1412 let new_size = parent_size.scale_by(scale);
1413 let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1414
1415 if scale <= 1. {
1416 if parent_size <= min {
1421 (parent_size, new_unconstrained_size)
1422 } else {
1423 (min.max(new_size), new_unconstrained_size)
1424 }
1425 } else {
1426 (
1432 new_size.min(new_unconstrained_size.max(min)),
1433 new_unconstrained_size,
1434 )
1435 }
1436 };
1437 let font = context.builder.mutate_font();
1438 font.mFont.size = NonNegative(new_size);
1439 font.mSize = NonNegative(new_size);
1440 font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1441 }
1442}