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