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::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 rule_cache: Option<&RuleCache>,
78 rule_cache_conditions: &mut RuleCacheConditions,
79 element: Option<E>,
80) -> Arc<ComputedValues>
81where
82 E: TElement,
83{
84 cascade_rules(
85 stylist,
86 pseudo,
87 rule_node,
88 guards,
89 parent_style,
90 layout_parent_style,
91 first_line_reparenting,
92 try_tactic,
93 CascadeMode::Unvisited { visited_rules },
94 cascade_input_flags,
95 rule_cache,
96 rule_cache_conditions,
97 element,
98 )
99}
100
101struct DeclarationIterator<'a> {
102 guards: &'a StylesheetGuards<'a>,
104 restriction: Option<PropertyFlags>,
105 current_rule_node: Option<&'a StrongRuleNode>,
107 declarations: DeclarationImportanceIterator<'a>,
109 origin: Origin,
110 importance: Importance,
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 origin: Origin::UserAgent,
126 importance: Importance::Normal,
127 priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()),
128 declarations: DeclarationImportanceIterator::default(),
129 restriction,
130 };
131 iter.update_for_node(rule_node);
132 iter
133 }
134
135 fn update_for_node(&mut self, node: &'a StrongRuleNode) {
136 self.priority = node.cascade_priority();
137 let level = self.priority.cascade_level();
138 self.origin = level.origin();
139 self.importance = level.importance();
140 let guard = match self.origin {
141 Origin::Author => self.guards.author,
142 Origin::User | Origin::UserAgent => self.guards.ua_or_user,
143 };
144 self.declarations = match node.style_source() {
145 Some(source) => source.read(guard).declaration_importance_iter(),
146 None => DeclarationImportanceIterator::default(),
147 };
148 }
149}
150
151impl<'a> Iterator for DeclarationIterator<'a> {
152 type Item = (&'a PropertyDeclaration, CascadePriority);
153
154 #[inline]
155 fn next(&mut self) -> Option<Self::Item> {
156 loop {
157 if let Some((decl, importance)) = self.declarations.next_back() {
158 if self.importance != importance {
159 continue;
160 }
161
162 if let Some(restriction) = self.restriction {
163 if let PropertyDeclarationId::Longhand(id) = decl.id() {
168 if !id.flags().contains(restriction) && self.origin != Origin::UserAgent {
169 continue;
170 }
171 }
172 }
173
174 return Some((decl, self.priority));
175 }
176
177 let next_node = self.current_rule_node.take()?.parent()?;
178 self.current_rule_node = Some(next_node);
179 self.update_for_node(next_node);
180 }
181 }
182}
183
184fn cascade_rules<E>(
185 stylist: &Stylist,
186 pseudo: Option<&PseudoElement>,
187 rule_node: &StrongRuleNode,
188 guards: &StylesheetGuards,
189 parent_style: Option<&ComputedValues>,
190 layout_parent_style: Option<&ComputedValues>,
191 first_line_reparenting: FirstLineReparenting,
192 try_tactic: &PositionTryFallbacksTryTactic,
193 cascade_mode: CascadeMode,
194 cascade_input_flags: ComputedValueFlags,
195 rule_cache: Option<&RuleCache>,
196 rule_cache_conditions: &mut RuleCacheConditions,
197 element: Option<E>,
198) -> Arc<ComputedValues>
199where
200 E: TElement,
201{
202 apply_declarations(
203 stylist,
204 pseudo,
205 rule_node,
206 guards,
207 DeclarationIterator::new(rule_node, guards, pseudo),
208 parent_style,
209 layout_parent_style,
210 first_line_reparenting,
211 try_tactic,
212 cascade_mode,
213 cascade_input_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) {
240 for (declaration, priority) in iter {
241 if let PropertyDeclaration::Custom(ref declaration) = *declaration {
242 if let Some(ref mut builder) = custom_builder {
243 builder.cascade(declaration, priority);
244 }
245 } else {
246 let id = declaration.id().as_longhand().unwrap();
247 declarations.note_declaration(declaration, priority, id);
248 if CustomPropertiesBuilder::might_have_non_custom_dependency(id, declaration) {
249 if let Some(ref mut builder) = custom_builder {
250 builder.maybe_note_non_custom_dependency(id, declaration);
251 }
252 }
253 }
254 }
255}
256
257pub fn apply_declarations<'a, E, I>(
260 stylist: &'a Stylist,
261 pseudo: Option<&'a PseudoElement>,
262 rules: &StrongRuleNode,
263 guards: &StylesheetGuards,
264 iter: I,
265 parent_style: Option<&'a ComputedValues>,
266 layout_parent_style: Option<&ComputedValues>,
267 first_line_reparenting: FirstLineReparenting<'a>,
268 try_tactic: &'a PositionTryFallbacksTryTactic,
269 cascade_mode: CascadeMode,
270 cascade_input_flags: ComputedValueFlags,
271 rule_cache: Option<&'a RuleCache>,
272 rule_cache_conditions: &'a mut RuleCacheConditions,
273 element: Option<E>,
274) -> Arc<ComputedValues>
275where
276 E: TElement + 'a,
277 I: Iterator<Item = (&'a PropertyDeclaration, CascadePriority)>,
278{
279 debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
280 let device = stylist.device();
281 let inherited_style = parent_style.unwrap_or(device.default_computed_values());
282 let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
283
284 let container_size_query =
285 ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());
286
287 let mut context = computed::Context::new(
288 StyleBuilder::new(
292 device,
293 Some(stylist),
294 parent_style,
295 pseudo,
296 Some(rules.clone()),
297 is_root_element,
298 ),
299 stylist.quirks_mode(),
300 rule_cache_conditions,
301 container_size_query,
302 );
303
304 context.style().add_flags(cascade_input_flags);
305
306 let using_cached_reset_properties;
307 let ignore_colors = context.builder.device.forced_colors().is_active();
308 let mut cascade = Cascade::new(first_line_reparenting, try_tactic, ignore_colors);
309 let mut declarations = Default::default();
310 let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
311 let properties_to_apply = match cascade_mode {
312 CascadeMode::Visited { unvisited_context } => {
313 context.builder.custom_properties = unvisited_context.builder.custom_properties.clone();
314 context.builder.writing_mode = unvisited_context.builder.writing_mode;
315 context.builder.color_scheme = unvisited_context.builder.color_scheme;
316 using_cached_reset_properties = false;
320 iter_declarations(iter, &mut declarations, None);
324
325 LonghandIdSet::visited_dependent()
326 },
327 CascadeMode::Unvisited { visited_rules } => {
328 let deferred_custom_properties = {
329 let mut builder = CustomPropertiesBuilder::new(stylist, &mut context);
330 iter_declarations(iter, &mut declarations, Some(&mut builder));
331 builder.build(DeferFontRelativeCustomPropertyResolution::Yes)
336 };
337
338 cascade.apply_prioritary_properties(&mut context, &declarations, &mut shorthand_cache);
341
342 if let Some(deferred) = deferred_custom_properties {
344 CustomPropertiesBuilder::build_deferred(deferred, stylist, &mut context);
345 }
346
347 if let Some(visited_rules) = visited_rules {
348 cascade.compute_visited_style_if_needed(
349 &mut context,
350 element,
351 parent_style,
352 layout_parent_style,
353 visited_rules,
354 guards,
355 );
356 }
357
358 using_cached_reset_properties = cascade.try_to_use_cached_reset_properties(
359 &mut context.builder,
360 rule_cache,
361 guards,
362 );
363
364 if using_cached_reset_properties {
365 LonghandIdSet::late_group_only_inherited()
366 } else {
367 LonghandIdSet::late_group()
368 }
369 },
370 };
371
372 cascade.apply_non_prioritary_properties(
373 &mut context,
374 &declarations.longhand_declarations,
375 &mut shorthand_cache,
376 &properties_to_apply,
377 );
378
379 cascade.finished_applying_properties(&mut context.builder);
380
381 std::mem::drop(cascade);
382
383 context.builder.clear_modified_reset();
384
385 if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
386 StyleAdjuster::new(&mut context.builder).adjust(
387 layout_parent_style.unwrap_or(inherited_style),
388 element,
389 try_tactic,
390 );
391 }
392
393 if context.builder.modified_reset() || using_cached_reset_properties {
394 context.rule_cache_conditions.borrow_mut().set_uncacheable();
400 }
401
402 context.builder.build()
403}
404
405type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
411
412fn tweak_when_ignoring_colors(
413 context: &computed::Context,
414 longhand_id: LonghandId,
415 origin: Origin,
416 declaration: &mut Cow<PropertyDeclaration>,
417 declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
418) {
419 use crate::values::computed::ToComputedValue;
420 use crate::values::specified::Color;
421
422 if !longhand_id.ignored_when_document_colors_disabled() {
423 return;
424 }
425
426 let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
427 if is_ua_or_user_rule {
428 return;
429 }
430
431 #[cfg(feature = "gecko")]
433 {
434 let forced = context
435 .builder
436 .get_inherited_text()
437 .clone_forced_color_adjust();
438 if forced == computed::ForcedColorAdjust::None {
439 return;
440 }
441 }
442
443 if context
447 .builder
448 .pseudo
449 .map_or(false, |p| p.is_color_swatch())
450 && longhand_id == LonghandId::BackgroundColor
451 {
452 return;
453 }
454
455 fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
456 color
458 .to_computed_value(context)
459 .resolve_to_absolute(&AbsoluteColor::BLACK)
460 .alpha
461 }
462
463 match **declaration {
465 PropertyDeclaration::CSSWideKeyword(..) => return,
467 PropertyDeclaration::BackgroundColor(ref color) => {
468 if color.honored_in_forced_colors_mode(true) {
478 return;
479 }
480 let alpha = alpha_channel(color, context);
484 if alpha == 0.0 {
485 return;
486 }
487 let mut color = context.builder.device.default_background_color();
488 color.alpha = alpha;
489 declarations_to_apply_unless_overridden
490 .push(PropertyDeclaration::BackgroundColor(color.into()))
491 },
492 PropertyDeclaration::Color(ref color) => {
493 if color
495 .0
496 .honored_in_forced_colors_mode(true)
497 {
498 return;
499 }
500 if context
504 .builder
505 .get_parent_inherited_text()
506 .clone_color()
507 .alpha
508 == 0.0
509 {
510 let color = context.builder.device.default_color();
511 declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
512 specified::ColorPropertyValue(color.into()),
513 ))
514 }
515 },
516 #[cfg(feature = "gecko")]
518 PropertyDeclaration::BackgroundImage(ref bkg) => {
519 use crate::values::generics::image::Image;
520 if static_prefs::pref!("browser.display.permit_backplate") {
521 if bkg
522 .0
523 .iter()
524 .all(|image| matches!(*image, Image::Url(..) | Image::None))
525 {
526 return;
527 }
528 }
529 },
530 _ => {
531 if let Some(color) = declaration.color_value() {
544 if color.honored_in_forced_colors_mode(false) {
545 return;
546 }
547 }
548 },
549 }
550
551 *declaration.to_mut() =
552 PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
553}
554
555type DeclarationIndex = u16;
557
558#[derive(Copy, Clone)]
563struct PrioritaryDeclarationPosition {
564 most_important: DeclarationIndex,
566 least_important: DeclarationIndex,
567}
568
569impl Default for PrioritaryDeclarationPosition {
570 fn default() -> Self {
571 Self {
572 most_important: DeclarationIndex::MAX,
573 least_important: DeclarationIndex::MAX,
574 }
575 }
576}
577
578#[derive(Copy, Clone)]
579struct Declaration<'a> {
580 decl: &'a PropertyDeclaration,
581 priority: CascadePriority,
582 next_index: DeclarationIndex,
583}
584
585#[derive(Default)]
587struct Declarations<'a> {
588 has_prioritary_properties: bool,
590 longhand_declarations: SmallVec<[Declaration<'a>; 64]>,
592 prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
594}
595
596impl<'a> Declarations<'a> {
597 fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
598 let new_index = self.longhand_declarations.len();
599 if new_index >= DeclarationIndex::MAX as usize {
600 return;
603 }
604
605 self.has_prioritary_properties = true;
606 let new_index = new_index as DeclarationIndex;
607 let position = &mut self.prioritary_positions[id as usize];
608 if position.most_important == DeclarationIndex::MAX {
609 position.most_important = new_index;
612 } else {
613 self.longhand_declarations[position.least_important as usize].next_index = new_index;
615 }
616 position.least_important = new_index;
617 }
618
619 fn note_declaration(
620 &mut self,
621 decl: &'a PropertyDeclaration,
622 priority: CascadePriority,
623 id: LonghandId,
624 ) {
625 if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
626 self.note_prioritary_property(id);
627 }
628 self.longhand_declarations.push(Declaration {
629 decl,
630 priority,
631 next_index: 0,
632 });
633 }
634}
635
636struct Cascade<'b> {
637 first_line_reparenting: FirstLineReparenting<'b>,
638 try_tactic: &'b PositionTryFallbacksTryTactic,
639 ignore_colors: bool,
640 seen: LonghandIdSet,
641 author_specified: LonghandIdSet,
642 reverted_set: LonghandIdSet,
643 reverted: FxHashMap<LonghandId, (CascadePriority, bool)>,
644 declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
645}
646
647impl<'b> Cascade<'b> {
648 fn new(
649 first_line_reparenting: FirstLineReparenting<'b>,
650 try_tactic: &'b PositionTryFallbacksTryTactic,
651 ignore_colors: bool,
652 ) -> Self {
653 Self {
654 first_line_reparenting,
655 try_tactic,
656 ignore_colors,
657 seen: LonghandIdSet::default(),
658 author_specified: LonghandIdSet::default(),
659 reverted_set: Default::default(),
660 reverted: Default::default(),
661 declarations_to_apply_unless_overridden: Default::default(),
662 }
663 }
664
665 fn substitute_variables_if_needed<'cache, 'decl>(
666 &self,
667 context: &mut computed::Context,
668 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
669 declaration: &'decl PropertyDeclaration,
670 ) -> Cow<'decl, PropertyDeclaration>
671 where
672 'cache: 'decl,
673 {
674 let declaration = match *declaration {
675 PropertyDeclaration::WithVariables(ref declaration) => declaration,
676 ref d => return Cow::Borrowed(d),
677 };
678
679 if !declaration.id.inherited() {
680 context.rule_cache_conditions.borrow_mut().set_uncacheable();
681
682 match declaration.id {
688 LonghandId::Display => {
689 context
690 .builder
691 .add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
692 },
693 LonghandId::Content => {
694 context
695 .builder
696 .add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
697 },
698 _ => {},
699 }
700 }
701
702 debug_assert!(
703 context.builder.stylist.is_some(),
704 "Need a Stylist to substitute variables!"
705 );
706 declaration.value.substitute_variables(
707 declaration.id,
708 context.builder.custom_properties(),
709 context.builder.stylist.unwrap(),
710 context,
711 shorthand_cache,
712 )
713 }
714
715 fn apply_one_prioritary_property(
716 &mut self,
717 context: &mut computed::Context,
718 decls: &Declarations,
719 cache: &mut ShorthandsWithPropertyReferencesCache,
720 id: PrioritaryPropertyId,
721 ) -> bool {
722 let mut index = decls.prioritary_positions[id as usize].most_important;
723 if index == DeclarationIndex::MAX {
724 return false;
725 }
726
727 let longhand_id = id.to_longhand();
728 debug_assert!(
729 !longhand_id.is_logical(),
730 "That could require more book-keeping"
731 );
732 loop {
733 let decl = decls.longhand_declarations[index as usize];
734 self.apply_one_longhand(context, longhand_id, decl.decl, decl.priority, cache);
735 if self.seen.contains(longhand_id) {
736 return true; }
738 debug_assert!(
739 self.reverted_set.contains(longhand_id),
740 "How else can we fail to apply a prioritary property?"
741 );
742 debug_assert!(
743 decl.next_index == 0 || decl.next_index > index,
744 "should make progress! {} -> {}",
745 index,
746 decl.next_index,
747 );
748 index = decl.next_index;
749 if index == 0 {
750 break;
751 }
752 }
753 false
754 }
755
756 fn apply_prioritary_properties(
757 &mut self,
758 context: &mut computed::Context,
759 decls: &Declarations,
760 cache: &mut ShorthandsWithPropertyReferencesCache,
761 ) {
762 macro_rules! apply {
765 ($prop:ident) => {
766 self.apply_one_prioritary_property(
767 context,
768 decls,
769 cache,
770 PrioritaryPropertyId::$prop,
771 )
772 };
773 }
774
775 if !decls.has_prioritary_properties {
776 return;
777 }
778
779 let has_writing_mode = apply!(WritingMode) | apply!(Direction);
780 #[cfg(feature = "gecko")]
781 let has_writing_mode = has_writing_mode | apply!(TextOrientation);
782
783 if has_writing_mode {
784 context.builder.writing_mode = WritingMode::new(context.builder.get_inherited_box())
785 }
786
787 if apply!(Zoom) {
788 context.builder.recompute_effective_zooms();
789 if !context.builder.effective_zoom_for_inheritance.is_one() {
790 self.recompute_font_size_for_zoom_change(&mut context.builder);
796 }
797 }
798
799 let has_font_family = apply!(FontFamily);
801 let has_lang = apply!(XLang);
802 #[cfg(feature = "gecko")]
803 {
804 if has_lang {
805 self.recompute_initial_font_family_if_needed(&mut context.builder);
806 }
807 if has_font_family {
808 self.prioritize_user_fonts_if_needed(&mut context.builder);
809 }
810
811 if apply!(XTextScale) {
813 self.unzoom_fonts_if_needed(&mut context.builder);
814 }
815 let has_font_size = apply!(FontSize);
816 let has_math_depth = apply!(MathDepth);
817 let has_min_font_size_ratio = apply!(MozMinFontSizeRatio);
818
819 if has_math_depth && has_font_size {
820 self.recompute_math_font_size_if_needed(context);
821 }
822 if has_lang || has_font_family {
823 self.recompute_keyword_font_size_if_needed(context);
824 }
825 if has_font_size || has_min_font_size_ratio || has_lang || has_font_family {
826 self.constrain_font_size_if_needed(&mut context.builder);
827 }
828 }
829
830 #[cfg(feature = "servo")]
831 {
832 apply!(FontSize);
833 if has_lang || has_font_family {
834 self.recompute_keyword_font_size_if_needed(context);
835 }
836 }
837
838 apply!(FontWeight);
840 apply!(FontStretch);
841 apply!(FontStyle);
842 #[cfg(feature = "gecko")]
843 apply!(FontSizeAdjust);
844
845 #[cfg(feature = "gecko")]
846 apply!(ForcedColorAdjust);
847 if apply!(ColorScheme) {
850 context.builder.color_scheme = context.builder.get_inherited_ui().color_scheme_bits();
851 }
852 apply!(LineHeight);
853 }
854
855 fn apply_non_prioritary_properties(
856 &mut self,
857 context: &mut computed::Context,
858 longhand_declarations: &[Declaration],
859 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
860 properties_to_apply: &LonghandIdSet,
861 ) {
862 debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
863 debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
864 for declaration in &*longhand_declarations {
865 let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
866 if !properties_to_apply.contains(longhand_id) {
867 continue;
868 }
869 debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
870 let is_logical = longhand_id.is_logical();
871 if is_logical {
872 let wm = context.builder.writing_mode;
873 context
874 .rule_cache_conditions
875 .borrow_mut()
876 .set_writing_mode_dependency(wm);
877 longhand_id = longhand_id.to_physical(wm);
878 }
879 self.apply_one_longhand(
880 context,
881 longhand_id,
882 declaration.decl,
883 declaration.priority,
884 shorthand_cache,
885 );
886 }
887 if !self.declarations_to_apply_unless_overridden.is_empty() {
888 debug_assert!(self.ignore_colors);
889 for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
890 let longhand_id = declaration.id().as_longhand().unwrap();
891 debug_assert!(!longhand_id.is_logical());
892 if !self.seen.contains(longhand_id) {
893 unsafe {
894 self.do_apply_declaration(context, longhand_id, &declaration);
895 }
896 }
897 }
898 }
899
900 if !context.builder.effective_zoom_for_inheritance.is_one() {
901 self.recompute_zoom_dependent_inherited_lengths(context);
902 }
903 }
904
905 #[cold]
906 fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {
907 debug_assert!(self.seen.contains(LonghandId::Zoom));
908 for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {
909 if self.seen.contains(prop) {
910 continue;
911 }
912 let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);
913 unsafe {
914 self.do_apply_declaration(context, prop, &declaration);
915 }
916 }
917 }
918
919 fn apply_one_longhand(
920 &mut self,
921 context: &mut computed::Context,
922 longhand_id: LonghandId,
923 declaration: &PropertyDeclaration,
924 priority: CascadePriority,
925 cache: &mut ShorthandsWithPropertyReferencesCache,
926 ) {
927 debug_assert!(!longhand_id.is_logical());
928 let origin = priority.cascade_level().origin();
929 if self.seen.contains(longhand_id) {
930 return;
931 }
932
933 if self.reverted_set.contains(longhand_id) {
934 if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&longhand_id) {
935 if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
936 return;
937 }
938 }
939 }
940
941 let mut declaration = self.substitute_variables_if_needed(context, cache, declaration);
942
943 if self.ignore_colors {
946 tweak_when_ignoring_colors(
947 context,
948 longhand_id,
949 origin,
950 &mut declaration,
951 &mut self.declarations_to_apply_unless_overridden,
952 );
953 }
954 let can_skip_apply = match declaration.get_css_wide_keyword() {
955 Some(keyword) => {
956 if matches!(
957 keyword,
958 CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert
959 ) {
960 let origin_revert = keyword == CSSWideKeyword::Revert;
961 self.reverted_set.insert(longhand_id);
964 self.reverted.insert(longhand_id, (priority, origin_revert));
965 return;
966 }
967
968 let inherited = longhand_id.inherited();
969 let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()
970 && longhand_id.zoom_dependent();
971 match keyword {
972 CSSWideKeyword::Revert | CSSWideKeyword::RevertLayer => unreachable!(),
973 CSSWideKeyword::Unset => !zoomed || !inherited,
974 CSSWideKeyword::Inherit => inherited && !zoomed,
975 CSSWideKeyword::Initial => !inherited,
976 }
977 },
978 None => false,
979 };
980
981 self.seen.insert(longhand_id);
982 if origin == Origin::Author {
983 self.author_specified.insert(longhand_id);
984 }
985
986 if !can_skip_apply {
987 unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
988 }
989 }
990
991 #[inline]
992 unsafe fn do_apply_declaration(
993 &self,
994 context: &mut computed::Context,
995 longhand_id: LonghandId,
996 declaration: &PropertyDeclaration,
997 ) {
998 debug_assert!(!longhand_id.is_logical());
999 (CASCADE_PROPERTY[longhand_id as usize])(&declaration, context);
1005 }
1006
1007 fn compute_visited_style_if_needed<E>(
1008 &self,
1009 context: &mut computed::Context,
1010 element: Option<E>,
1011 parent_style: Option<&ComputedValues>,
1012 layout_parent_style: Option<&ComputedValues>,
1013 visited_rules: &StrongRuleNode,
1014 guards: &StylesheetGuards,
1015 ) where
1016 E: TElement,
1017 {
1018 let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
1019
1020 macro_rules! visited_parent {
1021 ($parent:expr) => {
1022 if is_link {
1023 $parent
1024 } else {
1025 $parent.map(|p| p.visited_style().unwrap_or(p))
1026 }
1027 };
1028 }
1029
1030 let style = cascade_rules(
1033 context.builder.stylist.unwrap(),
1034 context.builder.pseudo,
1035 visited_rules,
1036 guards,
1037 visited_parent!(parent_style),
1038 visited_parent!(layout_parent_style),
1039 self.first_line_reparenting,
1040 self.try_tactic,
1041 CascadeMode::Visited {
1042 unvisited_context: &*context,
1043 },
1044 Default::default(),
1047 None, &mut *context.rule_cache_conditions.borrow_mut(),
1053 element,
1054 );
1055 context.builder.visited_style = Some(style);
1056 }
1057
1058 fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
1059 #[cfg(feature = "gecko")]
1060 {
1061 if let Some(bg) = builder.get_background_if_mutated() {
1062 bg.fill_arrays();
1063 }
1064
1065 if let Some(svg) = builder.get_svg_if_mutated() {
1066 svg.fill_arrays();
1067 }
1068 }
1069
1070 if self
1071 .author_specified
1072 .contains_any(LonghandIdSet::border_background_properties())
1073 {
1074 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
1075 }
1076
1077 if self.author_specified.contains(LonghandId::FontFamily) {
1078 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY);
1079 }
1080
1081 if self.author_specified.contains(LonghandId::Color) {
1082 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);
1083 }
1084
1085 if self.author_specified.contains(LonghandId::LetterSpacing) {
1086 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING);
1087 }
1088
1089 if self.author_specified.contains(LonghandId::WordSpacing) {
1090 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING);
1091 }
1092
1093 if self
1094 .author_specified
1095 .contains(LonghandId::FontSynthesisWeight)
1096 {
1097 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT);
1098 }
1099
1100 #[cfg(feature = "gecko")]
1101 if self
1102 .author_specified
1103 .contains(LonghandId::FontSynthesisStyle)
1104 {
1105 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE);
1106 }
1107
1108 #[cfg(feature = "servo")]
1109 {
1110 if let Some(font) = builder.get_font_if_mutated() {
1111 font.compute_font_hash();
1112 }
1113 }
1114 }
1115
1116 fn try_to_use_cached_reset_properties(
1117 &self,
1118 builder: &mut StyleBuilder<'b>,
1119 cache: Option<&'b RuleCache>,
1120 guards: &StylesheetGuards,
1121 ) -> bool {
1122 let style = match self.first_line_reparenting {
1123 FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
1124 FirstLineReparenting::No => {
1125 let Some(cache) = cache else { return false };
1126 let Some(style) = cache.find(guards, builder) else {
1127 return false;
1128 };
1129 style
1130 },
1131 };
1132
1133 builder.copy_reset_from(style);
1134
1135 let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND
1147 | ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS
1148 | ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS
1149 | ComputedValueFlags::USES_CONTAINER_UNITS
1150 | ComputedValueFlags::USES_VIEWPORT_UNITS;
1151 builder.add_flags(style.flags & bits_to_copy);
1152
1153 true
1154 }
1155
1156 #[inline]
1159 #[cfg(feature = "gecko")]
1160 fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
1161 use crate::gecko_bindings::bindings;
1162 use crate::values::computed::font::FontFamily;
1163
1164 let default_font_type = {
1165 let font = builder.get_font();
1166
1167 if !font.mFont.family.is_initial {
1168 return;
1169 }
1170
1171 let default_font_type = unsafe {
1172 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1173 builder.device.document(),
1174 font.mLanguage.mRawPtr,
1175 )
1176 };
1177
1178 let initial_generic = font.mFont.family.families.single_generic();
1179 debug_assert!(
1180 initial_generic.is_some(),
1181 "Initial font should be just one generic font"
1182 );
1183 if initial_generic == Some(default_font_type) {
1184 return;
1185 }
1186
1187 default_font_type
1188 };
1189
1190 builder.mutate_font().mFont.family.families =
1192 FontFamily::generic(default_font_type).families.clone();
1193 }
1194
1195 #[inline]
1197 #[cfg(feature = "gecko")]
1198 fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1199 use crate::gecko_bindings::bindings;
1200
1201 if static_prefs::pref!("browser.display.use_document_fonts") != 0
1204 || builder.device.chrome_rules_enabled_for_document()
1205 {
1206 return;
1207 }
1208
1209 let default_font_type = {
1210 let font = builder.get_font();
1211
1212 if font.mFont.family.is_system_font {
1213 return;
1214 }
1215
1216 if !font.mFont.family.families.needs_user_font_prioritization() {
1217 return;
1218 }
1219
1220 unsafe {
1221 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1222 builder.device.document(),
1223 font.mLanguage.mRawPtr,
1224 )
1225 }
1226 };
1227
1228 let font = builder.mutate_font();
1229 font.mFont
1230 .family
1231 .families
1232 .prioritize_first_generic_or_prepend(default_font_type);
1233 }
1234
1235 fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
1237 use crate::values::computed::ToComputedValue;
1238
1239 if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
1240 return;
1241 }
1242
1243 let new_size = {
1244 let font = context.builder.get_font();
1245 let info = font.clone_font_size().keyword_info;
1246 let new_size = match info.kw {
1247 specified::FontSizeKeyword::None => return,
1248 _ => {
1249 context.for_non_inherited_property = false;
1250 specified::FontSize::Keyword(info).to_computed_value(context)
1251 },
1252 };
1253
1254 #[cfg(feature = "gecko")]
1255 if font.mScriptUnconstrainedSize == new_size.computed_size {
1256 return;
1257 }
1258
1259 new_size
1260 };
1261
1262 context.builder.mutate_font().set_font_size(new_size);
1263 }
1264
1265 #[cfg(feature = "gecko")]
1268 fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
1269 use crate::gecko_bindings::bindings;
1270 use crate::values::generics::NonNegative;
1271
1272 let min_font_size = {
1273 let font = builder.get_font();
1274 let min_font_size = unsafe {
1275 bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
1276 };
1277
1278 if font.mFont.size.0 >= min_font_size {
1279 return;
1280 }
1281
1282 NonNegative(min_font_size)
1283 };
1284
1285 builder.mutate_font().mFont.size = min_font_size;
1286 }
1287
1288 #[cfg(feature = "gecko")]
1292 fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1293 debug_assert!(self.seen.contains(LonghandId::XTextScale));
1294
1295 let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
1296 let text_scale = builder.get_font().clone__x_text_scale();
1297 if parent_text_scale == text_scale {
1298 return;
1299 }
1300 debug_assert_ne!(
1301 parent_text_scale.text_zoom_enabled(),
1302 text_scale.text_zoom_enabled(),
1303 "There's only one value that disables it"
1304 );
1305 debug_assert!(
1306 !text_scale.text_zoom_enabled(),
1307 "We only ever disable text zoom never enable it"
1308 );
1309 let device = builder.device;
1310 builder.mutate_font().unzoom_fonts(device);
1311 }
1312
1313 fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
1314 debug_assert!(self.seen.contains(LonghandId::Zoom));
1315 let old_size = builder.get_font().clone_font_size();
1318 let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);
1319 if old_size == new_size {
1320 return;
1321 }
1322 builder.mutate_font().set_font_size(new_size);
1323 }
1324
1325 #[cfg(feature = "gecko")]
1330 fn recompute_math_font_size_if_needed(&self, context: &mut computed::Context) {
1331 use crate::values::generics::NonNegative;
1332
1333 if context.builder.get_font().clone_font_size().keyword_info.kw
1335 != specified::FontSizeKeyword::Math
1336 {
1337 return;
1338 }
1339
1340 const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
1341
1342 fn scale_factor_for_math_depth_change(
1352 parent_math_depth: i32,
1353 computed_math_depth: i32,
1354 parent_script_percent_scale_down: Option<f32>,
1355 parent_script_script_percent_scale_down: Option<f32>,
1356 ) -> f32 {
1357 let mut a = parent_math_depth;
1358 let mut b = computed_math_depth;
1359 let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
1360 let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c);
1361 let scale_between_0_and_2 =
1362 parent_script_script_percent_scale_down.unwrap_or_else(|| c * c);
1363 let mut s = 1.0;
1364 let mut invert_scale_factor = false;
1365 if a == b {
1366 return s;
1367 }
1368 if b < a {
1369 std::mem::swap(&mut a, &mut b);
1370 invert_scale_factor = true;
1371 }
1372 let mut e = b - a;
1373 if a <= 0 && b >= 2 {
1374 s *= scale_between_0_and_2;
1375 e -= 2;
1376 } else if a == 1 {
1377 s *= scale_between_0_and_2 / scale_between_0_and_1;
1378 e -= 1;
1379 } else if b == 1 {
1380 s *= scale_between_0_and_1;
1381 e -= 1;
1382 }
1383 s *= (c as f32).powi(e);
1384 if invert_scale_factor {
1385 1.0 / s.max(f32::MIN_POSITIVE)
1386 } else {
1387 s
1388 }
1389 }
1390
1391 let (new_size, new_unconstrained_size) = {
1392 use crate::values::specified::font::QueryFontMetricsFlags;
1393
1394 let builder = &context.builder;
1395 let font = builder.get_font();
1396 let parent_font = builder.get_parent_font();
1397
1398 let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1399
1400 if delta == 0 {
1401 return;
1402 }
1403
1404 let mut min = parent_font.mScriptMinSize;
1405 if font.mXTextScale.text_zoom_enabled() {
1406 min = builder.device.zoom_text(min);
1407 }
1408
1409 let scale = {
1411 let font_metrics = context.query_font_metrics(
1413 FontBaseSize::InheritedStyle,
1414 FontMetricsOrientation::Horizontal,
1415 QueryFontMetricsFlags::NEEDS_MATH_SCALES,
1416 );
1417 scale_factor_for_math_depth_change(
1418 parent_font.mMathDepth as i32,
1419 font.mMathDepth as i32,
1420 font_metrics.script_percent_scale_down,
1421 font_metrics.script_script_percent_scale_down,
1422 )
1423 };
1424
1425 let parent_size = parent_font.mSize.0;
1426 let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1427 let new_size = parent_size.scale_by(scale);
1428 let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1429
1430 if scale <= 1. {
1431 if parent_size <= min {
1436 (parent_size, new_unconstrained_size)
1437 } else {
1438 (min.max(new_size), new_unconstrained_size)
1439 }
1440 } else {
1441 (
1447 new_size.min(new_unconstrained_size.max(min)),
1448 new_unconstrained_size,
1449 )
1450 }
1451 };
1452 let font = context.builder.mutate_font();
1453 font.mFont.size = NonNegative(new_size);
1454 font.mSize = NonNegative(new_size);
1455 font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1456 }
1457}