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/. */
4 
5 //! The main cascading algorithm of the style system.
6 
7 use crate::context::QuirksMode;
8 use crate::custom_properties::CustomPropertiesBuilder;
9 use crate::dom::TElement;
10 use crate::font_metrics::FontMetricsProvider;
11 use crate::logical_geometry::WritingMode;
12 use crate::media_queries::Device;
13 use crate::properties::{
14     CSSWideKeyword, ComputedValueFlags, ComputedValues, DeclarationImportanceIterator, Importance,
15     LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationId, PropertyFlags,
16     ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
17 };
18 use crate::rule_cache::{RuleCache, RuleCacheConditions};
19 use crate::rule_tree::StrongRuleNode;
20 use crate::selector_parser::PseudoElement;
21 use crate::shared_lock::StylesheetGuards;
22 use crate::style_adjuster::StyleAdjuster;
23 use crate::stylesheets::{Origin, PerOrigin};
24 use crate::values::{computed, specified};
25 use servo_arc::Arc;
26 use smallvec::SmallVec;
27 use std::borrow::Cow;
28 use std::cell::RefCell;
29 
30 /// We split the cascade in two phases: 'early' properties, and 'late'
31 /// properties.
32 ///
33 /// Early properties are the ones that don't have dependencies _and_ other
34 /// properties depend on, for example, writing-mode related properties, color
35 /// (for currentColor), or font-size (for em, etc).
36 ///
37 /// Late properties are all the others.
38 trait CascadePhase {
39     fn is_early() -> bool;
40 }
41 
42 struct EarlyProperties;
43 impl CascadePhase for EarlyProperties {
44     fn is_early() -> bool {
45         true
46     }
47 }
48 
49 struct LateProperties;
50 impl CascadePhase for LateProperties {
main()51     fn is_early() -> bool {
52         false
53     }
54 }
55 
56 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
57 enum ApplyResetProperties {
58     No,
59     Yes,
60 }
61 
62 /// Performs the CSS cascade, computing new styles for an element from its parent style.
63 ///
64 /// The arguments are:
65 ///
66 ///   * `device`: Used to get the initial viewport and other external state.
67 ///
68 ///   * `rule_node`: The rule node in the tree that represent the CSS rules that
69 ///   matched.
70 ///
71 ///   * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
72 ///
73 /// Returns the computed values.
74 ///   * `flags`: Various flags.
75 ///
76 pub fn cascade<E>(
77     device: &Device,
78     pseudo: Option<&PseudoElement>,
79     rule_node: &StrongRuleNode,
80     guards: &StylesheetGuards,
81     parent_style: Option<&ComputedValues>,
82     parent_style_ignoring_first_line: Option<&ComputedValues>,
83     layout_parent_style: Option<&ComputedValues>,
84     visited_rules: Option<&StrongRuleNode>,
85     font_metrics_provider: &dyn FontMetricsProvider,
86     quirks_mode: QuirksMode,
87     rule_cache: Option<&RuleCache>,
88     rule_cache_conditions: &mut RuleCacheConditions,
89     element: Option<E>,
90 ) -> Arc<ComputedValues>
91 where
92     E: TElement,
93 {
94     cascade_rules(
95         device,
96         pseudo,
97         rule_node,
98         guards,
99         parent_style,
100         parent_style_ignoring_first_line,
101         layout_parent_style,
102         font_metrics_provider,
103         CascadeMode::Unvisited { visited_rules },
104         quirks_mode,
105         rule_cache,
106         rule_cache_conditions,
107         element,
108     )
109 }
110 
111 struct DeclarationIterator<'a> {
112     // Global to the iteration.
113     guards: &'a StylesheetGuards<'a>,
114     restriction: Option<PropertyFlags>,
115     // The rule we're iterating over.
116     current_rule_node: Option<&'a StrongRuleNode>,
117     // Per rule state.
118     declarations: DeclarationImportanceIterator<'a>,
119     origin: Origin,
120     importance: Importance,
121 }
122 
123 impl<'a> DeclarationIterator<'a> {
124     #[inline]
125     fn new(
126         rule_node: &'a StrongRuleNode,
127         guards: &'a StylesheetGuards,
128         pseudo: Option<&PseudoElement>,
129     ) -> Self {
130         let restriction = pseudo.and_then(|p| p.property_restriction());
131         let mut iter = Self {
132             guards,
133             current_rule_node: Some(rule_node),
134             origin: Origin::Author,
135             importance: Importance::Normal,
136             declarations: DeclarationImportanceIterator::default(),
137             restriction,
abort(message)138         };
139         iter.update_for_node(rule_node);
140         iter
141     }
142 
render(filename, **context)143     fn update_for_node(&mut self, node: &'a StrongRuleNode) {
144         let origin = node.cascade_level().origin();
145         self.origin = origin;
146         self.importance = node.importance();
147         let guard = match origin {
148             Origin::Author => self.guards.author,
149             Origin::User | Origin::UserAgent => self.guards.ua_or_user,
150         };
151         self.declarations = match node.style_source() {
152             Some(source) => source.read(guard).declaration_importance_iter(),
153             None => DeclarationImportanceIterator::default(),
154         };
155     }
156 }
157 
158 impl<'a> Iterator for DeclarationIterator<'a> {
159     type Item = (&'a PropertyDeclaration, Origin);
160 
161     #[inline]
162     fn next(&mut self) -> Option<Self::Item> {
163         loop {
write(directory, filename, content)164             if let Some((decl, importance)) = self.declarations.next_back() {
165                 if self.importance != importance {
166                     continue;
167                 }
168 
169                 let origin = self.origin;
170                 if let Some(restriction) = self.restriction {
171                     // decl.id() is either a longhand or a custom
172                     // property.  Custom properties are always allowed, but
173                     // longhands are only allowed if they have our
174                     // restriction flag set.
175                     if let PropertyDeclarationId::Longhand(id) = decl.id() {
176                         if !id.flags().contains(restriction) && origin != Origin::UserAgent {
177                             continue;
178                         }
179                     }
180                 }
181 
182                 return Some((decl, origin));
183             }
184 
185             let next_node = self.current_rule_node.take()?.parent()?;
186             self.current_rule_node = Some(next_node);
187             self.update_for_node(next_node);
188         }
189     }
190 }
191 
192 fn cascade_rules<E>(
193     device: &Device,
194     pseudo: Option<&PseudoElement>,
195     rule_node: &StrongRuleNode,
196     guards: &StylesheetGuards,
197     parent_style: Option<&ComputedValues>,
198     parent_style_ignoring_first_line: Option<&ComputedValues>,
199     layout_parent_style: Option<&ComputedValues>,
200     font_metrics_provider: &dyn FontMetricsProvider,
201     cascade_mode: CascadeMode,
202     quirks_mode: QuirksMode,
203     rule_cache: Option<&RuleCache>,
204     rule_cache_conditions: &mut RuleCacheConditions,
205     element: Option<E>,
206 ) -> Arc<ComputedValues>
207 where
208     E: TElement,
209 {
210     debug_assert_eq!(
211         parent_style.is_some(),
212         parent_style_ignoring_first_line.is_some()
213     );
214     apply_declarations(
215         device,
216         pseudo,
217         rule_node,
218         guards,
219         DeclarationIterator::new(rule_node, guards, pseudo),
220         parent_style,
221         parent_style_ignoring_first_line,
222         layout_parent_style,
223         font_metrics_provider,
224         cascade_mode,
225         quirks_mode,
226         rule_cache,
227         rule_cache_conditions,
228         element,
229     )
230 }
231 
232 /// Whether we're cascading for visited or unvisited styles.
233 #[derive(Clone, Copy)]
234 pub enum CascadeMode<'a> {
235     /// We're cascading for unvisited styles.
236     Unvisited {
237         /// The visited rules that should match the visited style.
238         visited_rules: Option<&'a StrongRuleNode>,
239     },
240     /// We're cascading for visited styles.
241     Visited {
242         /// The writing mode of our unvisited style, needed to correctly resolve
243         /// logical properties..
244         writing_mode: WritingMode,
245     },
246 }
247 
248 /// NOTE: This function expects the declaration with more priority to appear
249 /// first.
250 pub fn apply_declarations<'a, E, I>(
251     device: &Device,
252     pseudo: Option<&PseudoElement>,
253     rules: &StrongRuleNode,
254     guards: &StylesheetGuards,
255     iter: I,
256     parent_style: Option<&ComputedValues>,
257     parent_style_ignoring_first_line: Option<&ComputedValues>,
258     layout_parent_style: Option<&ComputedValues>,
259     font_metrics_provider: &dyn FontMetricsProvider,
260     cascade_mode: CascadeMode,
261     quirks_mode: QuirksMode,
262     rule_cache: Option<&RuleCache>,
263     rule_cache_conditions: &mut RuleCacheConditions,
264     element: Option<E>,
265 ) -> Arc<ComputedValues>
266 where
267     E: TElement,
268     I: Iterator<Item = (&'a PropertyDeclaration, Origin)>,
269 {
270     debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
271     debug_assert_eq!(
272         parent_style.is_some(),
273         parent_style_ignoring_first_line.is_some()
274     );
275     #[cfg(feature = "gecko")]
276     debug_assert!(
277         parent_style.is_none() ||
278             ::std::ptr::eq(
279                 parent_style.unwrap(),
280                 parent_style_ignoring_first_line.unwrap()
281             ) ||
282             parent_style.unwrap().is_first_line_style()
283     );
284 
285     let inherited_style = parent_style.unwrap_or(device.default_computed_values());
286 
287     let mut declarations = SmallVec::<[(&_, Origin); 32]>::new();
288     let custom_properties = {
289         let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties(), device);
290 
291         for (declaration, origin) in iter {
292             declarations.push((declaration, origin));
293             if let PropertyDeclaration::Custom(ref declaration) = *declaration {
294                 builder.cascade(declaration, origin);
295             }
296         }
297 
298         builder.build()
299     };
300 
301     let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
302 
303     let mut context = computed::Context {
304         // We'd really like to own the rules here to avoid refcount traffic, but
305         // animation's usage of `apply_declarations` make this tricky. See bug
306         // 1375525.
307         builder: StyleBuilder::new(
308             device,
309             parent_style,
310             parent_style_ignoring_first_line,
311             pseudo,
312             Some(rules.clone()),
313             custom_properties,
314             is_root_element,
315         ),
316         cached_system_font: None,
317         in_media_query: false,
318         for_smil_animation: false,
319         for_non_inherited_property: None,
320         font_metrics_provider,
321         quirks_mode,
322         rule_cache_conditions: RefCell::new(rule_cache_conditions),
323     };
324 
325     let using_cached_reset_properties = {
326         let mut cascade = Cascade::new(&mut context, cascade_mode);
327         let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
328 
329         cascade.apply_properties::<EarlyProperties, _>(
330             ApplyResetProperties::Yes,
331             declarations.iter().cloned(),
332             &mut shorthand_cache,
333         );
334 
335         cascade.compute_visited_style_if_needed(
336             element,
337             parent_style,
338             parent_style_ignoring_first_line,
339             layout_parent_style,
340             guards,
341         );
342 
343         let using_cached_reset_properties =
344             cascade.try_to_use_cached_reset_properties(rule_cache, guards);
345 
346         let apply_reset = if using_cached_reset_properties {
347             ApplyResetProperties::No
348         } else {
349             ApplyResetProperties::Yes
350         };
351 
352         cascade.apply_properties::<LateProperties, _>(
353             apply_reset,
354             declarations.iter().cloned(),
355             &mut shorthand_cache,
356         );
357 
358         using_cached_reset_properties
359     };
360 
361     context.builder.clear_modified_reset();
362 
363     if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
364         StyleAdjuster::new(&mut context.builder)
365             .adjust(layout_parent_style.unwrap_or(inherited_style), element);
366     }
367 
368     if context.builder.modified_reset() || using_cached_reset_properties {
369         // If we adjusted any reset structs, we can't cache this ComputedValues.
370         //
371         // Also, if we re-used existing reset structs, don't bother caching it
372         // back again. (Aside from being wasted effort, it will be wrong, since
373         // context.rule_cache_conditions won't be set appropriately if we didn't
374         // compute those reset properties.)
375         context.rule_cache_conditions.borrow_mut().set_uncacheable();
376     }
377 
378     context.builder.build()
379 }
380 
381 /// For ignored colors mode, we sometimes want to do something equivalent to
382 /// "revert-or-initial", where we `revert` for a given origin, but then apply a
383 /// given initial value if nothing in other origins did override it.
384 ///
385 /// This is a bit of a clunky way of achieving this.
386 type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
387 
388 fn tweak_when_ignoring_colors(
389     context: &computed::Context,
390     longhand_id: LonghandId,
391     origin: Origin,
392     declaration: &mut Cow<PropertyDeclaration>,
393     declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden,
394 ) {
395     use crate::values::specified::Color;
396     use crate::values::computed::ToComputedValue;
397     use cssparser::RGBA;
398 
399     if !longhand_id.ignored_when_document_colors_disabled() {
400         return;
401     }
402 
403     let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
404     if is_ua_or_user_rule {
405         return;
406     }
407 
408     // Don't override background-color on ::-moz-color-swatch. It is set as an
409     // author style (via the style attribute), but it's pretty important for it
410     // to show up for obvious reasons :)
411     if context.builder.pseudo.map_or(false, |p| p.is_color_swatch()) &&
412         longhand_id == LonghandId::BackgroundColor
413     {
414         return;
415     }
416 
417     fn alpha_channel(color: &Color, context: &computed::Context) -> u8 {
418         // We assume here currentColor is opaque.
419         let color = color.to_computed_value(context).to_rgba(RGBA::new(0, 0, 0, 255));
420         color.alpha
421     }
422 
423     // A few special-cases ahead.
424     match **declaration {
425         PropertyDeclaration::BackgroundColor(ref color) => {
426             // We honor system colors.
427             if color.is_system() {
428                 return;
429             }
430             // For background-color, we revert or initial-with-preserved-alpha
431             // otherwise, this is needed to preserve semi-transparent
432             // backgrounds.
433             //
434             // FIXME(emilio, bug 1666059): We revert for alpha == 0, but maybe
435             // should consider not doing that even if it causes some issues like
436             // bug 1625036, or finding a performant way to preserve the original
437             // widget background color's rgb channels but not alpha...
438             let alpha = alpha_channel(color, context);
439             if alpha != 0 {
440                 let mut color = context.builder.device.default_background_color();
441                 color.alpha = alpha;
442                 declarations_to_apply_unless_overriden
443                     .push(PropertyDeclaration::BackgroundColor(color.into()))
444             }
445         },
446         PropertyDeclaration::Color(ref color) => {
447             // We honor color: transparent and system colors.
448             if color.0.is_system() {
449                 return;
450             }
451             if alpha_channel(&color.0, context) == 0 {
452                 return;
453             }
454             // If the inherited color would be transparent, but we would
455             // override this with a non-transparent color, then override it with
456             // the default color. Otherwise just let it inherit through.
457             if context.builder.get_parent_inherited_text().clone_color().alpha == 0 {
458                 let color = context.builder.device.default_color();
459                 declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
460                     specified::ColorPropertyValue(color.into()),
461                 ))
462             }
463         },
464         // We honor url background-images if backplating.
465         #[cfg(feature = "gecko")]
466         PropertyDeclaration::BackgroundImage(ref bkg) => {
467             use crate::values::generics::image::Image;
468             if static_prefs::pref!("browser.display.permit_backplate") {
469                 if bkg.0.iter().all(|image| matches!(*image, Image::Url(..))) {
470                     return;
471                 }
472             }
473         },
474         _ => {},
475     }
476 
477     *declaration.to_mut() =
478         PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
479 }
480 
481 struct Cascade<'a, 'b: 'a> {
482     context: &'a mut computed::Context<'b>,
483     cascade_mode: CascadeMode<'a>,
484     seen: LonghandIdSet,
485     author_specified: LonghandIdSet,
486     reverted: PerOrigin<LonghandIdSet>,
487 }
488 
489 impl<'a, 'b: 'a> Cascade<'a, 'b> {
490     fn new(context: &'a mut computed::Context<'b>, cascade_mode: CascadeMode<'a>) -> Self {
491         Self {
492             context,
493             cascade_mode,
494             seen: LonghandIdSet::default(),
495             author_specified: LonghandIdSet::default(),
496             reverted: Default::default(),
497         }
498     }
499 
500     fn substitute_variables_if_needed<'decl, 'cache>(
501         &mut self,
502         declaration: &'decl PropertyDeclaration,
503         cache: &'cache mut ShorthandsWithPropertyReferencesCache,
504     ) -> Cow<'decl, PropertyDeclaration>
505     where
506         'cache: 'decl,
507     {
508         let declaration = match *declaration {
509             PropertyDeclaration::WithVariables(ref declaration) => declaration,
510             ref d => return Cow::Borrowed(d),
511         };
512 
513         if !declaration.id.inherited() {
514             self.context
515                 .rule_cache_conditions
516                 .borrow_mut()
517                 .set_uncacheable();
518 
519             // NOTE(emilio): We only really need to add the `display` /
520             // `content` flag if the CSS variable has not been specified on our
521             // declarations, but we don't have that information at this point,
522             // and it doesn't seem like an important enough optimization to
523             // warrant it.
524             match declaration.id {
525                 LonghandId::Display => {
526                     self.context
527                         .builder
528                         .add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
529                 },
530                 LonghandId::Content => {
531                     self.context
532                         .builder
533                         .add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
534                 },
535                 _ => {},
536             }
537         }
538 
539         declaration.value.substitute_variables(
540             declaration.id,
541             self.context.builder.writing_mode,
542             self.context.builder.custom_properties.as_ref(),
543             self.context.quirks_mode,
544             self.context.device(),
545             cache,
546         )
547     }
548 
549     #[inline(always)]
550     fn apply_declaration(&mut self, longhand_id: LonghandId, declaration: &PropertyDeclaration) {
551         // We could (and used to) use a pattern match here, but that bloats this
552         // function to over 100K of compiled code!
553         //
554         // To improve i-cache behavior, we outline the individual functions and
555         // use virtual dispatch instead.
556         let discriminant = longhand_id as usize;
557         (CASCADE_PROPERTY[discriminant])(declaration, &mut self.context);
558     }
559 
560     fn apply_properties<'decls, Phase, I>(
561         &mut self,
562         apply_reset: ApplyResetProperties,
563         declarations: I,
564         mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
565     ) where
566         Phase: CascadePhase,
567         I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>,
568     {
569         let apply_reset = apply_reset == ApplyResetProperties::Yes;
570 
571         debug_assert!(
572             !Phase::is_early() || apply_reset,
573             "Should always apply reset properties in the early phase, since we \
574              need to know font-size / writing-mode to decide whether to use the \
575              cached reset properties"
576         );
577 
578         let ignore_colors = !self.context.builder.device.use_document_colors();
579         let mut declarations_to_apply_unless_overriden = DeclarationsToApplyUnlessOverriden::new();
580 
581         for (declaration, origin) in declarations {
582             let declaration_id = declaration.id();
583             let longhand_id = match declaration_id {
584                 PropertyDeclarationId::Longhand(id) => id,
585                 PropertyDeclarationId::Custom(..) => continue,
586             };
587 
588             let inherited = longhand_id.inherited();
589             if !apply_reset && !inherited {
590                 continue;
591             }
592 
593             if Phase::is_early() != longhand_id.is_early_property() {
594                 continue;
595             }
596 
597             debug_assert!(!Phase::is_early() || !longhand_id.is_logical());
598             let physical_longhand_id = if Phase::is_early() {
599                 longhand_id
600             } else {
601                 longhand_id.to_physical(self.context.builder.writing_mode)
602             };
603 
604             if self.seen.contains(physical_longhand_id) {
605                 continue;
606             }
607 
608             if self
609                 .reverted
610                 .borrow_for_origin(&origin)
611                 .contains(physical_longhand_id)
612             {
613                 continue;
614             }
615 
616             // Only a few properties are allowed to depend on the visited state
617             // of links.  When cascading visited styles, we can save time by
618             // only processing these properties.
619             if matches!(self.cascade_mode, CascadeMode::Visited { .. }) &&
620                 !physical_longhand_id.is_visited_dependent()
621             {
622                 continue;
623             }
624 
625             let mut declaration =
626                 self.substitute_variables_if_needed(declaration, &mut shorthand_cache);
627 
628             // When document colors are disabled, do special handling of
629             // properties that are marked as ignored in that mode.
630             if ignore_colors {
631                 tweak_when_ignoring_colors(
632                     &self.context,
633                     longhand_id,
634                     origin,
635                     &mut declaration,
636                     &mut declarations_to_apply_unless_overriden,
637                 );
638                 debug_assert_eq!(
639                     declaration.id(),
640                     PropertyDeclarationId::Longhand(longhand_id),
641                     "Shouldn't change the declaration id!",
642                 );
643             }
644 
645             let css_wide_keyword = declaration.get_css_wide_keyword();
646             if let Some(CSSWideKeyword::Revert) = css_wide_keyword {
647                 // We intentionally don't want to insert it into `self.seen`,
648                 // `reverted` takes care of rejecting other declarations as
649                 // needed.
650                 for origin in origin.following_including() {
651                     self.reverted
652                         .borrow_mut_for_origin(&origin)
653                         .insert(physical_longhand_id);
654                 }
655                 continue;
656             }
657 
658             self.seen.insert(physical_longhand_id);
659             if origin == Origin::Author {
660                 self.author_specified.insert(physical_longhand_id);
661             }
662 
663             let unset = css_wide_keyword.map_or(false, |css_wide_keyword| match css_wide_keyword {
664                 CSSWideKeyword::Unset => true,
665                 CSSWideKeyword::Inherit => inherited,
666                 CSSWideKeyword::Initial => !inherited,
667                 CSSWideKeyword::Revert => unreachable!(),
668             });
669 
670             if unset {
671                 continue;
672             }
673 
674             // FIXME(emilio): We should avoid generating code for logical
675             // longhands and just use the physical ones, then rename
676             // physical_longhand_id to just longhand_id.
677             self.apply_declaration(longhand_id, &*declaration);
678         }
679 
680         if ignore_colors {
681             for declaration in declarations_to_apply_unless_overriden.iter() {
682                 let longhand_id = match declaration.id() {
683                     PropertyDeclarationId::Longhand(id) => id,
684                     PropertyDeclarationId::Custom(..) => unreachable!(),
685                 };
686                 debug_assert!(!longhand_id.is_logical());
687                 if self.seen.contains(longhand_id) {
688                     continue;
689                 }
690                 self.apply_declaration(longhand_id, declaration);
691             }
692         }
693 
694         if Phase::is_early() {
695             self.fixup_font_stuff();
696             self.compute_writing_mode();
697         } else {
698             self.finished_applying_properties();
699         }
700     }
701 
702     fn compute_writing_mode(&mut self) {
703         let writing_mode = match self.cascade_mode {
704             CascadeMode::Unvisited { .. } => {
705                 WritingMode::new(self.context.builder.get_inherited_box())
706             },
707             CascadeMode::Visited { writing_mode } => writing_mode,
708         };
709         self.context.builder.writing_mode = writing_mode;
710     }
711 
712     fn compute_visited_style_if_needed<E>(
713         &mut self,
714         element: Option<E>,
715         parent_style: Option<&ComputedValues>,
716         parent_style_ignoring_first_line: Option<&ComputedValues>,
717         layout_parent_style: Option<&ComputedValues>,
718         guards: &StylesheetGuards,
719     ) where
720         E: TElement,
721     {
722         let visited_rules = match self.cascade_mode {
723             CascadeMode::Unvisited { visited_rules } => visited_rules,
724             CascadeMode::Visited { .. } => return,
725         };
726 
727         let visited_rules = match visited_rules {
728             Some(rules) => rules,
729             None => return,
730         };
731 
732         let is_link = self.context.builder.pseudo.is_none() && element.unwrap().is_link();
733 
734         macro_rules! visited_parent {
735             ($parent:expr) => {
736                 if is_link {
737                     $parent
738                 } else {
739                     $parent.map(|p| p.visited_style().unwrap_or(p))
740                 }
741             };
742         }
743 
744         let writing_mode = self.context.builder.writing_mode;
745 
746         // We could call apply_declarations directly, but that'd cause
747         // another instantiation of this function which is not great.
748         let style = cascade_rules(
749             self.context.builder.device,
750             self.context.builder.pseudo,
751             visited_rules,
752             guards,
753             visited_parent!(parent_style),
754             visited_parent!(parent_style_ignoring_first_line),
755             visited_parent!(layout_parent_style),
756             self.context.font_metrics_provider,
757             CascadeMode::Visited { writing_mode },
758             self.context.quirks_mode,
759             // The rule cache doesn't care about caching :visited
760             // styles, we cache the unvisited style instead. We still do
761             // need to set the caching dependencies properly if present
762             // though, so the cache conditions need to match.
763             None, // rule_cache
764             &mut *self.context.rule_cache_conditions.borrow_mut(),
765             element,
766         );
767         self.context.builder.visited_style = Some(style);
768     }
769 
770     fn finished_applying_properties(&mut self) {
771         let builder = &mut self.context.builder;
772 
773         #[cfg(feature = "gecko")]
774         {
775             if let Some(bg) = builder.get_background_if_mutated() {
776                 bg.fill_arrays();
777             }
778 
779             if let Some(svg) = builder.get_svg_if_mutated() {
780                 svg.fill_arrays();
781             }
782         }
783 
784         if self
785             .author_specified
786             .contains_any(LonghandIdSet::border_background_properties())
787         {
788             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
789         }
790         if self
791             .author_specified
792             .contains_any(LonghandIdSet::padding_properties())
793         {
794             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING);
795         }
796 
797         if self
798             .author_specified
799             .contains(LonghandId::FontFamily)
800         {
801             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY);
802         }
803 
804         if self
805             .author_specified
806             .contains(LonghandId::LetterSpacing)
807         {
808             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING);
809         }
810 
811         if self
812             .author_specified
813             .contains(LonghandId::WordSpacing)
814         {
815             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING);
816         }
817 
818         if self
819             .author_specified
820             .contains(LonghandId::FontSynthesis)
821         {
822             builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS);
823         }
824 
825         #[cfg(feature = "servo")]
826         {
827             if let Some(font) = builder.get_font_if_mutated() {
828                 font.compute_font_hash();
829             }
830         }
831     }
832 
833     fn try_to_use_cached_reset_properties(
834         &mut self,
835         cache: Option<&'b RuleCache>,
836         guards: &StylesheetGuards,
837     ) -> bool {
838         let cache = match cache {
839             Some(cache) => cache,
840             None => return false,
841         };
842 
843         let builder = &mut self.context.builder;
844 
845         let cached_style = match cache.find(guards, &builder) {
846             Some(style) => style,
847             None => return false,
848         };
849 
850         builder.copy_reset_from(cached_style);
851 
852         // We're using the same reset style as another element, and we'll skip
853         // applying the relevant properties. So we need to do the relevant
854         // bookkeeping here to keep these bits correct.
855         //
856         // Note that the border/background properties are non-inherited, so we
857         // don't need to do anything else other than just copying the bits over.
858         //
859         // When using this optimization, we also need to copy whether the old
860         // style specified viewport units / used font-relative lengths, this one
861         // would as well.  It matches the same rules, so it is the right thing
862         // to do anyways, even if it's only used on inherited properties.
863         let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND |
864             ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING |
865             ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS |
866             ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS |
867             ComputedValueFlags::USES_VIEWPORT_UNITS;
868         builder.add_flags(cached_style.flags & bits_to_copy);
869 
870         true
871     }
872 
873     /// The default font type (which is stored in FontFamilyList's
874     /// `mDefaultFontType`) depends on the current lang group and generic font
875     /// family, so we may need to recompute it if or the family changed.
876     ///
877     /// Also, we prioritize non-document fonts here if we need to (see the pref
878     /// `browser.display.use_document_fonts`).
879     #[inline]
880     #[cfg(feature = "gecko")]
881     fn recompute_default_font_family_type_if_needed(&mut self) {
882         use crate::gecko_bindings::bindings;
883         use crate::values::computed::font::GenericFontFamily;
884 
885         if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
886             return;
887         }
888 
889         let use_document_fonts = static_prefs::pref!("browser.display.use_document_fonts") != 0;
890         let builder = &mut self.context.builder;
891         let (default_font_type, prioritize_user_fonts) = {
892             let font = builder.get_font().gecko();
893 
894             // System fonts are all right, and should have the default font type
895             // set to none already, so bail out early.
896             if font.mFont.family.is_system_font {
897                 debug_assert_eq!(
898                     font.mFont.family.families.fallback,
899                     GenericFontFamily::None
900                 );
901                 return;
902             }
903 
904             let generic = font.mFont.family.families.single_generic().unwrap_or(GenericFontFamily::None);
905             let default_font_type = unsafe {
906                 bindings::Gecko_nsStyleFont_ComputeDefaultFontType(
907                     builder.device.document(),
908                     generic,
909                     font.mLanguage.mRawPtr,
910                 )
911             };
912 
913             // We prioritize user fonts over document fonts if the pref is set,
914             // and we don't have a generic family already (or we're using
915             // cursive or fantasy, since they're ignored, see bug 789788), and
916             // we have a generic family to actually replace it with.
917             let prioritize_user_fonts = !use_document_fonts &&
918                 default_font_type != GenericFontFamily::None &&
919                 matches!(
920                     generic,
921                     GenericFontFamily::None |
922                     GenericFontFamily::Fantasy |
923                     GenericFontFamily::Cursive
924                 );
925 
926             if !prioritize_user_fonts && default_font_type == font.mFont.family.families.fallback {
927                 // Nothing to do.
928                 return;
929             }
930             (default_font_type, prioritize_user_fonts)
931         };
932 
933         let font = builder.mutate_font().gecko_mut();
934         font.mFont.family.families.fallback = default_font_type;
935         if prioritize_user_fonts {
936             font.mFont.family.families.prioritize_first_generic_or_prepend(default_font_type);
937         }
938     }
939 
940     /// Some keyword sizes depend on the font family and language.
941     #[cfg(feature = "gecko")]
942     fn recompute_keyword_font_size_if_needed(&mut self) {
943         use crate::values::computed::ToComputedValue;
944         use crate::values::specified;
945 
946         if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
947             return;
948         }
949 
950         let new_size = {
951             let font = self.context.builder.get_font();
952             let info = font.clone_font_size().keyword_info;
953             let new_size = match info.kw {
954                 specified::FontSizeKeyword::None => return,
955                 _ => {
956                     self.context.for_non_inherited_property = None;
957                     specified::FontSize::Keyword(info).to_computed_value(self.context)
958                 },
959             };
960 
961             if font.gecko().mScriptUnconstrainedSize == new_size.size {
962                 return;
963             }
964 
965             new_size
966         };
967 
968         self.context.builder.mutate_font().set_font_size(new_size);
969     }
970 
971     /// Some properties, plus setting font-size itself, may make us go out of
972     /// our minimum font-size range.
973     #[cfg(feature = "gecko")]
974     fn constrain_font_size_if_needed(&mut self) {
975         use crate::gecko_bindings::bindings;
976         use crate::values::generics::NonNegative;
977 
978         if !self.seen.contains(LonghandId::XLang) &&
979             !self.seen.contains(LonghandId::FontFamily) &&
980             !self.seen.contains(LonghandId::MozMinFontSizeRatio) &&
981             !self.seen.contains(LonghandId::FontSize)
982         {
983             return;
984         }
985 
986         let builder = &mut self.context.builder;
987         let min_font_size = {
988             let font = builder.get_font().gecko();
989             let min_font_size = unsafe {
990                 bindings::Gecko_nsStyleFont_ComputeMinSize(font, builder.device.document())
991             };
992 
993             if font.mFont.size.0 >= min_font_size {
994                 return;
995             }
996 
997             NonNegative(min_font_size)
998         };
999 
1000         builder.mutate_font().gecko_mut().mFont.size = min_font_size;
1001     }
1002 
1003     /// <svg:text> is not affected by text zoom, and it uses a preshint
1004     /// to disable it. We fix up the struct when this happens by
1005     /// unzooming its contained font values, which will have been zoomed
1006     /// in the parent.
1007     ///
1008     /// FIXME(emilio): Also, why doing this _before_ handling font-size? That
1009     /// sounds wrong.
1010     #[cfg(feature = "gecko")]
1011     fn unzoom_fonts_if_needed(&mut self) {
1012         if !self.seen.contains(LonghandId::XTextZoom) {
1013             return;
1014         }
1015 
1016         let builder = &mut self.context.builder;
1017 
1018         let parent_zoom = builder.get_parent_font().gecko().mAllowZoomAndMinSize;
1019         let zoom = builder.get_font().gecko().mAllowZoomAndMinSize;
1020         if zoom == parent_zoom {
1021             return;
1022         }
1023         debug_assert!(
1024             !zoom,
1025             "We only ever disable text zoom (in svg:text), never enable it"
1026         );
1027         let device = builder.device;
1028         builder.mutate_font().unzoom_fonts(device);
1029     }
1030 
1031     /// MathML script* attributes do some very weird shit with font-size.
1032     ///
1033     /// Handle them specially here, separate from other font-size stuff.
1034     ///
1035     /// How this should interact with lang="" and font-family-dependent sizes is
1036     /// not clear to me. For now just pretend those don't exist here.
1037     #[cfg(feature = "gecko")]
1038     fn handle_mathml_scriptlevel_if_needed(&mut self) {
1039         use crate::values::generics::NonNegative;
1040 
1041         if !self.seen.contains(LonghandId::MathDepth) &&
1042             !self.seen.contains(LonghandId::MozScriptMinSize) &&
1043             !self.seen.contains(LonghandId::MozScriptSizeMultiplier)
1044         {
1045             return;
1046         }
1047 
1048         // If the user specifies a font-size, just let it be.
1049         if self.seen.contains(LonghandId::FontSize) {
1050             return;
1051         }
1052 
1053         let builder = &mut self.context.builder;
1054         let (new_size, new_unconstrained_size) = {
1055             let font = builder.get_font().gecko();
1056             let parent_font = builder.get_parent_font().gecko();
1057 
1058             let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
1059 
1060             if delta == 0 {
1061                 return;
1062             }
1063 
1064             let mut min = parent_font.mScriptMinSize;
1065             if font.mAllowZoomAndMinSize {
1066                 min = builder.device.zoom_text(min);
1067             }
1068 
1069             let scale = (parent_font.mScriptSizeMultiplier as f32).powi(delta as i32);
1070             let parent_size = parent_font.mSize.0;
1071             let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
1072             let new_size = parent_size.scale_by(scale);
1073             let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
1074 
1075             if scale <= 1. {
1076                 // The parent size can be smaller than scriptminsize, e.g. if it
1077                 // was specified explicitly. Don't scale in this case, but we
1078                 // don't want to set it to scriptminsize either since that will
1079                 // make it larger.
1080                 if parent_size <= min {
1081                     (parent_size, new_unconstrained_size)
1082                 } else {
1083                     (min.max(new_size), new_unconstrained_size)
1084                 }
1085             } else {
1086                 // If the new unconstrained size is larger than the min size,
1087                 // this means we have escaped the grasp of scriptminsize and can
1088                 // revert to using the unconstrained size.
1089                 // However, if the new size is even larger (perhaps due to usage
1090                 // of em units), use that instead.
1091                 (
1092                     new_size.min(new_unconstrained_size.max(min)),
1093                     new_unconstrained_size,
1094                 )
1095             }
1096         };
1097         let font = builder.mutate_font().gecko_mut();
1098         font.mFont.size = NonNegative(new_size);
1099         font.mSize = NonNegative(new_size);
1100         font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
1101     }
1102 
1103     /// Various properties affect how font-size and font-family are computed.
1104     ///
1105     /// These need to be handled here, since relative lengths and ex / ch units
1106     /// for late properties depend on these.
1107     fn fixup_font_stuff(&mut self) {
1108         #[cfg(feature = "gecko")]
1109         {
1110             self.unzoom_fonts_if_needed();
1111             self.recompute_default_font_family_type_if_needed();
1112             self.recompute_keyword_font_size_if_needed();
1113             self.handle_mathml_scriptlevel_if_needed();
1114             self.constrain_font_size_if_needed()
1115         }
1116     }
1117 }
1118