Backed out 5 changesets (bug 1731541) for causing multiple wpt failures. CLOSED TREE
[gecko.git] / servo / components / style / style_resolver.rs
blob6e5e081156f0ddbd491e3058df27aa07239f810b
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::computed_value_flags::ComputedValueFlags;
9 use crate::context::{CascadeInputs, ElementCascadeInputs, StyleContext};
10 use crate::data::{EagerPseudoStyles, ElementStyles};
11 use crate::dom::TElement;
12 use crate::matching::MatchMethods;
13 use crate::properties::longhands::display::computed_value::T as Display;
14 use crate::properties::{ComputedValues, FirstLineReparenting};
15 use crate::rule_tree::StrongRuleNode;
16 use crate::selector_parser::{PseudoElement, SelectorImpl};
17 use crate::stylist::RuleInclusion;
18 use log::Level::Trace;
19 use selectors::matching::{
20     MatchingForInvalidation, MatchingContext, MatchingMode, NeedsSelectorFlags,
21     RelativeSelectorMatchingState, VisitedHandlingMode,
23 use servo_arc::Arc;
25 /// Whether pseudo-elements should be resolved or not.
26 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
27 pub enum PseudoElementResolution {
28     /// Only resolve pseudo-styles if possibly applicable.
29     IfApplicable,
30     /// Force pseudo-element resolution.
31     Force,
34 /// A struct that takes care of resolving the style of a given element.
35 pub struct StyleResolverForElement<'a, 'ctx, 'le, E>
36 where
37     'ctx: 'a,
38     'le: 'ctx,
39     E: TElement + MatchMethods + 'le,
41     element: E,
42     context: &'a mut StyleContext<'ctx, E>,
43     rule_inclusion: RuleInclusion,
44     pseudo_resolution: PseudoElementResolution,
45     _marker: ::std::marker::PhantomData<&'le E>,
48 struct MatchingResults {
49     rule_node: StrongRuleNode,
50     flags: ComputedValueFlags,
53 /// A style returned from the resolver machinery.
54 pub struct ResolvedStyle(pub Arc<ComputedValues>);
56 /// The primary style of an element or an element-backed pseudo-element.
57 pub struct PrimaryStyle {
58     /// The style itself.
59     pub style: ResolvedStyle,
60     /// Whether the style was reused from another element via the rule node (see
61     /// `StyleSharingCache::lookup_by_rules`).
62     pub reused_via_rule_node: bool,
65 /// A set of style returned from the resolver machinery.
66 pub struct ResolvedElementStyles {
67     /// Primary style.
68     pub primary: PrimaryStyle,
69     /// Pseudo styles.
70     pub pseudos: EagerPseudoStyles,
73 impl ResolvedElementStyles {
74     /// Convenience accessor for the primary style.
75     pub fn primary_style(&self) -> &Arc<ComputedValues> {
76         &self.primary.style.0
77     }
79     /// Convenience mutable accessor for the style.
80     pub fn primary_style_mut(&mut self) -> &mut Arc<ComputedValues> {
81         &mut self.primary.style.0
82     }
85 impl PrimaryStyle {
86     /// Convenience accessor for the style.
87     pub fn style(&self) -> &ComputedValues {
88         &*self.style.0
89     }
92 impl From<ResolvedElementStyles> for ElementStyles {
93     fn from(r: ResolvedElementStyles) -> ElementStyles {
94         ElementStyles {
95             primary: Some(r.primary.style.0),
96             pseudos: r.pseudos,
97         }
98     }
101 fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
102 where
103     E: TElement,
104     F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,
106     let parent_el = element.inheritance_parent();
107     let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
108     let parent_style = parent_data.as_ref().map(|d| d.styles.primary());
110     let mut layout_parent_el = parent_el.clone();
111     let layout_parent_data;
112     let mut layout_parent_style = parent_style;
113     if parent_style.map_or(false, |s| s.is_display_contents()) {
114         layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
115         layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
116         layout_parent_style = Some(layout_parent_data.styles.primary());
117     }
119     f(
120         parent_style.map(|x| &**x),
121         layout_parent_style.map(|s| &**s),
122     )
125 fn layout_parent_style_for_pseudo<'a>(
126     primary_style: &'a PrimaryStyle,
127     layout_parent_style: Option<&'a ComputedValues>,
128 ) -> Option<&'a ComputedValues> {
129     if primary_style.style().is_display_contents() {
130         layout_parent_style
131     } else {
132         Some(primary_style.style())
133     }
136 fn eager_pseudo_is_definitely_not_generated(
137     pseudo: &PseudoElement,
138     style: &ComputedValues,
139 ) -> bool {
140     if !pseudo.is_before_or_after() {
141         return false;
142     }
144     if !style
145         .flags
146         .intersects(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE) &&
147         style.get_box().clone_display() == Display::None
148     {
149         return true;
150     }
152     if !style
153         .flags
154         .intersects(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE) &&
155         style.ineffective_content_property()
156     {
157         return true;
158     }
160     false
163 impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
164 where
165     'ctx: 'a,
166     'le: 'ctx,
167     E: TElement + MatchMethods + 'le,
169     /// Trivially construct a new StyleResolverForElement.
170     pub fn new(
171         element: E,
172         context: &'a mut StyleContext<'ctx, E>,
173         rule_inclusion: RuleInclusion,
174         pseudo_resolution: PseudoElementResolution,
175     ) -> Self {
176         Self {
177             element,
178             context,
179             rule_inclusion,
180             pseudo_resolution,
181             _marker: ::std::marker::PhantomData,
182         }
183     }
185     /// Resolve just the style of a given element.
186     pub fn resolve_primary_style(
187         &mut self,
188         parent_style: Option<&ComputedValues>,
189         layout_parent_style: Option<&ComputedValues>,
190     ) -> PrimaryStyle {
191         let primary_results = self.match_primary(VisitedHandlingMode::AllLinksUnvisited);
193         let inside_link = parent_style.map_or(false, |s| s.visited_style().is_some());
195         let visited_rules = if self.context.shared.visited_styles_enabled &&
196             (inside_link || self.element.is_link())
197         {
198             let visited_matching_results =
199                 self.match_primary(VisitedHandlingMode::RelevantLinkVisited);
200             Some(visited_matching_results.rule_node)
201         } else {
202             None
203         };
205         self.cascade_primary_style(
206             CascadeInputs {
207                 rules: Some(primary_results.rule_node),
208                 visited_rules,
209                 flags: primary_results.flags,
210             },
211             parent_style,
212             layout_parent_style,
213         )
214     }
216     fn cascade_primary_style(
217         &mut self,
218         inputs: CascadeInputs,
219         parent_style: Option<&ComputedValues>,
220         layout_parent_style: Option<&ComputedValues>,
221     ) -> PrimaryStyle {
222         // Before doing the cascade, check the sharing cache and see if we can
223         // reuse the style via rule node identity.
224         let may_reuse = self.element.matches_user_and_content_rules() &&
225             parent_style.is_some() &&
226             inputs.rules.is_some();
228         if may_reuse {
229             let cached = self.context.thread_local.sharing_cache.lookup_by_rules(
230                 self.context.shared,
231                 parent_style.unwrap(),
232                 inputs.rules.as_ref().unwrap(),
233                 inputs.visited_rules.as_ref(),
234                 self.element,
235             );
236             if let Some(mut primary_style) = cached {
237                 self.context.thread_local.statistics.styles_reused += 1;
238                 primary_style.reused_via_rule_node |= true;
239                 return primary_style;
240             }
241         }
243         // No style to reuse. Cascade the style, starting with visited style
244         // if necessary.
245         PrimaryStyle {
246             style: self.cascade_style_and_visited(
247                 inputs,
248                 parent_style,
249                 layout_parent_style,
250                 /* pseudo = */ None,
251             ),
252             reused_via_rule_node: false,
253         }
254     }
256     /// Resolve the style of a given element, and all its eager pseudo-elements.
257     pub fn resolve_style(
258         &mut self,
259         parent_style: Option<&ComputedValues>,
260         layout_parent_style: Option<&ComputedValues>,
261     ) -> ResolvedElementStyles {
262         let primary_style = self.resolve_primary_style(parent_style, layout_parent_style);
264         let mut pseudo_styles = EagerPseudoStyles::default();
266         if !self.element.is_pseudo_element() {
267             let layout_parent_style_for_pseudo =
268                 layout_parent_style_for_pseudo(&primary_style, layout_parent_style);
269             SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
270                 let pseudo_style = self.resolve_pseudo_style(
271                     &pseudo,
272                     &primary_style,
273                     layout_parent_style_for_pseudo,
274                 );
276                 if let Some(style) = pseudo_style {
277                     if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
278                         eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
279                     {
280                         return;
281                     }
282                     pseudo_styles.set(&pseudo, style.0);
283                 }
284             })
285         }
287         ResolvedElementStyles {
288             primary: primary_style,
289             pseudos: pseudo_styles,
290         }
291     }
293     /// Resolve an element's styles with the default inheritance parent/layout
294     /// parents.
295     pub fn resolve_style_with_default_parents(&mut self) -> ResolvedElementStyles {
296         with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
297             self.resolve_style(parent_style, layout_parent_style)
298         })
299     }
301     /// Cascade a set of rules, using the default parent for inheritance.
302     pub fn cascade_style_and_visited_with_default_parents(
303         &mut self,
304         inputs: CascadeInputs,
305     ) -> ResolvedStyle {
306         with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
307             self.cascade_style_and_visited(
308                 inputs,
309                 parent_style,
310                 layout_parent_style,
311                 /* pseudo = */ None,
312             )
313         })
314     }
316     /// Cascade a set of rules for pseudo element, using the default parent for inheritance.
317     pub fn cascade_style_and_visited_for_pseudo_with_default_parents(
318         &mut self,
319         inputs: CascadeInputs,
320         pseudo: &PseudoElement,
321         primary_style: &PrimaryStyle,
322     ) -> ResolvedStyle {
323         with_default_parent_styles(self.element, |_, layout_parent_style| {
324             let layout_parent_style_for_pseudo =
325                 layout_parent_style_for_pseudo(primary_style, layout_parent_style);
327             self.cascade_style_and_visited(
328                 inputs,
329                 Some(primary_style.style()),
330                 layout_parent_style_for_pseudo,
331                 Some(pseudo),
332             )
333         })
334     }
336     fn cascade_style_and_visited(
337         &mut self,
338         inputs: CascadeInputs,
339         parent_style: Option<&ComputedValues>,
340         layout_parent_style: Option<&ComputedValues>,
341         pseudo: Option<&PseudoElement>,
342     ) -> ResolvedStyle {
343         debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
345         let implemented_pseudo = self.element.implemented_pseudo_element();
346         let pseudo = pseudo.or(implemented_pseudo.as_ref());
348         let mut conditions = Default::default();
349         let values = self.context.shared.stylist.cascade_style_and_visited(
350             Some(self.element),
351             pseudo,
352             inputs,
353             &self.context.shared.guards,
354             pseudo.and(parent_style),
355             parent_style,
356             layout_parent_style,
357             FirstLineReparenting::No,
358             Some(&self.context.thread_local.rule_cache),
359             &mut conditions,
360         );
362         self.context.thread_local.rule_cache.insert_if_possible(
363             &self.context.shared.guards,
364             &values,
365             pseudo,
366             &conditions,
367         );
369         ResolvedStyle(values)
370     }
372     /// Cascade the element and pseudo-element styles with the default parents.
373     pub fn cascade_styles_with_default_parents(
374         &mut self,
375         inputs: ElementCascadeInputs,
376     ) -> ResolvedElementStyles {
377         with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
378             let primary_style =
379                 self.cascade_primary_style(inputs.primary, parent_style, layout_parent_style);
381             let mut pseudo_styles = EagerPseudoStyles::default();
382             if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
383                 let layout_parent_style_for_pseudo = if primary_style.style().is_display_contents()
384                 {
385                     layout_parent_style
386                 } else {
387                     Some(primary_style.style())
388                 };
390                 for (i, inputs) in pseudo_array.iter_mut().enumerate() {
391                     if let Some(inputs) = inputs.take() {
392                         let pseudo = PseudoElement::from_eager_index(i);
394                         let style = self.cascade_style_and_visited(
395                             inputs,
396                             Some(primary_style.style()),
397                             layout_parent_style_for_pseudo,
398                             Some(&pseudo),
399                         );
401                         if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
402                             eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
403                         {
404                             continue;
405                         }
407                         pseudo_styles.set(&pseudo, style.0);
408                     }
409                 }
410             }
412             ResolvedElementStyles {
413                 primary: primary_style,
414                 pseudos: pseudo_styles,
415             }
416         })
417     }
419     fn resolve_pseudo_style(
420         &mut self,
421         pseudo: &PseudoElement,
422         originating_element_style: &PrimaryStyle,
423         layout_parent_style: Option<&ComputedValues>,
424     ) -> Option<ResolvedStyle> {
425         let MatchingResults {
426             rule_node,
427             mut flags,
428         } = self.match_pseudo(
429             &originating_element_style.style.0,
430             pseudo,
431             VisitedHandlingMode::AllLinksUnvisited,
432         )?;
434         let mut visited_rules = None;
435         if originating_element_style.style().visited_style().is_some() {
436             visited_rules = self
437                 .match_pseudo(
438                     &originating_element_style.style.0,
439                     pseudo,
440                     VisitedHandlingMode::RelevantLinkVisited,
441                 )
442                 .map(|results| {
443                     flags |= results.flags;
444                     results.rule_node
445                 });
446         }
448         Some(self.cascade_style_and_visited(
449             CascadeInputs {
450                 rules: Some(rule_node),
451                 visited_rules,
452                 flags,
453             },
454             Some(originating_element_style.style()),
455             layout_parent_style,
456             Some(pseudo),
457         ))
458     }
460     fn match_primary(&mut self, visited_handling: VisitedHandlingMode) -> MatchingResults {
461         debug!(
462             "Match primary for {:?}, visited: {:?}",
463             self.element, visited_handling
464         );
465         let mut applicable_declarations = ApplicableDeclarationList::new();
467         let bloom_filter = self.context.thread_local.bloom_filter.filter();
468         let selector_caches = &mut self.context.thread_local.selector_caches;
469         let mut matching_context = MatchingContext::new_for_visited(
470             MatchingMode::Normal,
471             Some(bloom_filter),
472             selector_caches,
473             visited_handling,
474             self.context.shared.quirks_mode(),
475             NeedsSelectorFlags::Yes,
476             MatchingForInvalidation::No,
477         );
479         let stylist = &self.context.shared.stylist;
480         let implemented_pseudo = self.element.implemented_pseudo_element();
481         // Compute the primary rule node.
482         stylist.push_applicable_declarations(
483             self.element,
484             implemented_pseudo.as_ref(),
485             self.element.style_attribute(),
486             self.element.smil_override(),
487             self.element.animation_declarations(self.context.shared),
488             self.rule_inclusion,
489             &mut applicable_declarations,
490             &mut matching_context,
491         );
493         // FIXME(emilio): This is a hack for animations, and should go away.
494         self.element.unset_dirty_style_attribute();
496         let rule_node = stylist
497             .rule_tree()
498             .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
500         if log_enabled!(Trace) {
501             trace!("Matched rules for {:?}:", self.element);
502             for rn in rule_node.self_and_ancestors() {
503                 let source = rn.style_source();
504                 if source.is_some() {
505                     trace!(" > {:?}", source);
506                 }
507             }
508         }
509         // This is a bit awkward - ideally, the flag is set directly where `considered_relative_selector`
510         // is; however, in that context, the implementation detail of `extra_data` is not visible, so
511         // it's done here. A trait for manipulating the flags is an option, but not worth it for a single flag.
512         match matching_context.considered_relative_selector {
513             RelativeSelectorMatchingState::None => (),
514             RelativeSelectorMatchingState::Considered => {
515                 matching_context
516                     .extra_data
517                     .cascade_input_flags
518                     .insert(ComputedValueFlags::CONSIDERED_RELATIVE_SELECTOR);
519             },
520             RelativeSelectorMatchingState::ConsideredAnchor => {
521                 matching_context.extra_data.cascade_input_flags.insert(
522                     ComputedValueFlags::ANCHORS_RELATIVE_SELECTOR |
523                         ComputedValueFlags::CONSIDERED_RELATIVE_SELECTOR,
524                 );
525             },
526         };
528         MatchingResults {
529             rule_node,
530             flags: matching_context.extra_data.cascade_input_flags,
531         }
532     }
534     fn match_pseudo(
535         &mut self,
536         originating_element_style: &ComputedValues,
537         pseudo_element: &PseudoElement,
538         visited_handling: VisitedHandlingMode,
539     ) -> Option<MatchingResults> {
540         debug!(
541             "Match pseudo {:?} for {:?}, visited: {:?}",
542             self.element, pseudo_element, visited_handling
543         );
544         debug_assert!(pseudo_element.is_eager());
545         debug_assert!(
546             !self.element.is_pseudo_element(),
547             "Element pseudos can't have any other eager pseudo."
548         );
550         let mut applicable_declarations = ApplicableDeclarationList::new();
552         let stylist = &self.context.shared.stylist;
554         if !self
555             .element
556             .may_generate_pseudo(pseudo_element, originating_element_style)
557         {
558             return None;
559         }
561         let bloom_filter = self.context.thread_local.bloom_filter.filter();
562         let selector_caches = &mut self.context.thread_local.selector_caches;
564         let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
565             MatchingMode::ForStatelessPseudoElement,
566             Some(bloom_filter),
567             selector_caches,
568             visited_handling,
569             self.context.shared.quirks_mode(),
570             NeedsSelectorFlags::Yes,
571             MatchingForInvalidation::No,
572         );
573         matching_context.extra_data.originating_element_style = Some(originating_element_style);
575         // NB: We handle animation rules for ::before and ::after when
576         // traversing them.
577         stylist.push_applicable_declarations(
578             self.element,
579             Some(pseudo_element),
580             None,
581             None,
582             /* animation_declarations = */ Default::default(),
583             self.rule_inclusion,
584             &mut applicable_declarations,
585             &mut matching_context,
586         );
588         if applicable_declarations.is_empty() {
589             return None;
590         }
592         let rule_node = stylist
593             .rule_tree()
594             .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
596         Some(MatchingResults {
597             rule_node,
598             flags: matching_context.extra_data.cascade_input_flags,
599         })
600     }