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