Bug 1756130 [wpt PR 32898] - [CSP] Enhance unsafe-eval test to check both realms...
[gecko.git] / servo / components / style / style_resolver.rs
blob7c28e00140319c1fe11713656ccbba26020c8012
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
5 //! Style resolution for a given element or pseudo-element.
7 use crate::applicable_declarations::ApplicableDeclarationList;
8 use crate::context::{CascadeInputs, ElementCascadeInputs, StyleContext};
9 use crate::data::{EagerPseudoStyles, ElementStyles};
10 use crate::dom::TElement;
11 use crate::matching::MatchMethods;
12 use crate::properties::longhands::display::computed_value::T as Display;
13 use crate::properties::ComputedValues;
14 use crate::rule_tree::StrongRuleNode;
15 use crate::selector_parser::{PseudoElement, SelectorImpl};
16 use crate::stylist::RuleInclusion;
17 use log::Level::Trace;
18 use selectors::matching::{ElementSelectorFlags, MatchingContext};
19 use selectors::matching::{MatchingMode, VisitedHandlingMode};
20 use servo_arc::Arc;
22 /// Whether pseudo-elements should be resolved or not.
23 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
24 pub enum PseudoElementResolution {
25     /// Only resolve pseudo-styles if possibly applicable.
26     IfApplicable,
27     /// Force pseudo-element resolution.
28     Force,
31 /// A struct that takes care of resolving the style of a given element.
32 pub struct StyleResolverForElement<'a, 'ctx, 'le, E>
33 where
34     'ctx: 'a,
35     'le: 'ctx,
36     E: TElement + MatchMethods + 'le,
38     element: E,
39     context: &'a mut StyleContext<'ctx, E>,
40     rule_inclusion: RuleInclusion,
41     pseudo_resolution: PseudoElementResolution,
42     _marker: ::std::marker::PhantomData<&'le E>,
45 struct MatchingResults {
46     rule_node: StrongRuleNode,
49 /// A style returned from the resolver machinery.
50 pub struct ResolvedStyle(pub Arc<ComputedValues>);
52 /// The primary style of an element or an element-backed pseudo-element.
53 pub struct PrimaryStyle {
54     /// The style itself.
55     pub style: ResolvedStyle,
56     /// Whether the style was reused from another element via the rule node (see
57     /// `StyleSharingCache::lookup_by_rules`).
58     pub reused_via_rule_node: bool,
61 /// A set of style returned from the resolver machinery.
62 pub struct ResolvedElementStyles {
63     /// Primary style.
64     pub primary: PrimaryStyle,
65     /// Pseudo styles.
66     pub pseudos: EagerPseudoStyles,
69 impl ResolvedElementStyles {
70     /// Convenience accessor for the primary style.
71     pub fn primary_style(&self) -> &Arc<ComputedValues> {
72         &self.primary.style.0
73     }
75     /// Convenience mutable accessor for the style.
76     pub fn primary_style_mut(&mut self) -> &mut Arc<ComputedValues> {
77         &mut self.primary.style.0
78     }
81 impl PrimaryStyle {
82     /// Convenience accessor for the style.
83     pub fn style(&self) -> &ComputedValues {
84         &*self.style.0
85     }
88 impl From<ResolvedElementStyles> for ElementStyles {
89     fn from(r: ResolvedElementStyles) -> ElementStyles {
90         ElementStyles {
91             primary: Some(r.primary.style.0),
92             pseudos: r.pseudos,
93         }
94     }
97 fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
98 where
99     E: TElement,
100     F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,
102     let parent_el = element.inheritance_parent();
103     let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
104     let parent_style = parent_data.as_ref().map(|d| d.styles.primary());
106     let mut layout_parent_el = parent_el.clone();
107     let layout_parent_data;
108     let mut layout_parent_style = parent_style;
109     if parent_style.map_or(false, |s| s.is_display_contents()) {
110         layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
111         layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
112         layout_parent_style = Some(layout_parent_data.styles.primary());
113     }
115     f(
116         parent_style.map(|x| &**x),
117         layout_parent_style.map(|s| &**s),
118     )
121 fn layout_parent_style_for_pseudo<'a>(
122     primary_style: &'a PrimaryStyle,
123     layout_parent_style: Option<&'a ComputedValues>,
124 ) -> Option<&'a ComputedValues> {
125     if primary_style.style().is_display_contents() {
126         layout_parent_style
127     } else {
128         Some(primary_style.style())
129     }
132 fn eager_pseudo_is_definitely_not_generated(
133     pseudo: &PseudoElement,
134     style: &ComputedValues,
135 ) -> bool {
136     use crate::computed_value_flags::ComputedValueFlags;
138     if !pseudo.is_before_or_after() {
139         return false;
140     }
142     if !style
143         .flags
144         .intersects(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE) &&
145         style.get_box().clone_display() == Display::None
146     {
147         return true;
148     }
150     if !style
151         .flags
152         .intersects(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE) &&
153         style.ineffective_content_property()
154     {
155         return true;
156     }
158     false
161 impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
162 where
163     'ctx: 'a,
164     'le: 'ctx,
165     E: TElement + MatchMethods + 'le,
167     /// Trivially construct a new StyleResolverForElement.
168     pub fn new(
169         element: E,
170         context: &'a mut StyleContext<'ctx, E>,
171         rule_inclusion: RuleInclusion,
172         pseudo_resolution: PseudoElementResolution,
173     ) -> Self {
174         Self {
175             element,
176             context,
177             rule_inclusion,
178             pseudo_resolution,
179             _marker: ::std::marker::PhantomData,
180         }
181     }
183     /// Resolve just the style of a given element.
184     pub fn resolve_primary_style(
185         &mut self,
186         parent_style: Option<&ComputedValues>,
187         layout_parent_style: Option<&ComputedValues>,
188     ) -> PrimaryStyle {
189         let primary_results = self.match_primary(VisitedHandlingMode::AllLinksUnvisited);
191         let inside_link = parent_style.map_or(false, |s| s.visited_style().is_some());
193         let visited_rules = if self.context.shared.visited_styles_enabled &&
194             (inside_link || self.element.is_link())
195         {
196             let visited_matching_results =
197                 self.match_primary(VisitedHandlingMode::RelevantLinkVisited);
198             Some(visited_matching_results.rule_node)
199         } else {
200             None
201         };
203         self.cascade_primary_style(
204             CascadeInputs {
205                 rules: Some(primary_results.rule_node),
206                 visited_rules,
207             },
208             parent_style,
209             layout_parent_style,
210         )
211     }
213     fn cascade_primary_style(
214         &mut self,
215         inputs: CascadeInputs,
216         parent_style: Option<&ComputedValues>,
217         layout_parent_style: Option<&ComputedValues>,
218     ) -> PrimaryStyle {
219         // Before doing the cascade, check the sharing cache and see if we can
220         // reuse the style via rule node identity.
221         let may_reuse = !self.element.is_in_native_anonymous_subtree() &&
222             parent_style.is_some() &&
223             inputs.rules.is_some();
225         if may_reuse {
226             let cached = self.context.thread_local.sharing_cache.lookup_by_rules(
227                 self.context.shared,
228                 parent_style.unwrap(),
229                 inputs.rules.as_ref().unwrap(),
230                 inputs.visited_rules.as_ref(),
231                 self.element,
232             );
233             if let Some(mut primary_style) = cached {
234                 self.context.thread_local.statistics.styles_reused += 1;
235                 primary_style.reused_via_rule_node |= true;
236                 return primary_style;
237             }
238         }
240         // No style to reuse. Cascade the style, starting with visited style
241         // if necessary.
242         PrimaryStyle {
243             style: self.cascade_style_and_visited(
244                 inputs,
245                 parent_style,
246                 layout_parent_style,
247                 /* pseudo = */ None,
248             ),
249             reused_via_rule_node: false,
250         }
251     }
253     /// Resolve the style of a given element, and all its eager pseudo-elements.
254     pub fn resolve_style(
255         &mut self,
256         parent_style: Option<&ComputedValues>,
257         layout_parent_style: Option<&ComputedValues>,
258     ) -> ResolvedElementStyles {
259         let primary_style = self.resolve_primary_style(parent_style, layout_parent_style);
261         let mut pseudo_styles = EagerPseudoStyles::default();
263         if !self.element.is_pseudo_element() {
264             let layout_parent_style_for_pseudo =
265                 layout_parent_style_for_pseudo(&primary_style, layout_parent_style);
266             SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
267                 let pseudo_style = self.resolve_pseudo_style(
268                     &pseudo,
269                     &primary_style,
270                     layout_parent_style_for_pseudo,
271                 );
273                 if let Some(style) = pseudo_style {
274                     if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
275                         eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
276                     {
277                         return;
278                     }
279                     pseudo_styles.set(&pseudo, style.0);
280                 }
281             })
282         }
284         ResolvedElementStyles {
285             primary: primary_style,
286             pseudos: pseudo_styles,
287         }
288     }
290     /// Resolve an element's styles with the default inheritance parent/layout
291     /// parents.
292     pub fn resolve_style_with_default_parents(&mut self) -> ResolvedElementStyles {
293         with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
294             self.resolve_style(parent_style, layout_parent_style)
295         })
296     }
298     /// Cascade a set of rules, using the default parent for inheritance.
299     pub fn cascade_style_and_visited_with_default_parents(
300         &mut self,
301         inputs: CascadeInputs,
302     ) -> ResolvedStyle {
303         with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
304             self.cascade_style_and_visited(
305                 inputs,
306                 parent_style,
307                 layout_parent_style,
308                 /* pseudo = */ None,
309             )
310         })
311     }
313     /// Cascade a set of rules for pseudo element, using the default parent for inheritance.
314     pub fn cascade_style_and_visited_for_pseudo_with_default_parents(
315         &mut self,
316         inputs: CascadeInputs,
317         pseudo: &PseudoElement,
318         primary_style: &PrimaryStyle,
319     ) -> ResolvedStyle {
320         with_default_parent_styles(self.element, |_, layout_parent_style| {
321             let layout_parent_style_for_pseudo =
322                 layout_parent_style_for_pseudo(primary_style, layout_parent_style);
324             self.cascade_style_and_visited(
325                 inputs,
326                 Some(primary_style.style()),
327                 layout_parent_style_for_pseudo,
328                 Some(pseudo),
329             )
330         })
331     }
333     fn cascade_style_and_visited(
334         &mut self,
335         inputs: CascadeInputs,
336         parent_style: Option<&ComputedValues>,
337         layout_parent_style: Option<&ComputedValues>,
338         pseudo: Option<&PseudoElement>,
339     ) -> ResolvedStyle {
340         debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
342         let implemented_pseudo = self.element.implemented_pseudo_element();
343         let pseudo = pseudo.or(implemented_pseudo.as_ref());
345         let mut conditions = Default::default();
346         let values = self.context.shared.stylist.cascade_style_and_visited(
347             Some(self.element),
348             pseudo,
349             inputs,
350             &self.context.shared.guards,
351             parent_style,
352             parent_style,
353             layout_parent_style,
354             &self.context.thread_local.font_metrics_provider,
355             Some(&self.context.thread_local.rule_cache),
356             &mut conditions,
357         );
359         self.context.thread_local.rule_cache.insert_if_possible(
360             &self.context.shared.guards,
361             &values,
362             pseudo,
363             &conditions,
364         );
366         ResolvedStyle(values)
367     }
369     /// Cascade the element and pseudo-element styles with the default parents.
370     pub fn cascade_styles_with_default_parents(
371         &mut self,
372         inputs: ElementCascadeInputs,
373     ) -> ResolvedElementStyles {
374         with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
375             let primary_style =
376                 self.cascade_primary_style(inputs.primary, parent_style, layout_parent_style);
378             let mut pseudo_styles = EagerPseudoStyles::default();
379             if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
380                 let layout_parent_style_for_pseudo = if primary_style.style().is_display_contents()
381                 {
382                     layout_parent_style
383                 } else {
384                     Some(primary_style.style())
385                 };
387                 for (i, inputs) in pseudo_array.iter_mut().enumerate() {
388                     if let Some(inputs) = inputs.take() {
389                         let pseudo = PseudoElement::from_eager_index(i);
391                         let style = self.cascade_style_and_visited(
392                             inputs,
393                             Some(primary_style.style()),
394                             layout_parent_style_for_pseudo,
395                             Some(&pseudo),
396                         );
398                         if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
399                             eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
400                         {
401                             continue;
402                         }
404                         pseudo_styles.set(&pseudo, style.0);
405                     }
406                 }
407             }
409             ResolvedElementStyles {
410                 primary: primary_style,
411                 pseudos: pseudo_styles,
412             }
413         })
414     }
416     fn resolve_pseudo_style(
417         &mut self,
418         pseudo: &PseudoElement,
419         originating_element_style: &PrimaryStyle,
420         layout_parent_style: Option<&ComputedValues>,
421     ) -> Option<ResolvedStyle> {
422         let rules = self.match_pseudo(
423             originating_element_style.style(),
424             pseudo,
425             VisitedHandlingMode::AllLinksUnvisited,
426         )?;
428         let mut visited_rules = None;
429         if originating_element_style.style().visited_style().is_some() {
430             visited_rules = self.match_pseudo(
431                 originating_element_style.style(),
432                 pseudo,
433                 VisitedHandlingMode::RelevantLinkVisited,
434             );
435         }
437         Some(self.cascade_style_and_visited(
438             CascadeInputs {
439                 rules: Some(rules),
440                 visited_rules,
441             },
442             Some(originating_element_style.style()),
443             layout_parent_style,
444             Some(pseudo),
445         ))
446     }
448     fn match_primary(&mut self, visited_handling: VisitedHandlingMode) -> MatchingResults {
449         debug!(
450             "Match primary for {:?}, visited: {:?}",
451             self.element, visited_handling
452         );
453         let mut applicable_declarations = ApplicableDeclarationList::new();
455         let map = &mut self.context.thread_local.selector_flags;
456         let bloom_filter = self.context.thread_local.bloom_filter.filter();
457         let nth_index_cache = &mut self.context.thread_local.nth_index_cache;
458         let mut matching_context = MatchingContext::new_for_visited(
459             MatchingMode::Normal,
460             Some(bloom_filter),
461             Some(nth_index_cache),
462             visited_handling,
463             self.context.shared.quirks_mode(),
464         );
466         let stylist = &self.context.shared.stylist;
467         let implemented_pseudo = self.element.implemented_pseudo_element();
468         {
469             let resolving_element = self.element;
470             let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
471                 resolving_element.apply_selector_flags(map, element, flags);
472             };
474             // Compute the primary rule node.
475             stylist.push_applicable_declarations(
476                 self.element,
477                 implemented_pseudo.as_ref(),
478                 self.element.style_attribute(),
479                 self.element.smil_override(),
480                 self.element.animation_declarations(self.context.shared),
481                 self.rule_inclusion,
482                 &mut applicable_declarations,
483                 &mut matching_context,
484                 &mut set_selector_flags,
485             );
486         }
488         // FIXME(emilio): This is a hack for animations, and should go away.
489         self.element.unset_dirty_style_attribute();
491         let rule_node = stylist
492             .rule_tree()
493             .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
495         if log_enabled!(Trace) {
496             trace!("Matched rules for {:?}:", self.element);
497             for rn in rule_node.self_and_ancestors() {
498                 let source = rn.style_source();
499                 if source.is_some() {
500                     trace!(" > {:?}", source);
501                 }
502             }
503         }
505         MatchingResults { rule_node }
506     }
508     fn match_pseudo(
509         &mut self,
510         originating_element_style: &ComputedValues,
511         pseudo_element: &PseudoElement,
512         visited_handling: VisitedHandlingMode,
513     ) -> Option<StrongRuleNode> {
514         debug!(
515             "Match pseudo {:?} for {:?}, visited: {:?}",
516             self.element, pseudo_element, visited_handling
517         );
518         debug_assert!(pseudo_element.is_eager());
519         debug_assert!(
520             !self.element.is_pseudo_element(),
521             "Element pseudos can't have any other eager pseudo."
522         );
524         let mut applicable_declarations = ApplicableDeclarationList::new();
526         let stylist = &self.context.shared.stylist;
528         if !self
529             .element
530             .may_generate_pseudo(pseudo_element, originating_element_style)
531         {
532             return None;
533         }
535         let bloom_filter = self.context.thread_local.bloom_filter.filter();
536         let nth_index_cache = &mut self.context.thread_local.nth_index_cache;
538         let mut matching_context = MatchingContext::new_for_visited(
539             MatchingMode::ForStatelessPseudoElement,
540             Some(bloom_filter),
541             Some(nth_index_cache),
542             visited_handling,
543             self.context.shared.quirks_mode(),
544         );
546         let map = &mut self.context.thread_local.selector_flags;
547         let resolving_element = self.element;
548         let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
549             resolving_element.apply_selector_flags(map, element, flags);
550         };
552         // NB: We handle animation rules for ::before and ::after when
553         // traversing them.
554         stylist.push_applicable_declarations(
555             self.element,
556             Some(pseudo_element),
557             None,
558             None,
559             /* animation_declarations = */ Default::default(),
560             self.rule_inclusion,
561             &mut applicable_declarations,
562             &mut matching_context,
563             &mut set_selector_flags,
564         );
566         if applicable_declarations.is_empty() {
567             return None;
568         }
570         let rule_node = stylist
571             .rule_tree()
572             .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
574         Some(rule_node)
575     }