1use crate::applicable_declarations::ApplicableDeclarationList;
8use crate::computed_value_flags::ComputedValueFlags;
9use crate::context::{CascadeInputs, ElementCascadeInputs, StyleContext};
10use crate::data::{EagerPseudoStyles, ElementStyles};
11use crate::dom::TElement;
12use crate::matching::MatchMethods;
13use crate::properties::longhands::display::computed_value::T as Display;
14use crate::properties::{ComputedValues, FirstLineReparenting};
15use crate::rule_tree::StrongRuleNode;
16use crate::selector_parser::{PseudoElement, SelectorImpl};
17use crate::stylist::RuleInclusion;
18use log::Level::Trace;
19use selectors::matching::{
20 IncludeStartingStyle, MatchingContext, MatchingForInvalidation, MatchingMode,
21 NeedsSelectorFlags, VisitedHandlingMode,
22};
23use selectors::parser::PseudoElement as PseudoElementTrait;
24use servo_arc::Arc;
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub enum PseudoElementResolution {
29 IfApplicable,
31 Force,
33}
34
35pub struct StyleResolverForElement<'a, 'ctx, 'le, E>
37where
38 'ctx: 'a,
39 'le: 'ctx,
40 E: TElement + MatchMethods + 'le,
41{
42 element: E,
43 context: &'a mut StyleContext<'ctx, E>,
44 rule_inclusion: RuleInclusion,
45 pseudo_resolution: PseudoElementResolution,
46 _marker: ::std::marker::PhantomData<&'le E>,
47}
48
49struct MatchingResults {
50 rule_node: StrongRuleNode,
51 flags: ComputedValueFlags,
52 has_starting_style: bool,
53}
54
55pub struct ResolvedStyle(pub Arc<ComputedValues>);
57
58impl ResolvedStyle {
59 #[inline]
61 pub fn style(&self) -> &ComputedValues {
62 &*self.0
63 }
64}
65
66pub struct PrimaryStyle {
68 pub style: ResolvedStyle,
70 pub reused_via_rule_node: bool,
73 pub may_have_starting_style: bool,
78}
79
80pub struct ResolvedElementStyles {
82 pub primary: PrimaryStyle,
84 pub pseudos: EagerPseudoStyles,
86}
87
88impl ResolvedElementStyles {
89 pub fn primary_style(&self) -> &Arc<ComputedValues> {
91 &self.primary.style.0
92 }
93
94 pub fn primary_style_mut(&mut self) -> &mut Arc<ComputedValues> {
96 &mut self.primary.style.0
97 }
98
99 #[inline]
101 pub fn may_have_starting_style(&self) -> bool {
102 self.primary.may_have_starting_style
103 }
104}
105
106impl PrimaryStyle {
107 pub fn style(&self) -> &ComputedValues {
109 &*self.style.0
110 }
111}
112
113impl From<ResolvedElementStyles> for ElementStyles {
114 fn from(r: ResolvedElementStyles) -> ElementStyles {
115 ElementStyles {
116 primary: Some(r.primary.style.0),
117 pseudos: r.pseudos,
118 }
119 }
120}
121
122fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
123where
124 E: TElement,
125 F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,
126{
127 let parent_el = element.inheritance_parent();
128 let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
129 let parent_style = parent_data.as_ref().map(|d| d.styles.primary());
130
131 let mut layout_parent_el = parent_el.clone();
132 let layout_parent_data;
133 let mut layout_parent_style = parent_style;
134 if parent_style.map_or(false, |s| s.is_display_contents()) {
135 layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
136 layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
137 layout_parent_style = Some(layout_parent_data.styles.primary());
138 }
139
140 f(
141 parent_style.map(|x| &**x),
142 layout_parent_style.map(|s| &**s),
143 )
144}
145
146fn layout_parent_style_for_pseudo<'a>(
147 primary_style: &'a PrimaryStyle,
148 layout_parent_style: Option<&'a ComputedValues>,
149) -> Option<&'a ComputedValues> {
150 if primary_style.style().is_display_contents() {
151 layout_parent_style
152 } else {
153 Some(primary_style.style())
154 }
155}
156
157fn eager_pseudo_is_definitely_not_generated(
158 pseudo: &PseudoElement,
159 style: &ComputedValues,
160) -> bool {
161 if !pseudo.is_before_or_after() {
162 return false;
163 }
164
165 if !style
166 .flags
167 .intersects(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE)
168 && style.get_box().clone_display() == Display::None
169 {
170 return true;
171 }
172
173 if !style
174 .flags
175 .intersects(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE)
176 && style.ineffective_content_property()
177 {
178 return true;
179 }
180
181 false
182}
183
184impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
185where
186 'ctx: 'a,
187 'le: 'ctx,
188 E: TElement + MatchMethods + 'le,
189{
190 pub fn new(
192 element: E,
193 context: &'a mut StyleContext<'ctx, E>,
194 rule_inclusion: RuleInclusion,
195 pseudo_resolution: PseudoElementResolution,
196 ) -> Self {
197 Self {
198 element,
199 context,
200 rule_inclusion,
201 pseudo_resolution,
202 _marker: ::std::marker::PhantomData,
203 }
204 }
205
206 pub fn resolve_primary_style(
208 &mut self,
209 parent_style: Option<&ComputedValues>,
210 layout_parent_style: Option<&ComputedValues>,
211 include_starting_style: IncludeStartingStyle,
212 ) -> PrimaryStyle {
213 let primary_results = self.match_primary(
214 VisitedHandlingMode::AllLinksUnvisited,
215 include_starting_style,
216 );
217
218 let inside_link = parent_style.map_or(false, |s| s.visited_style().is_some());
219
220 let visited_rules = if self.context.shared.visited_styles_enabled
221 && (inside_link || self.element.is_link())
222 {
223 let visited_matching_results = self.match_primary(
224 VisitedHandlingMode::RelevantLinkVisited,
225 IncludeStartingStyle::No,
226 );
227 Some(visited_matching_results.rule_node)
228 } else {
229 None
230 };
231
232 self.cascade_primary_style(
233 CascadeInputs {
234 rules: Some(primary_results.rule_node),
235 visited_rules,
236 flags: primary_results.flags,
237 },
238 parent_style,
239 layout_parent_style,
240 include_starting_style,
241 primary_results.has_starting_style,
242 )
243 }
244
245 fn cascade_primary_style(
246 &mut self,
247 inputs: CascadeInputs,
248 parent_style: Option<&ComputedValues>,
249 layout_parent_style: Option<&ComputedValues>,
250 include_starting_style: IncludeStartingStyle,
251 may_have_starting_style: bool,
252 ) -> PrimaryStyle {
253 let may_reuse = self.element.matches_user_and_content_rules()
256 && parent_style.is_some()
257 && inputs.rules.is_some()
258 && include_starting_style == IncludeStartingStyle::No;
259
260 if may_reuse {
261 let cached = self.context.thread_local.sharing_cache.lookup_by_rules(
262 self.context.shared,
263 parent_style.unwrap(),
264 inputs.rules.as_ref().unwrap(),
265 inputs.visited_rules.as_ref(),
266 self.element,
267 );
268 if let Some(mut primary_style) = cached {
269 self.context.thread_local.statistics.styles_reused += 1;
270 primary_style.reused_via_rule_node |= true;
271 return primary_style;
272 }
273 }
274
275 PrimaryStyle {
278 style: self.cascade_style_and_visited(
279 inputs,
280 parent_style,
281 layout_parent_style,
282 None,
283 ),
284 reused_via_rule_node: false,
285 may_have_starting_style,
286 }
287 }
288
289 pub fn resolve_style(
291 &mut self,
292 parent_style: Option<&ComputedValues>,
293 layout_parent_style: Option<&ComputedValues>,
294 ) -> ResolvedElementStyles {
295 let primary_style =
296 self.resolve_primary_style(parent_style, layout_parent_style, IncludeStartingStyle::No);
297
298 let mut pseudo_styles = EagerPseudoStyles::default();
299
300 if !self
301 .element
302 .implemented_pseudo_element()
303 .is_some_and(|p| !PseudoElementTrait::is_element_backed(&p))
304 {
305 let layout_parent_style_for_pseudo =
306 layout_parent_style_for_pseudo(&primary_style, layout_parent_style);
307 SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
308 let pseudo_style = self.resolve_pseudo_style(
309 &pseudo,
310 &primary_style,
311 layout_parent_style_for_pseudo,
312 );
313
314 if let Some(style) = pseudo_style {
315 if !matches!(self.pseudo_resolution, PseudoElementResolution::Force)
316 && eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
317 {
318 return;
319 }
320 pseudo_styles.set(&pseudo, style.0);
321 }
322 })
323 }
324
325 ResolvedElementStyles {
326 primary: primary_style,
327 pseudos: pseudo_styles,
328 }
329 }
330
331 pub fn resolve_style_with_default_parents(&mut self) -> ResolvedElementStyles {
334 with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
335 self.resolve_style(parent_style, layout_parent_style)
336 })
337 }
338
339 pub fn cascade_style_and_visited_with_default_parents(
341 &mut self,
342 inputs: CascadeInputs,
343 ) -> ResolvedStyle {
344 with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
345 self.cascade_style_and_visited(
346 inputs,
347 parent_style,
348 layout_parent_style,
349 None,
350 )
351 })
352 }
353
354 pub fn cascade_style_and_visited_for_pseudo_with_default_parents(
356 &mut self,
357 inputs: CascadeInputs,
358 pseudo: &PseudoElement,
359 primary_style: &PrimaryStyle,
360 ) -> ResolvedStyle {
361 with_default_parent_styles(self.element, |_, layout_parent_style| {
362 let layout_parent_style_for_pseudo =
363 layout_parent_style_for_pseudo(primary_style, layout_parent_style);
364
365 self.cascade_style_and_visited(
366 inputs,
367 Some(primary_style.style()),
368 layout_parent_style_for_pseudo,
369 Some(pseudo),
370 )
371 })
372 }
373
374 fn cascade_style_and_visited(
375 &mut self,
376 inputs: CascadeInputs,
377 parent_style: Option<&ComputedValues>,
378 layout_parent_style: Option<&ComputedValues>,
379 pseudo: Option<&PseudoElement>,
380 ) -> ResolvedStyle {
381 debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
382
383 let mut conditions = Default::default();
384 let values = self.context.shared.stylist.cascade_style_and_visited(
385 Some(self.element),
386 pseudo,
387 inputs,
388 &self.context.shared.guards,
389 parent_style,
390 layout_parent_style,
391 FirstLineReparenting::No,
392 Some(&self.context.thread_local.rule_cache),
393 &mut conditions,
394 );
395
396 self.context.thread_local.rule_cache.insert_if_possible(
397 &self.context.shared.guards,
398 &values,
399 pseudo,
400 &conditions,
401 );
402
403 ResolvedStyle(values)
404 }
405
406 pub fn cascade_styles_with_default_parents(
408 &mut self,
409 inputs: ElementCascadeInputs,
410 may_have_starting_style: bool,
411 ) -> ResolvedElementStyles {
412 with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
413 let primary_style = self.cascade_primary_style(
414 inputs.primary,
415 parent_style,
416 layout_parent_style,
417 IncludeStartingStyle::No,
418 may_have_starting_style,
419 );
420
421 let mut pseudo_styles = EagerPseudoStyles::default();
422 if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
423 let layout_parent_style_for_pseudo = if primary_style.style().is_display_contents()
424 {
425 layout_parent_style
426 } else {
427 Some(primary_style.style())
428 };
429
430 for (i, inputs) in pseudo_array.iter_mut().enumerate() {
431 if let Some(inputs) = inputs.take() {
432 let pseudo = PseudoElement::from_eager_index(i);
433
434 let style = self.cascade_style_and_visited(
435 inputs,
436 Some(primary_style.style()),
437 layout_parent_style_for_pseudo,
438 Some(&pseudo),
439 );
440
441 if !matches!(self.pseudo_resolution, PseudoElementResolution::Force)
442 && eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
443 {
444 continue;
445 }
446
447 pseudo_styles.set(&pseudo, style.0);
448 }
449 }
450 }
451
452 ResolvedElementStyles {
453 primary: primary_style,
454 pseudos: pseudo_styles,
455 }
456 })
457 }
458
459 fn resolve_pseudo_style(
460 &mut self,
461 pseudo: &PseudoElement,
462 originating_element_style: &PrimaryStyle,
463 layout_parent_style: Option<&ComputedValues>,
464 ) -> Option<ResolvedStyle> {
465 let MatchingResults {
466 rule_node,
467 mut flags,
468 has_starting_style: _,
469 } = self.match_pseudo(
470 &originating_element_style.style.0,
471 pseudo,
472 VisitedHandlingMode::AllLinksUnvisited,
473 )?;
474
475 let mut visited_rules = None;
476 if originating_element_style.style().visited_style().is_some() {
477 visited_rules = self
478 .match_pseudo(
479 &originating_element_style.style.0,
480 pseudo,
481 VisitedHandlingMode::RelevantLinkVisited,
482 )
483 .map(|results| {
484 flags |= results.flags;
485 results.rule_node
486 });
487 }
488
489 Some(self.cascade_style_and_visited(
490 CascadeInputs {
491 rules: Some(rule_node),
492 visited_rules,
493 flags,
494 },
495 Some(originating_element_style.style()),
496 layout_parent_style,
497 Some(pseudo),
498 ))
499 }
500
501 fn match_primary(
502 &mut self,
503 visited_handling: VisitedHandlingMode,
504 include_starting_style: IncludeStartingStyle,
505 ) -> MatchingResults {
506 debug!(
507 "Match primary for {:?}, visited: {:?}",
508 self.element, visited_handling
509 );
510 let mut applicable_declarations = ApplicableDeclarationList::new();
511
512 let bloom_filter = self.context.thread_local.bloom_filter.filter();
513 let selector_caches = &mut self.context.thread_local.selector_caches;
514 let mut matching_context = MatchingContext::new_for_visited(
515 MatchingMode::Normal,
516 Some(bloom_filter),
517 selector_caches,
518 visited_handling,
519 include_starting_style,
520 self.context.shared.quirks_mode(),
521 NeedsSelectorFlags::Yes,
522 MatchingForInvalidation::No,
523 );
524
525 let stylist = &self.context.shared.stylist;
526 stylist.push_applicable_declarations(
528 self.element,
529 None,
530 self.element.style_attribute(),
531 self.element.smil_override(),
532 self.element.animation_declarations(self.context.shared),
533 self.rule_inclusion,
534 &mut applicable_declarations,
535 &mut matching_context,
536 );
537
538 self.element.unset_dirty_style_attribute();
540
541 let rule_node = stylist
542 .rule_tree()
543 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
544
545 if log_enabled!(Trace) {
546 trace!("Matched rules for {:?}:", self.element);
547 for rn in rule_node.self_and_ancestors() {
548 let source = rn.style_source();
549 if source.is_some() {
550 trace!(" > {:?}", source);
551 }
552 }
553 }
554
555 MatchingResults {
556 rule_node,
557 flags: matching_context.extra_data.cascade_input_flags,
558 has_starting_style: matching_context.has_starting_style,
559 }
560 }
561
562 fn match_pseudo(
563 &mut self,
564 originating_element_style: &ComputedValues,
565 pseudo_element: &PseudoElement,
566 visited_handling: VisitedHandlingMode,
567 ) -> Option<MatchingResults> {
568 debug!(
569 "Match pseudo {:?} for {:?}, visited: {:?}",
570 self.element, pseudo_element, visited_handling
571 );
572 debug_assert!(pseudo_element.is_eager());
573
574 let mut applicable_declarations = ApplicableDeclarationList::new();
575
576 let stylist = &self.context.shared.stylist;
577
578 if !self
579 .element
580 .may_generate_pseudo(pseudo_element, originating_element_style)
581 {
582 return None;
583 }
584
585 let bloom_filter = self.context.thread_local.bloom_filter.filter();
586 let selector_caches = &mut self.context.thread_local.selector_caches;
587
588 let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
589 MatchingMode::ForStatelessPseudoElement,
590 Some(bloom_filter),
591 selector_caches,
592 visited_handling,
593 IncludeStartingStyle::No,
594 self.context.shared.quirks_mode(),
595 NeedsSelectorFlags::Yes,
596 MatchingForInvalidation::No,
597 );
598 matching_context.extra_data.originating_element_style = Some(originating_element_style);
599
600 stylist.push_applicable_declarations(
603 self.element,
604 Some(pseudo_element),
605 None,
606 None,
607 Default::default(),
608 self.rule_inclusion,
609 &mut applicable_declarations,
610 &mut matching_context,
611 );
612
613 if applicable_declarations.is_empty() {
614 return None;
615 }
616
617 let rule_node = stylist
618 .rule_tree()
619 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
620
621 Some(MatchingResults {
622 rule_node,
623 flags: matching_context.extra_data.cascade_input_flags,
624 has_starting_style: false, })
626 }
627
628 pub fn resolve_starting_style(&mut self) -> PrimaryStyle {
630 let parent_el = self.element.inheritance_parent();
634 let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
635 let parent_style = parent_data.as_ref().map(|d| d.styles.primary());
636 let parent_after_change_style = parent_style.and_then(|s| self.after_change_style(s));
637 let parent_values = parent_after_change_style
638 .as_ref()
639 .or(parent_style)
640 .map(|x| &**x);
641
642 let mut layout_parent_el = parent_el.clone();
643 let layout_parent_data;
644 let layout_parent_after_change_style;
645 let layout_parent_values = if parent_style.map_or(false, |s| s.is_display_contents()) {
646 layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
647 layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
648 let layout_parent_style = Some(layout_parent_data.styles.primary());
649 layout_parent_after_change_style =
650 layout_parent_style.and_then(|s| self.after_change_style(s));
651 layout_parent_after_change_style
652 .as_ref()
653 .or(layout_parent_style)
654 .map(|x| &**x)
655 } else {
656 parent_values
657 };
658
659 self.resolve_primary_style(
660 parent_values,
661 layout_parent_values,
662 IncludeStartingStyle::Yes,
663 )
664 }
665
666 pub fn after_change_style(
668 &mut self,
669 primary_style: &Arc<ComputedValues>,
670 ) -> Option<Arc<ComputedValues>> {
671 let rule_node = primary_style.rules();
672 let without_transition_rules = self
673 .context
674 .shared
675 .stylist
676 .rule_tree()
677 .remove_transition_rule_if_applicable(rule_node);
678 if without_transition_rules == *rule_node {
679 return None;
682 }
683
684 let inputs = CascadeInputs {
687 rules: Some(without_transition_rules),
688 visited_rules: primary_style.visited_rules().cloned(),
689 flags: primary_style.flags.for_cascade_inputs(),
690 };
691
692 let style = self.cascade_style_and_visited_with_default_parents(inputs);
693 Some(style.0)
694 }
695}