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.element.implemented_pseudo_element().is_some_and(|p| !PseudoElementTrait::is_element_backed(&p)) {
301 let layout_parent_style_for_pseudo =
302 layout_parent_style_for_pseudo(&primary_style, layout_parent_style);
303 SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
304 let pseudo_style = self.resolve_pseudo_style(
305 &pseudo,
306 &primary_style,
307 layout_parent_style_for_pseudo,
308 );
309
310 if let Some(style) = pseudo_style {
311 if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
312 eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
313 {
314 return;
315 }
316 pseudo_styles.set(&pseudo, style.0);
317 }
318 })
319 }
320
321 ResolvedElementStyles {
322 primary: primary_style,
323 pseudos: pseudo_styles,
324 }
325 }
326
327 pub fn resolve_style_with_default_parents(&mut self) -> ResolvedElementStyles {
330 with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
331 self.resolve_style(parent_style, layout_parent_style)
332 })
333 }
334
335 pub fn cascade_style_and_visited_with_default_parents(
337 &mut self,
338 inputs: CascadeInputs,
339 ) -> ResolvedStyle {
340 with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
341 self.cascade_style_and_visited(
342 inputs,
343 parent_style,
344 layout_parent_style,
345 None,
346 )
347 })
348 }
349
350 pub fn cascade_style_and_visited_for_pseudo_with_default_parents(
352 &mut self,
353 inputs: CascadeInputs,
354 pseudo: &PseudoElement,
355 primary_style: &PrimaryStyle,
356 ) -> ResolvedStyle {
357 with_default_parent_styles(self.element, |_, layout_parent_style| {
358 let layout_parent_style_for_pseudo =
359 layout_parent_style_for_pseudo(primary_style, layout_parent_style);
360
361 self.cascade_style_and_visited(
362 inputs,
363 Some(primary_style.style()),
364 layout_parent_style_for_pseudo,
365 Some(pseudo),
366 )
367 })
368 }
369
370 fn cascade_style_and_visited(
371 &mut self,
372 inputs: CascadeInputs,
373 parent_style: Option<&ComputedValues>,
374 layout_parent_style: Option<&ComputedValues>,
375 pseudo: Option<&PseudoElement>,
376 ) -> ResolvedStyle {
377 debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
378
379 let mut conditions = Default::default();
380 let values = self.context.shared.stylist.cascade_style_and_visited(
381 Some(self.element),
382 pseudo,
383 inputs,
384 &self.context.shared.guards,
385 parent_style,
386 layout_parent_style,
387 FirstLineReparenting::No,
388 Some(&self.context.thread_local.rule_cache),
389 &mut conditions,
390 );
391
392 self.context.thread_local.rule_cache.insert_if_possible(
393 &self.context.shared.guards,
394 &values,
395 pseudo,
396 &conditions,
397 );
398
399 ResolvedStyle(values)
400 }
401
402 pub fn cascade_styles_with_default_parents(
404 &mut self,
405 inputs: ElementCascadeInputs,
406 may_have_starting_style: bool,
407 ) -> ResolvedElementStyles {
408 with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
409 let primary_style = self.cascade_primary_style(
410 inputs.primary,
411 parent_style,
412 layout_parent_style,
413 IncludeStartingStyle::No,
414 may_have_starting_style,
415 );
416
417 let mut pseudo_styles = EagerPseudoStyles::default();
418 if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
419 let layout_parent_style_for_pseudo = if primary_style.style().is_display_contents()
420 {
421 layout_parent_style
422 } else {
423 Some(primary_style.style())
424 };
425
426 for (i, inputs) in pseudo_array.iter_mut().enumerate() {
427 if let Some(inputs) = inputs.take() {
428 let pseudo = PseudoElement::from_eager_index(i);
429
430 let style = self.cascade_style_and_visited(
431 inputs,
432 Some(primary_style.style()),
433 layout_parent_style_for_pseudo,
434 Some(&pseudo),
435 );
436
437 if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
438 eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
439 {
440 continue;
441 }
442
443 pseudo_styles.set(&pseudo, style.0);
444 }
445 }
446 }
447
448 ResolvedElementStyles {
449 primary: primary_style,
450 pseudos: pseudo_styles,
451 }
452 })
453 }
454
455 fn resolve_pseudo_style(
456 &mut self,
457 pseudo: &PseudoElement,
458 originating_element_style: &PrimaryStyle,
459 layout_parent_style: Option<&ComputedValues>,
460 ) -> Option<ResolvedStyle> {
461 let MatchingResults {
462 rule_node,
463 mut flags,
464 has_starting_style: _,
465 } = self.match_pseudo(
466 &originating_element_style.style.0,
467 pseudo,
468 VisitedHandlingMode::AllLinksUnvisited,
469 )?;
470
471 let mut visited_rules = None;
472 if originating_element_style.style().visited_style().is_some() {
473 visited_rules = self
474 .match_pseudo(
475 &originating_element_style.style.0,
476 pseudo,
477 VisitedHandlingMode::RelevantLinkVisited,
478 )
479 .map(|results| {
480 flags |= results.flags;
481 results.rule_node
482 });
483 }
484
485 Some(self.cascade_style_and_visited(
486 CascadeInputs {
487 rules: Some(rule_node),
488 visited_rules,
489 flags,
490 },
491 Some(originating_element_style.style()),
492 layout_parent_style,
493 Some(pseudo),
494 ))
495 }
496
497 fn match_primary(
498 &mut self,
499 visited_handling: VisitedHandlingMode,
500 include_starting_style: IncludeStartingStyle,
501 ) -> MatchingResults {
502 debug!(
503 "Match primary for {:?}, visited: {:?}",
504 self.element, visited_handling
505 );
506 let mut applicable_declarations = ApplicableDeclarationList::new();
507
508 let bloom_filter = self.context.thread_local.bloom_filter.filter();
509 let selector_caches = &mut self.context.thread_local.selector_caches;
510 let mut matching_context = MatchingContext::new_for_visited(
511 MatchingMode::Normal,
512 Some(bloom_filter),
513 selector_caches,
514 visited_handling,
515 include_starting_style,
516 self.context.shared.quirks_mode(),
517 NeedsSelectorFlags::Yes,
518 MatchingForInvalidation::No,
519 );
520
521 let stylist = &self.context.shared.stylist;
522 stylist.push_applicable_declarations(
524 self.element,
525 None,
526 self.element.style_attribute(),
527 self.element.smil_override(),
528 self.element.animation_declarations(self.context.shared),
529 self.rule_inclusion,
530 &mut applicable_declarations,
531 &mut matching_context,
532 );
533
534 self.element.unset_dirty_style_attribute();
536
537 let rule_node = stylist
538 .rule_tree()
539 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
540
541 if log_enabled!(Trace) {
542 trace!("Matched rules for {:?}:", self.element);
543 for rn in rule_node.self_and_ancestors() {
544 let source = rn.style_source();
545 if source.is_some() {
546 trace!(" > {:?}", source);
547 }
548 }
549 }
550
551 MatchingResults {
552 rule_node,
553 flags: matching_context.extra_data.cascade_input_flags,
554 has_starting_style: matching_context.has_starting_style,
555 }
556 }
557
558 fn match_pseudo(
559 &mut self,
560 originating_element_style: &ComputedValues,
561 pseudo_element: &PseudoElement,
562 visited_handling: VisitedHandlingMode,
563 ) -> Option<MatchingResults> {
564 debug!(
565 "Match pseudo {:?} for {:?}, visited: {:?}",
566 self.element, pseudo_element, visited_handling
567 );
568 debug_assert!(pseudo_element.is_eager());
569
570 let mut applicable_declarations = ApplicableDeclarationList::new();
571
572 let stylist = &self.context.shared.stylist;
573
574 if !self
575 .element
576 .may_generate_pseudo(pseudo_element, originating_element_style)
577 {
578 return None;
579 }
580
581 let bloom_filter = self.context.thread_local.bloom_filter.filter();
582 let selector_caches = &mut self.context.thread_local.selector_caches;
583
584 let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
585 MatchingMode::ForStatelessPseudoElement,
586 Some(bloom_filter),
587 selector_caches,
588 visited_handling,
589 IncludeStartingStyle::No,
590 self.context.shared.quirks_mode(),
591 NeedsSelectorFlags::Yes,
592 MatchingForInvalidation::No,
593 );
594 matching_context.extra_data.originating_element_style = Some(originating_element_style);
595
596 stylist.push_applicable_declarations(
599 self.element,
600 Some(pseudo_element),
601 None,
602 None,
603 Default::default(),
604 self.rule_inclusion,
605 &mut applicable_declarations,
606 &mut matching_context,
607 );
608
609 if applicable_declarations.is_empty() {
610 return None;
611 }
612
613 let rule_node = stylist
614 .rule_tree()
615 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
616
617 Some(MatchingResults {
618 rule_node,
619 flags: matching_context.extra_data.cascade_input_flags,
620 has_starting_style: false, })
622 }
623
624 pub fn resolve_starting_style(&mut self) -> PrimaryStyle {
626 let parent_el = self.element.inheritance_parent();
630 let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
631 let parent_style = parent_data.as_ref().map(|d| d.styles.primary());
632 let parent_after_change_style =
633 parent_style.and_then(|s| self.after_change_style(s));
634 let parent_values = parent_after_change_style
635 .as_ref()
636 .or(parent_style)
637 .map(|x| &**x);
638
639 let mut layout_parent_el = parent_el.clone();
640 let layout_parent_data;
641 let layout_parent_after_change_style;
642 let layout_parent_values = if parent_style.map_or(false, |s| s.is_display_contents()) {
643 layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
644 layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
645 let layout_parent_style = Some(layout_parent_data.styles.primary());
646 layout_parent_after_change_style =
647 layout_parent_style.and_then(|s| self.after_change_style(s));
648 layout_parent_after_change_style
649 .as_ref()
650 .or(layout_parent_style)
651 .map(|x| &**x)
652 } else {
653 parent_values
654 };
655
656 self.resolve_primary_style(
657 parent_values,
658 layout_parent_values,
659 IncludeStartingStyle::Yes,
660 )
661 }
662
663 pub fn after_change_style(
665 &mut self,
666 primary_style: &Arc<ComputedValues>,
667 ) -> Option<Arc<ComputedValues>> {
668 let rule_node = primary_style.rules();
669 let without_transition_rules = self.context
670 .shared
671 .stylist
672 .rule_tree()
673 .remove_transition_rule_if_applicable(rule_node);
674 if without_transition_rules == *rule_node {
675 return None;
678 }
679
680 let inputs = CascadeInputs {
683 rules: Some(without_transition_rules),
684 visited_rules: primary_style.visited_rules().cloned(),
685 flags: primary_style.flags.for_cascade_inputs(),
686 };
687
688 let style = self.cascade_style_and_visited_with_default_parents(inputs);
689 Some(style.0)
690 }
691}