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