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 //! Style resolution for a given element or pseudo-element.
6
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;
21
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,
29 }
30
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,
37 {
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>,
43 }
44
45 struct MatchingResults {
46 rule_node: StrongRuleNode,
47 }
48
49 /// A style returned from the resolver machinery.
50 pub struct ResolvedStyle(pub Arc<ComputedValues>);
51
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,
59 }
60
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,
67 }
68
69 impl ResolvedElementStyles {
70 /// Convenience accessor for the primary style.
primary_style(&self) -> &Arc<ComputedValues>71 pub fn primary_style(&self) -> &Arc<ComputedValues> {
72 &self.primary.style.0
73 }
74
75 /// Convenience mutable accessor for the style.
primary_style_mut(&mut self) -> &mut Arc<ComputedValues>76 pub fn primary_style_mut(&mut self) -> &mut Arc<ComputedValues> {
77 &mut self.primary.style.0
78 }
79 }
80
81 impl PrimaryStyle {
82 /// Convenience accessor for the style.
style(&self) -> &ComputedValues83 pub fn style(&self) -> &ComputedValues {
84 &*self.style.0
85 }
86 }
87
88 impl From<ResolvedElementStyles> for ElementStyles {
from(r: ResolvedElementStyles) -> ElementStyles89 fn from(r: ResolvedElementStyles) -> ElementStyles {
90 ElementStyles {
91 primary: Some(r.primary.style.0),
92 pseudos: r.pseudos,
93 }
94 }
95 }
96
with_default_parent_styles<E, F, R>(element: E, f: F) -> R where E: TElement, F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,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,
101 {
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());
105
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 }
114
115 f(
116 parent_style.map(|x| &**x),
117 layout_parent_style.map(|s| &**s),
118 )
119 }
120
layout_parent_style_for_pseudo<'a>( primary_style: &'a PrimaryStyle, layout_parent_style: Option<&'a ComputedValues>, ) -> Option<&'a ComputedValues>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 }
130 }
131
eager_pseudo_is_definitely_not_generated( pseudo: &PseudoElement, style: &ComputedValues, ) -> bool132 fn eager_pseudo_is_definitely_not_generated(
133 pseudo: &PseudoElement,
134 style: &ComputedValues,
135 ) -> bool {
136 use crate::computed_value_flags::ComputedValueFlags;
137
138 if !pseudo.is_before_or_after() {
139 return false;
140 }
141
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 }
149
150 if !style
151 .flags
152 .intersects(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE) &&
153 style.ineffective_content_property()
154 {
155 return true;
156 }
157
158 false
159 }
160
161 impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
162 where
163 'ctx: 'a,
164 'le: 'ctx,
165 E: TElement + MatchMethods + 'le,
166 {
167 /// Trivially construct a new StyleResolverForElement.
new( element: E, context: &'a mut StyleContext<'ctx, E>, rule_inclusion: RuleInclusion, pseudo_resolution: PseudoElementResolution, ) -> Self168 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 }
182
183 /// Resolve just the style of a given element.
resolve_primary_style( &mut self, parent_style: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>, ) -> PrimaryStyle184 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);
190
191 let inside_link = parent_style.map_or(false, |s| s.visited_style().is_some());
192
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 };
202
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 }
212
cascade_primary_style( &mut self, inputs: CascadeInputs, parent_style: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>, ) -> PrimaryStyle213 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();
224
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 }
239
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 }
252
253 /// Resolve the style of a given element, and all its eager pseudo-elements.
resolve_style( &mut self, parent_style: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>, ) -> ResolvedElementStyles254 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);
260
261 let mut pseudo_styles = EagerPseudoStyles::default();
262
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 );
272
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 }
283
284 ResolvedElementStyles {
285 primary: primary_style,
286 pseudos: pseudo_styles,
287 }
288 }
289
290 /// Resolve an element's styles with the default inheritance parent/layout
291 /// parents.
resolve_style_with_default_parents(&mut self) -> ResolvedElementStyles292 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 }
297
298 /// Cascade a set of rules, using the default parent for inheritance.
cascade_style_and_visited_with_default_parents( &mut self, inputs: CascadeInputs, ) -> ResolvedStyle299 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 }
312
313 /// Cascade a set of rules for pseudo element, using the default parent for inheritance.
cascade_style_and_visited_for_pseudo_with_default_parents( &mut self, inputs: CascadeInputs, pseudo: &PseudoElement, primary_style: &PrimaryStyle, ) -> ResolvedStyle314 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);
323
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 }
332
cascade_style_and_visited( &mut self, inputs: CascadeInputs, parent_style: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>, pseudo: Option<&PseudoElement>, ) -> ResolvedStyle333 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()));
341
342 let implemented_pseudo = self.element.implemented_pseudo_element();
343 let pseudo = pseudo.or(implemented_pseudo.as_ref());
344
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 );
358
359 self.context.thread_local.rule_cache.insert_if_possible(
360 &self.context.shared.guards,
361 &values,
362 pseudo,
363 &conditions,
364 );
365
366 ResolvedStyle(values)
367 }
368
369 /// Cascade the element and pseudo-element styles with the default parents.
cascade_styles_with_default_parents( &mut self, inputs: ElementCascadeInputs, ) -> ResolvedElementStyles370 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);
377
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 };
386
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);
390
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 );
397
398 if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
399 eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
400 {
401 continue;
402 }
403
404 pseudo_styles.set(&pseudo, style.0);
405 }
406 }
407 }
408
409 ResolvedElementStyles {
410 primary: primary_style,
411 pseudos: pseudo_styles,
412 }
413 })
414 }
415
resolve_pseudo_style( &mut self, pseudo: &PseudoElement, originating_element_style: &PrimaryStyle, layout_parent_style: Option<&ComputedValues>, ) -> Option<ResolvedStyle>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 )?;
427
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 }
436
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 }
447
match_primary(&mut self, visited_handling: VisitedHandlingMode) -> MatchingResults448 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();
454
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 );
465
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 };
473
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 }
487
488 // FIXME(emilio): This is a hack for animations, and should go away.
489 self.element.unset_dirty_style_attribute();
490
491 let rule_node = stylist
492 .rule_tree()
493 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
494
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 }
504
505 MatchingResults { rule_node }
506 }
507
match_pseudo( &mut self, originating_element_style: &ComputedValues, pseudo_element: &PseudoElement, visited_handling: VisitedHandlingMode, ) -> Option<StrongRuleNode>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 );
523
524 let mut applicable_declarations = ApplicableDeclarationList::new();
525
526 let stylist = &self.context.shared.stylist;
527
528 if !self
529 .element
530 .may_generate_pseudo(pseudo_element, originating_element_style)
531 {
532 return None;
533 }
534
535 let bloom_filter = self.context.thread_local.bloom_filter.filter();
536 let nth_index_cache = &mut self.context.thread_local.nth_index_cache;
537
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 );
545
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 };
551
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 );
565
566 if applicable_declarations.is_empty() {
567 return None;
568 }
569
570 let rule_node = stylist
571 .rule_tree()
572 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
573
574 Some(rule_node)
575 }
576 }
577