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::{AttributeProvider, DummyAttributeProvider, 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 attr_provider: &dyn AttributeProvider,
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, attr_provider);
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_dependency(id, declaration) {
250 if let Some(ref mut builder) = custom_builder {
251 builder.maybe_note_non_custom_dependency(id, declaration);
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 rule_cache: Option<&'a RuleCache>,
273 rule_cache_conditions: &'a mut RuleCacheConditions,
274 element: Option<E>,
275) -> Arc<ComputedValues>
276where
277 E: TElement + 'a,
278 I: Iterator<Item = (&'a PropertyDeclaration, CascadePriority)>,
279{
280 debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
281 let device = stylist.device();
282 let inherited_style = parent_style.unwrap_or(device.default_computed_values());
283 let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
284
285 let container_size_query =
286 ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());
287
288 let mut context = computed::Context::new(
289 StyleBuilder::new(
293 device,
294 Some(stylist),
295 parent_style,
296 pseudo,
297 Some(rules.clone()),
298 is_root_element,
299 ),
300 stylist.quirks_mode(),
301 rule_cache_conditions,
302 container_size_query,
303 );
304
305 context.style().add_flags(cascade_input_flags);
306
307 let using_cached_reset_properties;
308 let ignore_colors = context.builder.device.forced_colors().is_active();
309 let mut cascade = Cascade::new(first_line_reparenting, try_tactic, ignore_colors);
310 let mut declarations = Default::default();
311 let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
312 let attr_provider: &dyn AttributeProvider = match element {
313 Some(ref attr_provider) => attr_provider,
314 None => &DummyAttributeProvider {},
315 };
316 let properties_to_apply = match cascade_mode {
317 CascadeMode::Visited { unvisited_context } => {
318 context.builder.custom_properties = unvisited_context.builder.custom_properties.clone();
319 context.builder.writing_mode = unvisited_context.builder.writing_mode;
320 context.builder.color_scheme = unvisited_context.builder.color_scheme;
321 using_cached_reset_properties = false;
325 iter_declarations(iter, &mut declarations, None, attr_provider);
329
330 LonghandIdSet::visited_dependent()
331 },
332 CascadeMode::Unvisited { visited_rules } => {
333 let deferred_custom_properties = {
334 let mut builder = CustomPropertiesBuilder::new(stylist, &mut context);
335 iter_declarations(iter, &mut declarations, Some(&mut builder), attr_provider);
336 builder.build(
341 DeferFontRelativeCustomPropertyResolution::Yes,
342 attr_provider,
343 )
344 };
345
346 cascade.apply_prioritary_properties(
349 &mut context,
350 &declarations,
351 &mut shorthand_cache,
352 attr_provider,
353 );
354
355 if let Some(deferred) = deferred_custom_properties {
357 CustomPropertiesBuilder::build_deferred(
358 deferred,
359 stylist,
360 &mut context,
361 attr_provider,
362 );
363 }
364
365 if let Some(visited_rules) = visited_rules {
366 cascade.compute_visited_style_if_needed(
367 &mut context,
368 element,
369 parent_style,
370 layout_parent_style,
371 visited_rules,
372 guards,
373 );
374 }
375
376 using_cached_reset_properties = cascade.try_to_use_cached_reset_properties(
377 &mut context.builder,
378 rule_cache,
379 guards,
380 );
381
382 if using_cached_reset_properties {
383 LonghandIdSet::late_group_only_inherited()
384 } else {
385 LonghandIdSet::late_group()
386 }
387 },
388 };
389
390 cascade.apply_non_prioritary_properties(
391 &mut context,
392 &declarations.longhand_declarations,
393 &mut shorthand_cache,
394 &properties_to_apply,
395 attr_provider,
396 );
397
398 cascade.finished_applying_properties(&mut context.builder);
399
400 std::mem::drop(cascade);
401
402 context.builder.clear_modified_reset();
403
404 if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
405 StyleAdjuster::new(&mut context.builder).adjust(
406 layout_parent_style.unwrap_or(inherited_style),
407 element,
408 try_tactic,
409 );
410 }
411
412 if context.builder.modified_reset() || using_cached_reset_properties {
413 context.rule_cache_conditions.borrow_mut().set_uncacheable();
419 }
420
421 context.builder.build()
422}
423
424type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
430
431fn tweak_when_ignoring_colors(
432 context: &computed::Context,
433 longhand_id: LonghandId,
434 origin: Origin,
435 declaration: &mut Cow<PropertyDeclaration>,
436 declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
437) {
438 use crate::values::computed::ToComputedValue;
439 use crate::values::specified::Color;
440
441 if !longhand_id.ignored_when_document_colors_disabled() {
442 return;
443 }
444
445 let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
446 if is_ua_or_user_rule {
447 return;
448 }
449
450 #[cfg(feature = "gecko")]
452 {
453 let forced = context
454 .builder
455 .get_inherited_text()
456 .clone_forced_color_adjust();
457 if forced == computed::ForcedColorAdjust::None {
458 return;
459 }
460 }
461
462 if context
466 .builder
467 .pseudo
468 .map_or(false, |p| p.is_color_swatch())
469 && longhand_id == LonghandId::BackgroundColor
470 {
471 return;
472 }
473
474 fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
475 color
477 .to_computed_value(context)
478 .resolve_to_absolute(&AbsoluteColor::BLACK)
479 .alpha
480 }
481
482 match **declaration {
484 PropertyDeclaration::CSSWideKeyword(..) => return,
486 PropertyDeclaration::BackgroundColor(ref color) => {
487 if color.honored_in_forced_colors_mode(true) {
497 return;
498 }
499 let alpha = alpha_channel(color, context);
503 if alpha == 0.0 {
504 return;
505 }
506 let mut color = context.builder.device.default_background_color();
507 color.alpha = alpha;
508 declarations_to_apply_unless_overridden
509 .push(PropertyDeclaration::BackgroundColor(color.into()))
510 },
511 PropertyDeclaration::Color(ref color) => {
512 if color
514 .0
515 .honored_in_forced_colors_mode(true)
516 {
517 return;
518 }
519 if context
523 .builder
524 .get_parent_inherited_text()
525 .clone_color()
526 .alpha
527 == 0.0
528 {
529 let color = context.builder.device.default_color();
530 declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
531 specified::ColorPropertyValue(color.into()),
532 ))
533 }
534 },
535 #[cfg(feature = "gecko")]
537 PropertyDeclaration::BackgroundImage(ref bkg) => {
538 use crate::values::generics::image::Image;
539 if static_prefs::pref!("browser.display.permit_backplate") {
540 if bkg
541 .0
542 .iter()
543 .all(|image| matches!(*image, Image::Url(..) | Image::None))
544 {
545 return;
546 }
547 }
548 },
549 _ => {
550 if let Some(color) = declaration.color_value() {
563 if color.honored_in_forced_colors_mode(false) {
564 return;
565 }
566 }
567 },
568 }
569
570 *declaration.to_mut() =
571 PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
572}
573
574type DeclarationIndex = u16;
576
577#[derive(Copy, Clone)]
582struct PrioritaryDeclarationPosition {
583 most_important: DeclarationIndex,
585 least_important: DeclarationIndex,
586}
587
588impl Default for PrioritaryDeclarationPosition {
589 fn default() -> Self {
590 Self {
591 most_important: DeclarationIndex::MAX,
592 least_important: DeclarationIndex::MAX,
593 }
594 }
595}
596
597#[derive(Copy, Clone)]
598struct Declaration<'a> {
599 decl: &'a PropertyDeclaration,
600 priority: CascadePriority,
601 next_index: DeclarationIndex,
602}
603
604#[derive(Default)]
606struct Declarations<'a> {
607 has_prioritary_properties: bool,
609 longhand_declarations: SmallVec<[Declaration<'a>; 64]>,
611 prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
613}
614
615impl<'a> Declarations<'a> {
616 fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
617 let new_index = self.longhand_declarations.len();
618 if new_index >= DeclarationIndex::MAX as usize {
619 return;
622 }
623
624 self.has_prioritary_properties = true;
625 let new_index = new_index as DeclarationIndex;
626 let position = &mut self.prioritary_positions[id as usize];
627 if position.most_important == DeclarationIndex::MAX {
628 position.most_important = new_index;
631 } else {
632 self.longhand_declarations[position.least_important as usize].next_index = new_index;
634 }
635 position.least_important = new_index;
636 }
637
638 fn note_declaration(
639 &mut self,
640 decl: &'a PropertyDeclaration,
641 priority: CascadePriority,
642 id: LonghandId,
643 ) {
644 if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
645 self.note_prioritary_property(id);
646 }
647 self.longhand_declarations.push(Declaration {
648 decl,
649 priority,
650 next_index: 0,
651 });
652 }
653}
654
655struct Cascade<'b> {
656 first_line_reparenting: FirstLineReparenting<'b>,
657 try_tactic: &'b PositionTryFallbacksTryTactic,
658 ignore_colors: bool,
659 seen: LonghandIdSet,
660 author_specified: LonghandIdSet,
661 reverted_set: LonghandIdSet,
662 reverted: FxHashMap<LonghandId, (CascadePriority, bool)>,
663 declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
664}
665
666impl<'b> Cascade<'b> {
667 fn new(
668 first_line_reparenting: FirstLineReparenting<'b>,
669 try_tactic: &'b PositionTryFallbacksTryTactic,
670 ignore_colors: bool,
671 ) -> Self {
672 Self {
673 first_line_reparenting,
674 try_tactic,
675 ignore_colors,
676 seen: LonghandIdSet::default(),
677 author_specified: LonghandIdSet::default(),
678 reverted_set: Default::default(),
679 reverted: Default::default(),
680 declarations_to_apply_unless_overridden: Default::default(),
681 }
682 }
683
684 fn substitute_variables_if_needed<'cache, 'decl>(
685 &self,
686 context: &mut computed::Context,
687 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
688 declaration: &'decl PropertyDeclaration,
689 attr_provider: &dyn AttributeProvider,
690 ) -> Cow<'decl, PropertyDeclaration>
691 where
692 'cache: 'decl,
693 {
694 let declaration = match *declaration {
695 PropertyDeclaration::WithVariables(ref declaration) => declaration,
696 ref d => return Cow::Borrowed(d),
697 };
698
699 if !declaration.id.inherited() {
700 context.rule_cache_conditions.borrow_mut().set_uncacheable();
701
702 match declaration.id {
708 LonghandId::Display => {
709 context
710 .builder
711 .add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
712 },
713 LonghandId::Content => {
714 context
715 .builder
716 .add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
717 },
718 _ => {},
719 }
720 }
721
722 debug_assert!(
723 context.builder.stylist.is_some(),
724 "Need a Stylist to substitute variables!"
725 );
726 declaration.value.substitute_variables(
727 declaration.id,
728 context.builder.custom_properties(),
729 context.builder.stylist.unwrap(),
730 context,
731 shorthand_cache,
732 attr_provider,
733 )
734 }
735
736 fn apply_one_prioritary_property(
737 &mut self,
738 context: &mut computed::Context,
739 decls: &Declarations,
740 cache: &mut ShorthandsWithPropertyReferencesCache,
741 id: PrioritaryPropertyId,
742 attr_provider: &dyn AttributeProvider,
743 ) -> bool {
744 let mut index = decls.prioritary_positions[id as usize].most_important;
745 if index == DeclarationIndex::MAX {
746 return false;
747 }
748
749 let longhand_id = id.to_longhand();
750 debug_assert!(
751 !longhand_id.is_logical(),
752 "That could require more book-keeping"
753 );
754 loop {
755 let decl = decls.longhand_declarations[index as usize];
756 self.apply_one_longhand(
757 context,
758 longhand_id,
759 decl.decl,
760 decl.priority,
761 cache,
762 attr_provider,
763 );
764 if self.seen.contains(longhand_id) {
765 return true; }
767 debug_assert!(
768 self.reverted_set.contains(longhand_id),
769 "How else can we fail to apply a prioritary property?"
770 );
771 debug_assert!(
772 decl.next_index == 0 || decl.next_index > index,
773 "should make progress! {} -> {}",
774 index,
775 decl.next_index,
776 );
777 index = decl.next_index;
778 if index == 0 {
779 break;
780 }
781 }
782 false
783 }
784
785 fn apply_prioritary_properties(
786 &mut self,
787 context: &mut computed::Context,
788 decls: &Declarations,
789 cache: &mut ShorthandsWithPropertyReferencesCache,
790 attr_provider: &dyn AttributeProvider,
791 ) {
792 macro_rules! apply {
795 ($prop:ident) => {
796 self.apply_one_prioritary_property(
797 context,
798 decls,
799 cache,
800 PrioritaryPropertyId::$prop,
801 attr_provider,
802 )
803 };
804 }
805
806 if !decls.has_prioritary_properties {
807 return;
808 }
809
810 let has_writing_mode = apply!(WritingMode) | apply!(Direction);
811 #[cfg(feature = "gecko")]
812 let has_writing_mode = has_writing_mode | apply!(TextOrientation);
813
814 if has_writing_mode {
815 context.builder.writing_mode = WritingMode::new(context.builder.get_inherited_box())
816 }
817
818 if apply!(Zoom) {
819 context.builder.recompute_effective_zooms();
820 if !context.builder.effective_zoom_for_inheritance.is_one() {
821 self.recompute_font_size_for_zoom_change(&mut context.builder);
827 }
828 }
829
830 let has_font_family = apply!(FontFamily);
832 let has_lang = apply!(XLang);
833 #[cfg(feature = "gecko")]
834 {
835 if has_lang {
836 self.recompute_initial_font_family_if_needed(&mut context.builder);
837 }
838 if has_font_family {
839 self.prioritize_user_fonts_if_needed(&mut context.builder);
840 }
841
842 if apply!(XTextScale) {
844 self.unzoom_fonts_if_needed(&mut context.builder);
845 }
846 let has_font_size = apply!(FontSize);
847 let has_math_depth = apply!(MathDepth);
848 let has_min_font_size_ratio = apply!(MozMinFontSizeRatio);
849
850 if has_math_depth && has_font_size {
851 self.recompute_math_font_size_if_needed(context);
852 }
853 if has_lang || has_font_family {
854 self.recompute_keyword_font_size_if_needed(context);
855 }
856 if has_font_size || has_min_font_size_ratio || has_lang || has_font_family {
857 self.constrain_font_size_if_needed(&mut context.builder);
858 }
859 }
860
861 #[cfg(feature = "servo")]
862 {
863 apply!(FontSize);
864 if has_lang || has_font_family {
865 self.recompute_keyword_font_size_if_needed(context);
866 }
867 }
868
869 apply!(FontWeight);
871 apply!(FontStretch);
872 apply!(FontStyle);
873 #[cfg(feature = "gecko")]
874 apply!(FontSizeAdjust);
875
876 #[cfg(feature = "gecko")]
877 apply!(ForcedColorAdjust);
878 if apply!(ColorScheme) {
881 context.builder.color_scheme = context.builder.get_inherited_ui().color_scheme_bits();
882 }
883 apply!(LineHeight);
884 }
885
886 fn apply_non_prioritary_properties(
887 &mut self,
888 context: &mut computed::Context,
889 longhand_declarations: &[Declaration],
890 shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
891 properties_to_apply: &LonghandIdSet,
892 attr_provider: &dyn AttributeProvider,
893 ) {
894 debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
895 debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
896 for declaration in &*longhand_declarations {
897 let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
898 if !properties_to_apply.contains(longhand_id) {
899 continue;
900 }
901 debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
902 let is_logical = longhand_id.is_logical();
903 if is_logical {
904 let wm = context.builder.writing_mode;
905 context
906 .rule_cache_conditions
907 .borrow_mut()
908 .set_writing_mode_dependency(wm);
909 longhand_id = longhand_id.to_physical(wm);
910 }
911 self.apply_one_longhand(
912 context,
913 longhand_id,
914 declaration.decl,
915 declaration.priority,
916 shorthand_cache,
917 attr_provider,
918 );
919 }
920 if !self.declarations_to_apply_unless_overridden.is_empty() {
921 debug_assert!(self.ignore_colors);
922 for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
923 let longhand_id = declaration.id().as_longhand().unwrap();
924 debug_assert!(!longhand_id.is_logical());
925 if !self.seen.contains(longhand_id) {
926 unsafe {
927 self.do_apply_declaration(context, longhand_id, &declaration);
928 }
929 }
930 }
931 }
932
933 if !context.builder.effective_zoom_for_inheritance.is_one() {
934 self.recompute_zoom_dependent_inherited_lengths(context);
935 }
936 }
937
938 #[cold]
939 fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {
940 debug_assert!(self.seen.contains(LonghandId::Zoom));
941 for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {
942 if self.seen.contains(prop) {
943 continue;
944 }
945 let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);
946 unsafe {
947 self.do_apply_declaration(context, prop, &declaration);
948 }
949 }
950 }
951
952 fn apply_one_longhand(
953 &mut self,
954 context: &mut computed::Context,
955 longhand_id: LonghandId,
956 declaration: &PropertyDeclaration,
957 priority: CascadePriority,
958 cache: &mut ShorthandsWithPropertyReferencesCache,
959 attr_provider: &dyn AttributeProvider,
960 ) {
961 debug_assert!(!longhand_id.is_logical());
962 let origin = priority.cascade_level().origin();
963 if self.seen.contains(longhand_id) {
964 return;
965 }
966
967 if self.reverted_set.contains(longhand_id) {
968 if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&longhand_id) {
969 if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
970 return;
971 }
972 }
973 }
974
975 let mut declaration =
976 self.substitute_variables_if_needed(context, cache, declaration, attr_provider);
977
978 if self.ignore_colors {
981 tweak_when_ignoring_colors(
982 context,
983 longhand_id,
984 origin,
985 &mut declaration,
986 &mut self.declarations_to_apply_unless_overridden,
987 );
988 }
989 let can_skip_apply = match declaration.get_css_wide_keyword() {
990 Some(keyword) => {
991 if matches!(
992 keyword,
993 CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert
994 ) {
995 let origin_revert = keyword == CSSWideKeyword::Revert;
996 self.reverted_set.insert(longhand_id);
999 self.reverted.insert(longhand_id, (priority, origin_revert));
1000 return;
1001 }
1002
1003 let inherited = longhand_id.inherited();
1004 let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()
1005 && longhand_id.zoom_dependent();
1006 match keyword {
1007 CSSWideKeyword::Revert | CSSWideKeyword::RevertLayer => unreachable!(),
1008 CSSWideKeyword::Unset => !zoomed || !inherited,
1009 CSSWideKeyword::Inherit => inherited && !zoomed,
1010 CSSWideKeyword::Initial => !inherited,
1011 }
1012 },
1013 None => false,
1014 };
1015
1016 self.seen.insert(longhand_id);
1017 if origin == Origin::Author {
1018 self.author_specified.insert(longhand_id);
1019 }
1020
1021 if !can_skip_apply {
1022 unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
1023 }
1024 }
1025
1026 #[inline]
1027 unsafe fn do_apply_declaration(
1028 &self,
1029 context: &mut computed::Context,
1030 longhand_id: LonghandId,
1031 declaration: &PropertyDeclaration,
1032 ) {
1033 debug_assert!(!longhand_id.is_logical());
1034 (CASCADE_PROPERTY[longhand_id as usize])(&declaration, context);
1040 }
1041
1042 fn compute_visited_style_if_needed<E>(
1043 &self,
1044 context: &mut computed::Context,
1045 element: Option<E>,
1046 parent_style: Option<&ComputedValues>,
1047 layout_parent_style: Option<&ComputedValues>,
1048 visited_rules: &StrongRuleNode,
1049 guards: &StylesheetGuards,
1050 ) where
1051 E: TElement,
1052 {
1053 let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
1054
1055 macro_rules! visited_parent {
1056 ($parent:expr) => {
1057 if is_link {
1058 $parent
1059 } else {
1060 $parent.map(|p| p.visited_style().unwrap_or(p))
1061 }
1062 };
1063 }
1064
1065 let style = cascade_rules(
1068 context.builder.stylist.unwrap(),
1069 context.builder.pseudo,
1070 visited_rules,
1071 guards,
1072 visited_parent!(parent_style),
1073 visited_parent!(layout_parent_style),
1074 self.first_line_reparenting,
1075 self.try_tactic,
1076 CascadeMode::Visited {
1077 unvisited_context: &*context,
1078 },
1079 Default::default(),
1082 None, &mut *context.rule_cache_conditions.borrow_mut(),
1088 element,
1089 );
1090 context.builder.visited_style = Some(style);
1091 }
1092
1093 fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
1094 #[cfg(feature = "gecko")]
1095 {
1096 if let Some(bg) = builder.get_background_if_mutated() {
1097 bg.fill_arrays();
1098 }
1099
1100 if let Some(svg) = builder.get_svg_if_mutated() {
1101 svg.fill_arrays();
1102 }
1103 }
1104
1105 if self
1106 .author_specified
1107 .contains_any(LonghandIdSet::border_background_properties())
1108 {
1109 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
1110 }
1111
1112 if self.author_specified.contains(LonghandId::FontFamily) {
1113 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY);
1114 }
1115
1116 if self.author_specified.contains(LonghandId::Color) {
1117 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);
1118 }
1119
1120 if self.author_specified.contains(LonghandId::LetterSpacing) {
1121 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING);
1122 }
1123
1124 if self.author_specified.contains(LonghandId::WordSpacing) {
1125 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING);
1126 }
1127
1128 if self
1129 .author_specified
1130 .contains(LonghandId::FontSynthesisWeight)
1131 {
1132 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT);
1133 }
1134
1135 #[cfg(feature = "gecko")]
1136 if self
1137 .author_specified
1138 .contains(LonghandId::FontSynthesisStyle)
1139 {
1140 builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE);
1141 }
1142
1143 #[cfg(feature = "servo")]
1144 {
1145 if let Some(font) = builder.get_font_if_mutated() {
1146 font.compute_font_hash();
1147 }
1148 }
1149 }
1150
1151 fn try_to_use_cached_reset_properties(
1152 &self,
1153 builder: &mut StyleBuilder<'b>,
1154 cache: Option<&'b RuleCache>,
1155 guards: &StylesheetGuards,
1156 ) -> bool {
1157 let style = match self.first_line_reparenting {
1158 FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
1159 FirstLineReparenting::No => {
1160 let Some(cache) = cache else { return false };
1161 let Some(style) = cache.find(guards, builder) else {
1162 return false;
1163 };
1164 style
1165 },
1166 };
1167
1168 builder.copy_reset_from(style);
1169
1170 let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND
1182 | ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS
1183 | ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS
1184 | ComputedValueFlags::USES_CONTAINER_UNITS
1185 | ComputedValueFlags::USES_VIEWPORT_UNITS;
1186 builder.add_flags(style.flags & bits_to_copy);
1187
1188 true
1189 }
1190
1191 #[inline]
1194 #[cfg(feature = "gecko")]
1195 fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
1196 use crate::gecko_bindings::bindings;
1197 use crate::values::computed::font::FontFamily;
1198
1199 let default_font_type = {
1200 let font = builder.get_font();
1201
1202 if !font.mFont.family.is_initial {
1203 return;
1204 }
1205
1206 let default_font_type = unsafe {
1207 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1208 builder.device.document(),
1209 font.mLanguage.mRawPtr,
1210 )
1211 };
1212
1213 let initial_generic = font.mFont.family.families.single_generic();
1214 debug_assert!(
1215 initial_generic.is_some(),
1216 "Initial font should be just one generic font"
1217 );
1218 if initial_generic == Some(default_font_type) {
1219 return;
1220 }
1221
1222 default_font_type
1223 };
1224
1225 builder.mutate_font().mFont.family.families =
1227 FontFamily::generic(default_font_type).families.clone();
1228 }
1229
1230 #[inline]
1232 #[cfg(feature = "gecko")]
1233 fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1234 use crate::gecko_bindings::bindings;
1235
1236 if static_prefs::pref!("browser.display.use_document_fonts") != 0
1239 || builder.device.chrome_rules_enabled_for_document()
1240 {
1241 return;
1242 }
1243
1244 let default_font_type = {
1245 let font = builder.get_font();
1246
1247 if font.mFont.family.is_system_font {
1248 return;
1249 }
1250
1251 if !font.mFont.family.families.needs_user_font_prioritization() {
1252 return;
1253 }
1254
1255 unsafe {
1256 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1257 builder.device.document(),
1258 font.mLanguage.mRawPtr,
1259 )
1260 }
1261 };
1262
1263 let font = builder.mutate_font();
1264 font.mFont
1265 .family
1266 .families
1267 .prioritize_first_generic_or_prepend(default_font_type);
1268 }
1269
1270 fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
1272 use crate::values::computed::ToComputedValue;
1273
1274 if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
1275 return;
1276 }
1277
1278 let new_size = {
1279 let font = context.builder.get_font();
1280 let info = font.clone_font_size().keyword_info;
1281 let new_size = match info.kw {
1282 specified::FontSizeKeyword::None => return,
1283 _ => {
1284 context.for_non_inherited_property = false;
1285 specified::FontSize::Keyword(info).to_computed_value(context)
1286 },
1287 };
1288
1289 #[cfg(feature = "gecko")]
1290 if font.mScriptUnconstrainedSize == new_size.computed_size {
1291 return;
1292 }
1293
1294 new_size
1295 };
1296
1297 context.builder.mutate_font().set_font_size(new_size);
1298 }
1299
1300 #[cfg(feature = "gecko")]
1303 fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
1304 use crate::gecko_bindings::bindings;
1305 use crate::values::generics::NonNegative;
1306
1307 let min_font_size = {
1308 let font = builder.get_font();
1309 let min_font_size = unsafe {
1310 bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
1311 };
1312
1313 if font.mFont.size.0 >= min_font_size {
1314 return;
1315 }
1316
1317 NonNegative(min_font_size)
1318 };
1319
1320 builder.mutate_font().mFont.size = min_font_size;
1321 }
1322
1323 #[cfg(feature = "gecko")]
1327 fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1328 debug_assert!(self.seen.contains(LonghandId::XTextScale));
1329
1330 let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
1331 let text_scale = builder.get_font().clone__x_text_scale();
1332 if parent_text_scale == text_scale {
1333 return;
1334 }
1335 debug_assert_ne!(
1336 parent_text_scale.text_zoom_enabled(),
1337 text_scale.text_zoom_enabled(),
1338 "There's only one value that disables it"
1339 );
1340 debug_assert!(
1341 !text_scale.text_zoom_enabled(),
1342 "We only ever disable text zoom never enable it"
1343 );
1344 let device = builder.device;
1345 builder.mutate_font().unzoom_fonts(device);
1346 }
1347
1348 fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
1349 debug_assert!(self.seen.contains(LonghandId::Zoom));
1350 let old_size = builder.get_font().clone_font_size();
1353 let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);
1354 if old_size == new_size {
1355 return;
1356 }
1357 builder.mutate_font().set_font_size(new_size);
1358 }
1359
1360 #[cfg(feature = "gecko")]
1365 fn recompute_math_font_size_if_needed(&self, context: &mut computed::Context) {
1366 use crate::values::generics::NonNegative;
1367
1368 if context.builder.get_font().clone_font_size().keyword_info.kw
1370 != specified::FontSizeKeyword::Math
1371 {
1372 return;
1373 }
1374
1375 const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
1376
1377 fn scale_factor_for_math_depth_change(
1387 parent_math_depth: i32,
1388 computed_math_depth: i32,
1389 parent_script_percent_scale_down: Option<f32>,
1390 parent_script_script_percent_scale_down: Option<f32>,
1391 ) -> f32 {
1392 let mut a = parent_math_depth;
1393 let mut b = computed_math_depth;
1394 let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
1395 let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c);
1396 let scale_between_0_and_2 =
1397 parent_script_script_percent_scale_down.unwrap_or_else(|| c * c);
1398 let mut s = 1.0;
1399 let mut invert_scale_factor = false;
1400 if a == b {
1401 return s;
1402 }
1403 if b < a {
1404 std::mem::swap(&mut a, &mut b);
1405 invert_scale_factor = true;
1406 }
1407 let mut e = b - a;
1408 if a <= 0 && b >= 2 {
1409 s *= scale_between_0_and_2;
1410 e -= 2;
1411 } else if a == 1 {
1412 s *= scale_between_0_and_2 / scale_between_0_and_1;
1413 e -= 1;
1414 } else if b == 1 {
1415 s *= scale_between_0_and_1;
1416 e -= 1;
1417 }
1418 s *= (c as f32).powi(e);
1419 if invert_scale_factor {
1420 1.0 / s.max(f32::MIN_POSITIVE)
1421 } else {
1422 s
1423 }
1424 }
1425
1426 let (new_size, new_unconstrained_size) = {
1427 use crate::values::specified::font::QueryFontMetricsFlags;
1428
1429 let builder = &context.builder;
1430 let font = builder.get_font();
1431 let parent_font = builder.get_parent_font();
1432
1433 let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1434
1435 if delta == 0 {
1436 return;
1437 }
1438
1439 let mut min = parent_font.mScriptMinSize;
1440 if font.mXTextScale.text_zoom_enabled() {
1441 min = builder.device.zoom_text(min);
1442 }
1443
1444 let scale = {
1446 let font_metrics = context.query_font_metrics(
1448 FontBaseSize::InheritedStyle,
1449 FontMetricsOrientation::Horizontal,
1450 QueryFontMetricsFlags::NEEDS_MATH_SCALES,
1451 );
1452 scale_factor_for_math_depth_change(
1453 parent_font.mMathDepth as i32,
1454 font.mMathDepth as i32,
1455 font_metrics.script_percent_scale_down,
1456 font_metrics.script_script_percent_scale_down,
1457 )
1458 };
1459
1460 let parent_size = parent_font.mSize.0;
1461 let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1462 let new_size = parent_size.scale_by(scale);
1463 let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1464
1465 if scale <= 1. {
1466 if parent_size <= min {
1471 (parent_size, new_unconstrained_size)
1472 } else {
1473 (min.max(new_size), new_unconstrained_size)
1474 }
1475 } else {
1476 (
1482 new_size.min(new_unconstrained_size.max(min)),
1483 new_unconstrained_size,
1484 )
1485 }
1486 };
1487 let font = context.builder.mutate_font();
1488 font.mFont.size = NonNegative(new_size);
1489 font.mSize = NonNegative(new_size);
1490 font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1491 }
1492}