Bug 1883017: Handle resizable buffers in ArrayBufferObject::ensureNonInline. r=sfink
[gecko.git] / servo / components / style / properties / cascade.rs
blobd4d48eaeb83f0e7cfc990aa32ca4c26f971bbe5a
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
5 //! The main cascading algorithm of the style system.
7 use crate::applicable_declarations::CascadePriority;
8 use crate::color::AbsoluteColor;
9 use crate::computed_value_flags::ComputedValueFlags;
10 use crate::custom_properties::{
11     CustomPropertiesBuilder, DeferFontRelativeCustomPropertyResolution,
13 use crate::dom::TElement;
14 use crate::font_metrics::FontMetricsOrientation;
15 use crate::logical_geometry::WritingMode;
16 use crate::properties::{
17     property_counts, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, Importance,
18     LonghandId, LonghandIdSet, PrioritaryPropertyId, PropertyDeclaration, PropertyDeclarationId,
19     PropertyFlags, ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
21 use crate::rule_cache::{RuleCache, RuleCacheConditions};
22 use crate::rule_tree::{CascadeLevel, StrongRuleNode};
23 use crate::selector_parser::PseudoElement;
24 use crate::shared_lock::StylesheetGuards;
25 use crate::style_adjuster::StyleAdjuster;
26 use crate::stylesheets::container_rule::ContainerSizeQuery;
27 use crate::stylesheets::{layer_rule::LayerOrder, Origin};
28 use crate::stylist::Stylist;
29 use crate::values::specified::length::FontBaseSize;
30 use crate::values::{computed, specified};
31 use fxhash::FxHashMap;
32 use servo_arc::Arc;
33 use smallvec::SmallVec;
34 use std::borrow::Cow;
35 use std::mem;
37 /// Whether we're resolving a style with the purposes of reparenting for ::first-line.
38 #[derive(Copy, Clone)]
39 #[allow(missing_docs)]
40 pub enum FirstLineReparenting<'a> {
41     No,
42     Yes {
43         /// The style we're re-parenting for ::first-line. ::first-line only affects inherited
44         /// properties so we use this to avoid some work and also ensure correctness by copying the
45         /// reset structs from this style.
46         style_to_reparent: &'a ComputedValues,
47     },
50 /// Performs the CSS cascade, computing new styles for an element from its parent style.
51 ///
52 /// The arguments are:
53 ///
54 ///   * `device`: Used to get the initial viewport and other external state.
55 ///
56 ///   * `rule_node`: The rule node in the tree that represent the CSS rules that
57 ///   matched.
58 ///
59 ///   * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
60 ///
61 /// Returns the computed values.
62 ///   * `flags`: Various flags.
63 ///
64 pub fn cascade<E>(
65     stylist: &Stylist,
66     pseudo: Option<&PseudoElement>,
67     rule_node: &StrongRuleNode,
68     guards: &StylesheetGuards,
69     parent_style: Option<&ComputedValues>,
70     layout_parent_style: Option<&ComputedValues>,
71     first_line_reparenting: FirstLineReparenting,
72     visited_rules: Option<&StrongRuleNode>,
73     cascade_input_flags: ComputedValueFlags,
74     rule_cache: Option<&RuleCache>,
75     rule_cache_conditions: &mut RuleCacheConditions,
76     element: Option<E>,
77 ) -> Arc<ComputedValues>
78 where
79     E: TElement,
81     cascade_rules(
82         stylist,
83         pseudo,
84         rule_node,
85         guards,
86         parent_style,
87         layout_parent_style,
88         first_line_reparenting,
89         CascadeMode::Unvisited { visited_rules },
90         cascade_input_flags,
91         rule_cache,
92         rule_cache_conditions,
93         element,
94     )
97 struct DeclarationIterator<'a> {
98     // Global to the iteration.
99     guards: &'a StylesheetGuards<'a>,
100     restriction: Option<PropertyFlags>,
101     // The rule we're iterating over.
102     current_rule_node: Option<&'a StrongRuleNode>,
103     // Per rule state.
104     declarations: DeclarationImportanceIterator<'a>,
105     origin: Origin,
106     importance: Importance,
107     priority: CascadePriority,
110 impl<'a> DeclarationIterator<'a> {
111     #[inline]
112     fn new(
113         rule_node: &'a StrongRuleNode,
114         guards: &'a StylesheetGuards,
115         pseudo: Option<&PseudoElement>,
116     ) -> Self {
117         let restriction = pseudo.and_then(|p| p.property_restriction());
118         let mut iter = Self {
119             guards,
120             current_rule_node: Some(rule_node),
121             origin: Origin::UserAgent,
122             importance: Importance::Normal,
123             priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()),
124             declarations: DeclarationImportanceIterator::default(),
125             restriction,
126         };
127         iter.update_for_node(rule_node);
128         iter
129     }
131     fn update_for_node(&mut self, node: &'a StrongRuleNode) {
132         self.priority = node.cascade_priority();
133         let level = self.priority.cascade_level();
134         self.origin = level.origin();
135         self.importance = level.importance();
136         let guard = match self.origin {
137             Origin::Author => self.guards.author,
138             Origin::User | Origin::UserAgent => self.guards.ua_or_user,
139         };
140         self.declarations = match node.style_source() {
141             Some(source) => source.read(guard).declaration_importance_iter(),
142             None => DeclarationImportanceIterator::default(),
143         };
144     }
147 impl<'a> Iterator for DeclarationIterator<'a> {
148     type Item = (&'a PropertyDeclaration, CascadePriority);
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.importance != importance {
155                     continue;
156                 }
158                 if let Some(restriction) = self.restriction {
159                     // decl.id() is either a longhand or a custom
160                     // property.  Custom properties are always allowed, but
161                     // longhands are only allowed if they have our
162                     // restriction flag set.
163                     if let PropertyDeclarationId::Longhand(id) = decl.id() {
164                         if !id.flags().contains(restriction) && self.origin != Origin::UserAgent {
165                             continue;
166                         }
167                     }
168                 }
170                 return Some((decl, self.priority));
171             }
173             let next_node = self.current_rule_node.take()?.parent()?;
174             self.current_rule_node = Some(next_node);
175             self.update_for_node(next_node);
176         }
177     }
180 fn cascade_rules<E>(
181     stylist: &Stylist,
182     pseudo: Option<&PseudoElement>,
183     rule_node: &StrongRuleNode,
184     guards: &StylesheetGuards,
185     parent_style: Option<&ComputedValues>,
186     layout_parent_style: Option<&ComputedValues>,
187     first_line_reparenting: FirstLineReparenting,
188     cascade_mode: CascadeMode,
189     cascade_input_flags: ComputedValueFlags,
190     rule_cache: Option<&RuleCache>,
191     rule_cache_conditions: &mut RuleCacheConditions,
192     element: Option<E>,
193 ) -> Arc<ComputedValues>
194 where
195     E: TElement,
197     apply_declarations(
198         stylist,
199         pseudo,
200         rule_node,
201         guards,
202         DeclarationIterator::new(rule_node, guards, pseudo),
203         parent_style,
204         layout_parent_style,
205         first_line_reparenting,
206         cascade_mode,
207         cascade_input_flags,
208         rule_cache,
209         rule_cache_conditions,
210         element,
211     )
214 /// Whether we're cascading for visited or unvisited styles.
215 #[derive(Clone, Copy)]
216 pub enum CascadeMode<'a, 'b> {
217     /// We're cascading for unvisited styles.
218     Unvisited {
219         /// The visited rules that should match the visited style.
220         visited_rules: Option<&'a StrongRuleNode>,
221     },
222     /// We're cascading for visited styles.
223     Visited {
224         /// The cascade for our unvisited style.
225         unvisited_context: &'a computed::Context<'b>,
226     },
229 fn iter_declarations<'builder, 'decls: 'builder>(
230     iter: impl Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
231     declarations: &mut Declarations<'decls>,
232     mut custom_builder: Option<&mut CustomPropertiesBuilder<'builder, 'decls>>,
233 ) {
234     for (declaration, priority) in iter {
235         if let PropertyDeclaration::Custom(ref declaration) = *declaration {
236             if let Some(ref mut builder) = custom_builder {
237                 builder.cascade(declaration, priority);
238             }
239         } else {
240             let id = declaration.id().as_longhand().unwrap();
241             declarations.note_declaration(declaration, priority, id);
242             if let Some(ref mut builder) = custom_builder {
243                 if let PropertyDeclaration::WithVariables(ref v) = declaration {
244                     builder.note_potentially_cyclic_non_custom_dependency(id, v);
245                 }
246             }
247         }
248     }
251 /// NOTE: This function expects the declaration with more priority to appear
252 /// first.
253 pub fn apply_declarations<'a, E, I>(
254     stylist: &'a Stylist,
255     pseudo: Option<&'a PseudoElement>,
256     rules: &StrongRuleNode,
257     guards: &StylesheetGuards,
258     iter: I,
259     parent_style: Option<&'a ComputedValues>,
260     layout_parent_style: Option<&ComputedValues>,
261     first_line_reparenting: FirstLineReparenting<'a>,
262     cascade_mode: CascadeMode,
263     cascade_input_flags: ComputedValueFlags,
264     rule_cache: Option<&'a RuleCache>,
265     rule_cache_conditions: &'a mut RuleCacheConditions,
266     element: Option<E>,
267 ) -> Arc<ComputedValues>
268 where
269     E: TElement + 'a,
270     I: Iterator<Item = (&'a PropertyDeclaration, CascadePriority)>,
272     debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
273     let device = stylist.device();
274     let inherited_style = parent_style.unwrap_or(device.default_computed_values());
275     let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
277     let container_size_query =
278         ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());
280     let mut context = computed::Context::new(
281         // We'd really like to own the rules here to avoid refcount traffic, but
282         // animation's usage of `apply_declarations` make this tricky. See bug
283         // 1375525.
284         StyleBuilder::new(
285             device,
286             Some(stylist),
287             parent_style,
288             pseudo,
289             Some(rules.clone()),
290             is_root_element,
291         ),
292         stylist.quirks_mode(),
293         rule_cache_conditions,
294         container_size_query,
295     );
297     context.style().add_flags(cascade_input_flags);
299     let using_cached_reset_properties;
300     let ignore_colors = !context.builder.device.use_document_colors();
301     let mut cascade = Cascade::new(first_line_reparenting, ignore_colors);
302     let mut declarations = Default::default();
303     let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
304     let properties_to_apply = match cascade_mode {
305         CascadeMode::Visited { unvisited_context } => {
306             context.builder.custom_properties = unvisited_context.builder.custom_properties.clone();
307             context.builder.writing_mode = unvisited_context.builder.writing_mode;
308             // We never insert visited styles into the cache so we don't need to try looking it up.
309             // It also wouldn't be super-profitable, only a handful :visited properties are
310             // non-inherited.
311             using_cached_reset_properties = false;
312             // TODO(bug 1859385): If we match the same rules when visited and unvisited, we could
313             // try to avoid gathering the declarations. That'd be:
314             //      unvisited_context.builder.rules.as_ref() == Some(rules)
315             iter_declarations(iter, &mut declarations, None);
317             LonghandIdSet::visited_dependent()
318         },
319         CascadeMode::Unvisited { visited_rules } => {
320             let deferred_custom_properties = {
321                 let mut builder = CustomPropertiesBuilder::new(stylist, &mut context);
322                 iter_declarations(iter, &mut declarations, Some(&mut builder));
323                 // Detect cycles, remove properties participating in them, and resolve properties, except:
324                 // * Registered custom properties that depend on font-relative properties (Resolved)
325                 //   when prioritary properties are resolved), and
326                 // * Any property that, in turn, depend on properties like above.
327                 builder.build(DeferFontRelativeCustomPropertyResolution::Yes)
328             };
330             // Resolve prioritary properties - Guaranteed to not fall into a cycle with existing custom
331             // properties.
332             cascade.apply_prioritary_properties(&mut context, &declarations, &mut shorthand_cache);
334             // Resolve the deferred custom properties.
335             if let Some(deferred) = deferred_custom_properties {
336                 CustomPropertiesBuilder::build_deferred(deferred, stylist, &mut context);
337             }
339             if let Some(visited_rules) = visited_rules {
340                 cascade.compute_visited_style_if_needed(
341                     &mut context,
342                     element,
343                     parent_style,
344                     layout_parent_style,
345                     visited_rules,
346                     guards,
347                 );
348             }
350             using_cached_reset_properties = cascade.try_to_use_cached_reset_properties(
351                 &mut context.builder,
352                 rule_cache,
353                 guards,
354             );
356             if using_cached_reset_properties {
357                 LonghandIdSet::late_group_only_inherited()
358             } else {
359                 LonghandIdSet::late_group()
360             }
361         },
362     };
364     cascade.apply_non_prioritary_properties(
365         &mut context,
366         &declarations.longhand_declarations,
367         &mut shorthand_cache,
368         &properties_to_apply,
369     );
371     cascade.finished_applying_properties(&mut context.builder);
373     std::mem::drop(cascade);
375     context.builder.clear_modified_reset();
377     if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
378         StyleAdjuster::new(&mut context.builder)
379             .adjust(layout_parent_style.unwrap_or(inherited_style), element);
380     }
382     if context.builder.modified_reset() || using_cached_reset_properties {
383         // If we adjusted any reset structs, we can't cache this ComputedValues.
384         //
385         // Also, if we re-used existing reset structs, don't bother caching it back again. (Aside
386         // from being wasted effort, it will be wrong, since context.rule_cache_conditions won't be
387         // set appropriately if we didn't compute those reset properties.)
388         context.rule_cache_conditions.borrow_mut().set_uncacheable();
389     }
391     context.builder.build()
394 /// For ignored colors mode, we sometimes want to do something equivalent to
395 /// "revert-or-initial", where we `revert` for a given origin, but then apply a
396 /// given initial value if nothing in other origins did override it.
398 /// This is a bit of a clunky way of achieving this.
399 type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
401 fn tweak_when_ignoring_colors(
402     context: &computed::Context,
403     longhand_id: LonghandId,
404     origin: Origin,
405     declaration: &mut Cow<PropertyDeclaration>,
406     declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
407 ) {
408     use crate::values::computed::ToComputedValue;
409     use crate::values::specified::Color;
411     if !longhand_id.ignored_when_document_colors_disabled() {
412         return;
413     }
415     let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
416     if is_ua_or_user_rule {
417         return;
418     }
420     // Always honor colors if forced-color-adjust is set to none.
421     let forced = context
422         .builder
423         .get_inherited_text()
424         .clone_forced_color_adjust();
425     if forced == computed::ForcedColorAdjust::None {
426         return;
427     }
429     // Don't override background-color on ::-moz-color-swatch. It is set as an
430     // author style (via the style attribute), but it's pretty important for it
431     // to show up for obvious reasons :)
432     if context
433         .builder
434         .pseudo
435         .map_or(false, |p| p.is_color_swatch()) &&
436         longhand_id == LonghandId::BackgroundColor
437     {
438         return;
439     }
441     fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
442         // We assume here currentColor is opaque.
443         color
444             .to_computed_value(context)
445             .resolve_to_absolute(&AbsoluteColor::BLACK)
446             .alpha
447     }
449     // A few special-cases ahead.
450     match **declaration {
451         // Honor CSS-wide keywords like unset / revert / initial...
452         PropertyDeclaration::CSSWideKeyword(..) => return,
453         PropertyDeclaration::BackgroundColor(ref color) => {
454             // We honor system colors and transparent colors unconditionally.
455             //
456             // NOTE(emilio): We honor transparent unconditionally, like we do
457             // for color, even though it causes issues like bug 1625036. The
458             // reasoning is that the conditions that trigger that (having
459             // mismatched widget and default backgrounds) are both uncommon, and
460             // broken in other applications as well, and not honoring
461             // transparent makes stuff uglier or break unconditionally
462             // (bug 1666059, bug 1755713).
463             if color.honored_in_forced_colors_mode(/* allow_transparent = */ true) {
464                 return;
465             }
466             // For background-color, we revert or initial-with-preserved-alpha
467             // otherwise, this is needed to preserve semi-transparent
468             // backgrounds.
469             let alpha = alpha_channel(color, context);
470             if alpha == 0.0 {
471                 return;
472             }
473             let mut color = context.builder.device.default_background_color();
474             color.alpha = alpha;
475             declarations_to_apply_unless_overridden
476                 .push(PropertyDeclaration::BackgroundColor(color.into()))
477         },
478         PropertyDeclaration::Color(ref color) => {
479             // We honor color: transparent and system colors.
480             if color
481                 .0
482                 .honored_in_forced_colors_mode(/* allow_transparent = */ true)
483             {
484                 return;
485             }
486             // If the inherited color would be transparent, but we would
487             // override this with a non-transparent color, then override it with
488             // the default color. Otherwise just let it inherit through.
489             if context
490                 .builder
491                 .get_parent_inherited_text()
492                 .clone_color()
493                 .alpha ==
494                 0.0
495             {
496                 let color = context.builder.device.default_color();
497                 declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
498                     specified::ColorPropertyValue(color.into()),
499                 ))
500             }
501         },
502         // We honor url background-images if backplating.
503         #[cfg(feature = "gecko")]
504         PropertyDeclaration::BackgroundImage(ref bkg) => {
505             use crate::values::generics::image::Image;
506             if static_prefs::pref!("browser.display.permit_backplate") {
507                 if bkg
508                     .0
509                     .iter()
510                     .all(|image| matches!(*image, Image::Url(..) | Image::None))
511                 {
512                     return;
513                 }
514             }
515         },
516         _ => {
517             // We honor system colors more generally for all colors.
518             //
519             // We used to honor transparent but that causes accessibility
520             // regressions like bug 1740924.
521             //
522             // NOTE(emilio): This doesn't handle caret-color and accent-color
523             // because those use a slightly different syntax (<color> | auto for
524             // example).
525             //
526             // That's probably fine though, as using a system color for
527             // caret-color doesn't make sense (using currentColor is fine), and
528             // we ignore accent-color in high-contrast-mode anyways.
529             if let Some(color) = declaration.color_value() {
530                 if color.honored_in_forced_colors_mode(/* allow_transparent = */ false) {
531                     return;
532                 }
533             }
534         },
535     }
537     *declaration.to_mut() =
538         PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
541 /// We track the index only for prioritary properties. For other properties we can just iterate.
542 type DeclarationIndex = u16;
544 /// "Prioritary" properties are properties that other properties depend on in one way or another.
546 /// We keep track of their position in the declaration vector, in order to be able to cascade them
547 /// separately in precise order.
548 #[derive(Copy, Clone)]
549 struct PrioritaryDeclarationPosition {
550     // DeclarationIndex::MAX signals no index.
551     most_important: DeclarationIndex,
552     least_important: DeclarationIndex,
555 impl Default for PrioritaryDeclarationPosition {
556     fn default() -> Self {
557         Self {
558             most_important: DeclarationIndex::MAX,
559             least_important: DeclarationIndex::MAX,
560         }
561     }
564 #[derive(Copy, Clone)]
565 struct Declaration<'a> {
566     decl: &'a PropertyDeclaration,
567     priority: CascadePriority,
568     next_index: DeclarationIndex,
571 /// The set of property declarations from our rules.
572 #[derive(Default)]
573 struct Declarations<'a> {
574     /// Whether we have any prioritary property. This is just a minor optimization.
575     has_prioritary_properties: bool,
576     /// A list of all the applicable longhand declarations.
577     longhand_declarations: SmallVec<[Declaration<'a>; 32]>,
578     /// The prioritary property position data.
579     prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
582 impl<'a> Declarations<'a> {
583     fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
584         let new_index = self.longhand_declarations.len();
585         if new_index >= DeclarationIndex::MAX as usize {
586             // This prioritary property is past the amount of declarations we can track. Let's give
587             // up applying it to prevent getting confused.
588             return;
589         }
591         self.has_prioritary_properties = true;
592         let new_index = new_index as DeclarationIndex;
593         let position = &mut self.prioritary_positions[id as usize];
594         if position.most_important == DeclarationIndex::MAX {
595             // We still haven't seen this property, record the current position as the most
596             // prioritary index.
597             position.most_important = new_index;
598         } else {
599             // Let the previous item in the list know about us.
600             self.longhand_declarations[position.least_important as usize].next_index = new_index;
601         }
602         position.least_important = new_index;
603     }
605     fn note_declaration(
606         &mut self,
607         decl: &'a PropertyDeclaration,
608         priority: CascadePriority,
609         id: LonghandId,
610     ) {
611         if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
612             self.note_prioritary_property(id);
613         }
614         self.longhand_declarations.push(Declaration {
615             decl,
616             priority,
617             next_index: 0,
618         });
619     }
622 struct Cascade<'b> {
623     first_line_reparenting: FirstLineReparenting<'b>,
624     ignore_colors: bool,
625     seen: LonghandIdSet,
626     author_specified: LonghandIdSet,
627     reverted_set: LonghandIdSet,
628     reverted: FxHashMap<LonghandId, (CascadePriority, bool)>,
629     declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
632 impl<'b> Cascade<'b> {
633     fn new(first_line_reparenting: FirstLineReparenting<'b>, ignore_colors: bool) -> Self {
634         Self {
635             first_line_reparenting,
636             ignore_colors,
637             seen: LonghandIdSet::default(),
638             author_specified: LonghandIdSet::default(),
639             reverted_set: Default::default(),
640             reverted: Default::default(),
641             declarations_to_apply_unless_overridden: Default::default(),
642         }
643     }
645     fn substitute_variables_if_needed<'cache, 'decl>(
646         &self,
647         context: &mut computed::Context,
648         shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
649         declaration: &'decl PropertyDeclaration,
650     ) -> Cow<'decl, PropertyDeclaration>
651     where
652         'cache: 'decl,
653     {
654         let declaration = match *declaration {
655             PropertyDeclaration::WithVariables(ref declaration) => declaration,
656             ref d => return Cow::Borrowed(d),
657         };
659         if !declaration.id.inherited() {
660             context.rule_cache_conditions.borrow_mut().set_uncacheable();
662             // NOTE(emilio): We only really need to add the `display` /
663             // `content` flag if the CSS variable has not been specified on our
664             // declarations, but we don't have that information at this point,
665             // and it doesn't seem like an important enough optimization to
666             // warrant it.
667             match declaration.id {
668                 LonghandId::Display => {
669                     context
670                         .builder
671                         .add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
672                 },
673                 LonghandId::Content => {
674                     context
675                         .builder
676                         .add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
677                 },
678                 _ => {},
679             }
680         }
682         debug_assert!(
683             context.builder.stylist.is_some(),
684             "Need a Stylist to substitute variables!"
685         );
686         declaration.value.substitute_variables(
687             declaration.id,
688             context.builder.custom_properties(),
689             context.builder.stylist.unwrap(),
690             context,
691             shorthand_cache,
692         )
693     }
695     fn apply_one_prioritary_property(
696         &mut self,
697         context: &mut computed::Context,
698         decls: &Declarations,
699         cache: &mut ShorthandsWithPropertyReferencesCache,
700         id: PrioritaryPropertyId,
701     ) -> bool {
702         let mut index = decls.prioritary_positions[id as usize].most_important;
703         if index == DeclarationIndex::MAX {
704             return false;
705         }
707         let longhand_id = id.to_longhand();
708         debug_assert!(
709             !longhand_id.is_logical(),
710             "That could require more book-keeping"
711         );
712         loop {
713             let decl = decls.longhand_declarations[index as usize];
714             self.apply_one_longhand(context, longhand_id, decl.decl, decl.priority, cache);
715             if self.seen.contains(longhand_id) {
716                 return true; // Common case, we're done.
717             }
718             debug_assert!(
719                 self.reverted_set.contains(longhand_id),
720                 "How else can we fail to apply a prioritary property?"
721             );
722             debug_assert!(
723                 decl.next_index == 0 || decl.next_index > index,
724                 "should make progress! {} -> {}",
725                 index,
726                 decl.next_index,
727             );
728             index = decl.next_index;
729             if index == 0 {
730                 break;
731             }
732         }
733         false
734     }
736     fn apply_prioritary_properties(
737         &mut self,
738         context: &mut computed::Context,
739         decls: &Declarations,
740         cache: &mut ShorthandsWithPropertyReferencesCache,
741     ) {
742         // Keeps apply_one_prioritary_property calls readable, considering the repititious
743         // arguments.
744         macro_rules! apply {
745             ($prop:ident) => {
746                 self.apply_one_prioritary_property(
747                     context,
748                     decls,
749                     cache,
750                     PrioritaryPropertyId::$prop,
751                 )
752             };
753         }
755         if !decls.has_prioritary_properties {
756             return;
757         }
759         let has_writing_mode = apply!(WritingMode) | apply!(Direction) | apply!(TextOrientation);
760         if has_writing_mode {
761             self.compute_writing_mode(context);
762         }
764         if apply!(Zoom) {
765             self.compute_zoom(context);
766         }
768         // Compute font-family.
769         let has_font_family = apply!(FontFamily);
770         let has_lang = apply!(XLang);
771         if has_lang {
772             self.recompute_initial_font_family_if_needed(&mut context.builder);
773         }
774         if has_font_family {
775             self.prioritize_user_fonts_if_needed(&mut context.builder);
776         }
778         // Compute font-size.
779         if apply!(XTextScale) {
780             self.unzoom_fonts_if_needed(&mut context.builder);
781         }
782         let has_font_size = apply!(FontSize);
783         let has_math_depth = apply!(MathDepth);
784         let has_min_font_size_ratio = apply!(MozMinFontSizeRatio);
786         if has_math_depth && has_font_size {
787             self.recompute_math_font_size_if_needed(context);
788         }
789         if has_lang || has_font_family {
790             self.recompute_keyword_font_size_if_needed(context);
791         }
792         if has_font_size || has_min_font_size_ratio || has_lang || has_font_family {
793             self.constrain_font_size_if_needed(&mut context.builder);
794         }
796         // Compute the rest of the first-available-font-affecting properties.
797         apply!(FontWeight);
798         apply!(FontStretch);
799         apply!(FontStyle);
800         apply!(FontSizeAdjust);
802         apply!(ColorScheme);
803         apply!(ForcedColorAdjust);
805         // Compute the line height.
806         apply!(LineHeight);
807     }
809     fn apply_non_prioritary_properties(
810         &mut self,
811         context: &mut computed::Context,
812         longhand_declarations: &[Declaration],
813         shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
814         properties_to_apply: &LonghandIdSet,
815     ) {
816         debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
817         debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
818         for declaration in &*longhand_declarations {
819             let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
820             if !properties_to_apply.contains(longhand_id) {
821                 continue;
822             }
823             debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
824             let is_logical = longhand_id.is_logical();
825             if is_logical {
826                 let wm = context.builder.writing_mode;
827                 context
828                     .rule_cache_conditions
829                     .borrow_mut()
830                     .set_writing_mode_dependency(wm);
831                 longhand_id = longhand_id.to_physical(wm);
832             }
833             self.apply_one_longhand(
834                 context,
835                 longhand_id,
836                 declaration.decl,
837                 declaration.priority,
838                 shorthand_cache,
839             );
840         }
841         if !self.declarations_to_apply_unless_overridden.is_empty() {
842             debug_assert!(self.ignore_colors);
843             for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
844                 let longhand_id = declaration.id().as_longhand().unwrap();
845                 debug_assert!(!longhand_id.is_logical());
846                 if !self.seen.contains(longhand_id) {
847                     unsafe {
848                         self.do_apply_declaration(context, longhand_id, &declaration);
849                     }
850                 }
851             }
852         }
853     }
855     fn apply_one_longhand(
856         &mut self,
857         context: &mut computed::Context,
858         longhand_id: LonghandId,
859         declaration: &PropertyDeclaration,
860         priority: CascadePriority,
861         cache: &mut ShorthandsWithPropertyReferencesCache,
862     ) {
863         debug_assert!(!longhand_id.is_logical());
864         let origin = priority.cascade_level().origin();
865         if self.seen.contains(longhand_id) {
866             return;
867         }
869         if self.reverted_set.contains(longhand_id) {
870             if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&longhand_id) {
871                 if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
872                     return;
873                 }
874             }
875         }
877         let mut declaration = self.substitute_variables_if_needed(context, cache, declaration);
879         // When document colors are disabled, do special handling of
880         // properties that are marked as ignored in that mode.
881         if self.ignore_colors {
882             tweak_when_ignoring_colors(
883                 context,
884                 longhand_id,
885                 origin,
886                 &mut declaration,
887                 &mut self.declarations_to_apply_unless_overridden,
888             );
889         }
891         let is_unset = match declaration.get_css_wide_keyword() {
892             Some(keyword) => match keyword {
893                 CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => {
894                     let origin_revert = keyword == CSSWideKeyword::Revert;
895                     // We intentionally don't want to insert it into `self.seen`, `reverted` takes
896                     // care of rejecting other declarations as needed.
897                     self.reverted_set.insert(longhand_id);
898                     self.reverted.insert(longhand_id, (priority, origin_revert));
899                     return;
900                 },
901                 CSSWideKeyword::Unset => true,
902                 CSSWideKeyword::Inherit => longhand_id.inherited(),
903                 CSSWideKeyword::Initial => !longhand_id.inherited(),
904             },
905             None => false,
906         };
908         self.seen.insert(longhand_id);
909         if origin == Origin::Author {
910             self.author_specified.insert(longhand_id);
911         }
913         if is_unset {
914             return;
915         }
917         unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
918     }
920     #[inline]
921     unsafe fn do_apply_declaration(
922         &self,
923         context: &mut computed::Context,
924         longhand_id: LonghandId,
925         declaration: &PropertyDeclaration,
926     ) {
927         debug_assert!(!longhand_id.is_logical());
928         // We could (and used to) use a pattern match here, but that bloats this
929         // function to over 100K of compiled code!
930         //
931         // To improve i-cache behavior, we outline the individual functions and
932         // use virtual dispatch instead.
933         (CASCADE_PROPERTY[longhand_id as usize])(&declaration, context);
934     }
936     fn compute_zoom(&self, context: &mut computed::Context) {
937         context.builder.effective_zoom = context
938             .builder
939             .inherited_effective_zoom()
940             .compute_effective(context.builder.specified_zoom());
941     }
943     fn compute_writing_mode(&self, context: &mut computed::Context) {
944         context.builder.writing_mode = WritingMode::new(context.builder.get_inherited_box())
945     }
947     fn compute_visited_style_if_needed<E>(
948         &self,
949         context: &mut computed::Context,
950         element: Option<E>,
951         parent_style: Option<&ComputedValues>,
952         layout_parent_style: Option<&ComputedValues>,
953         visited_rules: &StrongRuleNode,
954         guards: &StylesheetGuards,
955     ) where
956         E: TElement,
957     {
958         let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
960         macro_rules! visited_parent {
961             ($parent:expr) => {
962                 if is_link {
963                     $parent
964                 } else {
965                     $parent.map(|p| p.visited_style().unwrap_or(p))
966                 }
967             };
968         }
970         // We could call apply_declarations directly, but that'd cause
971         // another instantiation of this function which is not great.
972         let style = cascade_rules(
973             context.builder.stylist.unwrap(),
974             context.builder.pseudo,
975             visited_rules,
976             guards,
977             visited_parent!(parent_style),
978             visited_parent!(layout_parent_style),
979             self.first_line_reparenting,
980             CascadeMode::Visited {
981                 unvisited_context: &*context,
982             },
983             // Cascade input flags don't matter for the visited style, they are
984             // in the main (unvisited) style.
985             Default::default(),
986             // The rule cache doesn't care about caching :visited
987             // styles, we cache the unvisited style instead. We still do
988             // need to set the caching dependencies properly if present
989             // though, so the cache conditions need to match.
990             None, // rule_cache
991             &mut *context.rule_cache_conditions.borrow_mut(),
992             element,
993         );
994         context.builder.visited_style = Some(style);
995     }
997     fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
998         #[cfg(feature = "gecko")]
999         {
1000             if let Some(bg) = builder.get_background_if_mutated() {
1001                 bg.fill_arrays();
1002             }
1004             if let Some(svg) = builder.get_svg_if_mutated() {
1005                 svg.fill_arrays();
1006             }
1007         }
1009         if self
1010             .author_specified
1011             .contains_any(LonghandIdSet::border_background_properties())
1012         {
1013             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
1014         }
1016         if self.author_specified.contains(LonghandId::FontFamily) {
1017             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY);
1018         }
1020         if self.author_specified.contains(LonghandId::Color) {
1021             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);
1022         }
1024         if self.author_specified.contains(LonghandId::LetterSpacing) {
1025             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING);
1026         }
1028         if self.author_specified.contains(LonghandId::WordSpacing) {
1029             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING);
1030         }
1032         if self
1033             .author_specified
1034             .contains(LonghandId::FontSynthesisWeight)
1035         {
1036             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT);
1037         }
1039         if self
1040             .author_specified
1041             .contains(LonghandId::FontSynthesisStyle)
1042         {
1043             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE);
1044         }
1046         #[cfg(feature = "servo")]
1047         {
1048             if let Some(font) = builder.get_font_if_mutated() {
1049                 font.compute_font_hash();
1050             }
1051         }
1052     }
1054     fn try_to_use_cached_reset_properties(
1055         &self,
1056         builder: &mut StyleBuilder<'b>,
1057         cache: Option<&'b RuleCache>,
1058         guards: &StylesheetGuards,
1059     ) -> bool {
1060         let style = match self.first_line_reparenting {
1061             FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
1062             FirstLineReparenting::No => {
1063                 let Some(cache) = cache else { return false };
1064                 let Some(style) = cache.find(guards, builder) else {
1065                     return false;
1066                 };
1067                 style
1068             },
1069         };
1071         builder.copy_reset_from(style);
1073         // We're using the same reset style as another element, and we'll skip
1074         // applying the relevant properties. So we need to do the relevant
1075         // bookkeeping here to keep these bits correct.
1076         //
1077         // Note that the border/background properties are non-inherited, so we
1078         // don't need to do anything else other than just copying the bits over.
1079         //
1080         // When using this optimization, we also need to copy whether the old
1081         // style specified viewport units / used font-relative lengths, this one
1082         // would as well.  It matches the same rules, so it is the right thing
1083         // to do anyways, even if it's only used on inherited properties.
1084         let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND |
1085             ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS |
1086             ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS |
1087             ComputedValueFlags::USES_CONTAINER_UNITS |
1088             ComputedValueFlags::USES_VIEWPORT_UNITS;
1089         builder.add_flags(style.flags & bits_to_copy);
1091         true
1092     }
1094     /// The initial font depends on the current lang group so we may need to
1095     /// recompute it if the language changed.
1096     #[inline]
1097     #[cfg(feature = "gecko")]
1098     fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
1099         use crate::gecko_bindings::bindings;
1100         use crate::values::computed::font::FontFamily;
1102         let default_font_type = {
1103             let font = builder.get_font();
1105             if !font.mFont.family.is_initial {
1106                 return;
1107             }
1109             let default_font_type = unsafe {
1110                 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1111                     builder.device.document(),
1112                     font.mLanguage.mRawPtr,
1113                 )
1114             };
1116             let initial_generic = font.mFont.family.families.single_generic();
1117             debug_assert!(
1118                 initial_generic.is_some(),
1119                 "Initial font should be just one generic font"
1120             );
1121             if initial_generic == Some(default_font_type) {
1122                 return;
1123             }
1125             default_font_type
1126         };
1128         // NOTE: Leaves is_initial untouched.
1129         builder.mutate_font().mFont.family.families =
1130             FontFamily::generic(default_font_type).families.clone();
1131     }
1133     /// Prioritize user fonts if needed by pref.
1134     #[inline]
1135     #[cfg(feature = "gecko")]
1136     fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1137         use crate::gecko_bindings::bindings;
1139         // Check the use_document_fonts setting for content, but for chrome
1140         // documents they're treated as always enabled.
1141         if static_prefs::pref!("browser.display.use_document_fonts") != 0 ||
1142             builder.device.chrome_rules_enabled_for_document()
1143         {
1144             return;
1145         }
1147         let default_font_type = {
1148             let font = builder.get_font();
1150             if font.mFont.family.is_system_font {
1151                 return;
1152             }
1154             if !font.mFont.family.families.needs_user_font_prioritization() {
1155                 return;
1156             }
1158             unsafe {
1159                 bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
1160                     builder.device.document(),
1161                     font.mLanguage.mRawPtr,
1162                 )
1163             }
1164         };
1166         let font = builder.mutate_font();
1167         font.mFont
1168             .family
1169             .families
1170             .prioritize_first_generic_or_prepend(default_font_type);
1171     }
1173     /// Some keyword sizes depend on the font family and language.
1174     #[cfg(feature = "gecko")]
1175     fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
1176         use crate::values::computed::ToComputedValue;
1178         if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
1179             return;
1180         }
1182         let new_size = {
1183             let font = context.builder.get_font();
1184             let info = font.clone_font_size().keyword_info;
1185             let new_size = match info.kw {
1186                 specified::FontSizeKeyword::None => return,
1187                 _ => {
1188                     context.for_non_inherited_property = false;
1189                     specified::FontSize::Keyword(info).to_computed_value(context)
1190                 },
1191             };
1193             if font.mScriptUnconstrainedSize == new_size.computed_size {
1194                 return;
1195             }
1197             new_size
1198         };
1200         context.builder.mutate_font().set_font_size(new_size);
1201     }
1203     /// Some properties, plus setting font-size itself, may make us go out of
1204     /// our minimum font-size range.
1205     #[cfg(feature = "gecko")]
1206     fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
1207         use crate::gecko_bindings::bindings;
1208         use crate::values::generics::NonNegative;
1210         let min_font_size = {
1211             let font = builder.get_font();
1212             let min_font_size = unsafe {
1213                 bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
1214             };
1216             if font.mFont.size.0 >= min_font_size {
1217                 return;
1218             }
1220             NonNegative(min_font_size)
1221         };
1223         builder.mutate_font().mFont.size = min_font_size;
1224     }
1226     /// <svg:text> is not affected by text zoom, and it uses a preshint to disable it. We fix up
1227     /// the struct when this happens by unzooming its contained font values, which will have been
1228     /// zoomed in the parent.
1229     ///
1230     /// FIXME(emilio): Why doing this _before_ handling font-size? That sounds wrong.
1231     #[cfg(feature = "gecko")]
1232     fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
1233         debug_assert!(self.seen.contains(LonghandId::XTextScale));
1235         let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
1236         let text_scale = builder.get_font().clone__x_text_scale();
1237         if parent_text_scale == text_scale {
1238             return;
1239         }
1240         debug_assert_ne!(
1241             parent_text_scale.text_zoom_enabled(),
1242             text_scale.text_zoom_enabled(),
1243             "There's only one value that disables it"
1244         );
1245         debug_assert!(
1246             !text_scale.text_zoom_enabled(),
1247             "We only ever disable text zoom (in svg:text), never enable it"
1248         );
1249         let device = builder.device;
1250         builder.mutate_font().unzoom_fonts(device);
1251     }
1253     /// Special handling of font-size: math (used for MathML).
1254     /// https://w3c.github.io/mathml-core/#the-math-script-level-property
1255     /// TODO: Bug: 1548471: MathML Core also does not specify a script min size
1256     /// should we unship that feature or standardize it?
1257     #[cfg(feature = "gecko")]
1258     fn recompute_math_font_size_if_needed(&self, context: &mut computed::Context) {
1259         use crate::values::generics::NonNegative;
1261         // Do not do anything if font-size: math or math-depth is not set.
1262         if context.builder.get_font().clone_font_size().keyword_info.kw !=
1263             specified::FontSizeKeyword::Math
1264         {
1265             return;
1266         }
1268         const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
1270         // Helper function that calculates the scale factor applied to font-size
1271         // when math-depth goes from parent_math_depth to computed_math_depth.
1272         // This function is essentially a modification of the MathML3's formula
1273         // 0.71^(parent_math_depth - computed_math_depth) so that a scale factor
1274         // of parent_script_percent_scale_down is applied when math-depth goes
1275         // from 0 to 1 and parent_script_script_percent_scale_down is applied
1276         // when math-depth goes from 0 to 2. This is also a straightforward
1277         // implementation of the specification's algorithm:
1278         // https://w3c.github.io/mathml-core/#the-math-script-level-property
1279         fn scale_factor_for_math_depth_change(
1280             parent_math_depth: i32,
1281             computed_math_depth: i32,
1282             parent_script_percent_scale_down: Option<f32>,
1283             parent_script_script_percent_scale_down: Option<f32>,
1284         ) -> f32 {
1285             let mut a = parent_math_depth;
1286             let mut b = computed_math_depth;
1287             let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
1288             let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c);
1289             let scale_between_0_and_2 =
1290                 parent_script_script_percent_scale_down.unwrap_or_else(|| c * c);
1291             let mut s = 1.0;
1292             let mut invert_scale_factor = false;
1293             if a == b {
1294                 return s;
1295             }
1296             if b < a {
1297                 mem::swap(&mut a, &mut b);
1298                 invert_scale_factor = true;
1299             }
1300             let mut e = b - a;
1301             if a <= 0 && b >= 2 {
1302                 s *= scale_between_0_and_2;
1303                 e -= 2;
1304             } else if a == 1 {
1305                 s *= scale_between_0_and_2 / scale_between_0_and_1;
1306                 e -= 1;
1307             } else if b == 1 {
1308                 s *= scale_between_0_and_1;
1309                 e -= 1;
1310             }
1311             s *= (c as f32).powi(e);
1312             if invert_scale_factor {
1313                 1.0 / s.max(f32::MIN_POSITIVE)
1314             } else {
1315                 s
1316             }
1317         }
1319         let (new_size, new_unconstrained_size) = {
1320             let builder = &context.builder;
1321             let font = builder.get_font();
1322             let parent_font = builder.get_parent_font();
1324             let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1326             if delta == 0 {
1327                 return;
1328             }
1330             let mut min = parent_font.mScriptMinSize;
1331             if font.mXTextScale.text_zoom_enabled() {
1332                 min = builder.device.zoom_text(min);
1333             }
1335             // Calculate scale factor following MathML Core's algorithm.
1336             let scale = {
1337                 // Script scale factors are independent of orientation.
1338                 let font_metrics = context.query_font_metrics(
1339                     FontBaseSize::InheritedStyle,
1340                     FontMetricsOrientation::Horizontal,
1341                     /* retrieve_math_scales = */ true,
1342                 );
1343                 scale_factor_for_math_depth_change(
1344                     parent_font.mMathDepth as i32,
1345                     font.mMathDepth as i32,
1346                     font_metrics.script_percent_scale_down,
1347                     font_metrics.script_script_percent_scale_down,
1348                 )
1349             };
1351             let parent_size = parent_font.mSize.0;
1352             let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1353             let new_size = parent_size.scale_by(scale);
1354             let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1356             if scale <= 1. {
1357                 // The parent size can be smaller than scriptminsize, e.g. if it
1358                 // was specified explicitly. Don't scale in this case, but we
1359                 // don't want to set it to scriptminsize either since that will
1360                 // make it larger.
1361                 if parent_size <= min {
1362                     (parent_size, new_unconstrained_size)
1363                 } else {
1364                     (min.max(new_size), new_unconstrained_size)
1365                 }
1366             } else {
1367                 // If the new unconstrained size is larger than the min size,
1368                 // this means we have escaped the grasp of scriptminsize and can
1369                 // revert to using the unconstrained size.
1370                 // However, if the new size is even larger (perhaps due to usage
1371                 // of em units), use that instead.
1372                 (
1373                     new_size.min(new_unconstrained_size.max(min)),
1374                     new_unconstrained_size,
1375                 )
1376             }
1377         };
1378         let font = context.builder.mutate_font();
1379         font.mFont.size = NonNegative(new_size);
1380         font.mSize = NonNegative(new_size);
1381         font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1382     }