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
122pub(crate) fn 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 &Default::default(),
393 Some(&self.context.thread_local.rule_cache),
394 &mut conditions,
395 );
396
397 self.context.thread_local.rule_cache.insert_if_possible(
398 &self.context.shared.guards,
399 &values,
400 pseudo,
401 &conditions,
402 );
403
404 ResolvedStyle(values)
405 }
406
407 pub fn cascade_styles_with_default_parents(
409 &mut self,
410 inputs: ElementCascadeInputs,
411 may_have_starting_style: bool,
412 ) -> ResolvedElementStyles {
413 with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
414 let primary_style = self.cascade_primary_style(
415 inputs.primary,
416 parent_style,
417 layout_parent_style,
418 IncludeStartingStyle::No,
419 may_have_starting_style,
420 );
421
422 let mut pseudo_styles = EagerPseudoStyles::default();
423 if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
424 let layout_parent_style_for_pseudo = if primary_style.style().is_display_contents()
425 {
426 layout_parent_style
427 } else {
428 Some(primary_style.style())
429 };
430
431 for (i, inputs) in pseudo_array.iter_mut().enumerate() {
432 if let Some(inputs) = inputs.take() {
433 let pseudo = PseudoElement::from_eager_index(i);
434
435 let style = self.cascade_style_and_visited(
436 inputs,
437 Some(primary_style.style()),
438 layout_parent_style_for_pseudo,
439 Some(&pseudo),
440 );
441
442 if !matches!(self.pseudo_resolution, PseudoElementResolution::Force)
443 && eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
444 {
445 continue;
446 }
447
448 pseudo_styles.set(&pseudo, style.0);
449 }
450 }
451 }
452
453 ResolvedElementStyles {
454 primary: primary_style,
455 pseudos: pseudo_styles,
456 }
457 })
458 }
459
460 fn resolve_pseudo_style(
461 &mut self,
462 pseudo: &PseudoElement,
463 originating_element_style: &PrimaryStyle,
464 layout_parent_style: Option<&ComputedValues>,
465 ) -> Option<ResolvedStyle> {
466 let MatchingResults {
467 rule_node,
468 mut flags,
469 has_starting_style: _,
470 } = self.match_pseudo(
471 &originating_element_style.style.0,
472 pseudo,
473 VisitedHandlingMode::AllLinksUnvisited,
474 )?;
475
476 let mut visited_rules = None;
477 if originating_element_style.style().visited_style().is_some() {
478 visited_rules = self
479 .match_pseudo(
480 &originating_element_style.style.0,
481 pseudo,
482 VisitedHandlingMode::RelevantLinkVisited,
483 )
484 .map(|results| {
485 flags |= results.flags;
486 results.rule_node
487 });
488 }
489
490 Some(self.cascade_style_and_visited(
491 CascadeInputs {
492 rules: Some(rule_node),
493 visited_rules,
494 flags,
495 },
496 Some(originating_element_style.style()),
497 layout_parent_style,
498 Some(pseudo),
499 ))
500 }
501
502 fn match_primary(
503 &mut self,
504 visited_handling: VisitedHandlingMode,
505 include_starting_style: IncludeStartingStyle,
506 ) -> MatchingResults {
507 debug!(
508 "Match primary for {:?}, visited: {:?}",
509 self.element, visited_handling
510 );
511 let mut applicable_declarations = ApplicableDeclarationList::new();
512
513 let bloom_filter = self.context.thread_local.bloom_filter.filter();
514 let selector_caches = &mut self.context.thread_local.selector_caches;
515 let mut matching_context = MatchingContext::new_for_visited(
516 MatchingMode::Normal,
517 Some(bloom_filter),
518 selector_caches,
519 visited_handling,
520 include_starting_style,
521 self.context.shared.quirks_mode(),
522 NeedsSelectorFlags::Yes,
523 MatchingForInvalidation::No,
524 );
525
526 let stylist = &self.context.shared.stylist;
527 stylist.push_applicable_declarations(
529 self.element,
530 None,
531 self.element.style_attribute(),
532 self.element.smil_override(),
533 self.element.animation_declarations(self.context.shared),
534 self.rule_inclusion,
535 &mut applicable_declarations,
536 &mut matching_context,
537 );
538
539 self.element.unset_dirty_style_attribute();
541
542 let rule_node = stylist
543 .rule_tree()
544 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
545
546 if log_enabled!(Trace) {
547 trace!("Matched rules for {:?}:", self.element);
548 for rn in rule_node.self_and_ancestors() {
549 let source = rn.style_source();
550 if source.is_some() {
551 trace!(" > {:?}", source);
552 }
553 }
554 }
555
556 MatchingResults {
557 rule_node,
558 flags: matching_context.extra_data.cascade_input_flags,
559 has_starting_style: matching_context.has_starting_style,
560 }
561 }
562
563 fn match_pseudo(
564 &mut self,
565 originating_element_style: &ComputedValues,
566 pseudo_element: &PseudoElement,
567 visited_handling: VisitedHandlingMode,
568 ) -> Option<MatchingResults> {
569 debug!(
570 "Match pseudo {:?} for {:?}, visited: {:?}",
571 self.element, pseudo_element, visited_handling
572 );
573 debug_assert!(pseudo_element.is_eager());
574
575 let mut applicable_declarations = ApplicableDeclarationList::new();
576
577 let stylist = &self.context.shared.stylist;
578
579 if !self
580 .element
581 .may_generate_pseudo(pseudo_element, originating_element_style)
582 {
583 return None;
584 }
585
586 let bloom_filter = self.context.thread_local.bloom_filter.filter();
587 let selector_caches = &mut self.context.thread_local.selector_caches;
588
589 let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
590 MatchingMode::ForStatelessPseudoElement,
591 Some(bloom_filter),
592 selector_caches,
593 visited_handling,
594 IncludeStartingStyle::No,
595 self.context.shared.quirks_mode(),
596 NeedsSelectorFlags::Yes,
597 MatchingForInvalidation::No,
598 );
599 matching_context.extra_data.originating_element_style = Some(originating_element_style);
600
601 stylist.push_applicable_declarations(
604 self.element,
605 Some(pseudo_element),
606 None,
607 None,
608 Default::default(),
609 self.rule_inclusion,
610 &mut applicable_declarations,
611 &mut matching_context,
612 );
613
614 if applicable_declarations.is_empty() {
615 return None;
616 }
617
618 let rule_node = stylist
619 .rule_tree()
620 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
621
622 Some(MatchingResults {
623 rule_node,
624 flags: matching_context.extra_data.cascade_input_flags,
625 has_starting_style: false, })
627 }
628
629 pub fn resolve_starting_style(&mut self) -> PrimaryStyle {
631 let parent_el = self.element.inheritance_parent();
635 let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
636 let parent_style = parent_data.as_ref().map(|d| d.styles.primary());
637 let parent_after_change_style = parent_style.and_then(|s| self.after_change_style(s));
638 let parent_values = parent_after_change_style
639 .as_ref()
640 .or(parent_style)
641 .map(|x| &**x);
642
643 let mut layout_parent_el = parent_el.clone();
644 let layout_parent_data;
645 let layout_parent_after_change_style;
646 let layout_parent_values = if parent_style.map_or(false, |s| s.is_display_contents()) {
647 layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
648 layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
649 let layout_parent_style = Some(layout_parent_data.styles.primary());
650 layout_parent_after_change_style =
651 layout_parent_style.and_then(|s| self.after_change_style(s));
652 layout_parent_after_change_style
653 .as_ref()
654 .or(layout_parent_style)
655 .map(|x| &**x)
656 } else {
657 parent_values
658 };
659
660 self.resolve_primary_style(
661 parent_values,
662 layout_parent_values,
663 IncludeStartingStyle::Yes,
664 )
665 }
666
667 pub fn after_change_style(
669 &mut self,
670 primary_style: &Arc<ComputedValues>,
671 ) -> Option<Arc<ComputedValues>> {
672 let rule_node = primary_style.rules();
673 let without_transition_rules = self
674 .context
675 .shared
676 .stylist
677 .rule_tree()
678 .remove_transition_rule_if_applicable(rule_node);
679 if without_transition_rules == *rule_node {
680 return None;
683 }
684
685 let inputs = CascadeInputs {
688 rules: Some(without_transition_rules),
689 visited_rules: primary_style.visited_rules().cloned(),
690 flags: primary_style.flags.for_cascade_inputs(),
691 };
692
693 let style = self.cascade_style_and_visited_with_default_parents(inputs);
694 Some(style.0)
695 }
696}