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