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::{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.clone();
120 let layout_parent_data;
121 let mut layout_parent_style = parent_style;
122 if parent_style.map_or(false, |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 Self {
177 element,
178 context,
179 rule_inclusion,
180 pseudo_resolution,
181 _marker: ::std::marker::PhantomData,
182 }
183 }
184
185 pub fn resolve_primary_style(
187 &mut self,
188 parent_style: Option<&ComputedValues>,
189 layout_parent_style: Option<&ComputedValues>,
190 ) -> PrimaryStyle {
191 let primary_results = self.match_primary(VisitedHandlingMode::AllLinksUnvisited);
192
193 let inside_link = parent_style.map_or(false, |s| s.visited_style().is_some());
194
195 let visited_rules = if self.context.shared.visited_styles_enabled
196 && (inside_link || self.element.is_link())
197 {
198 let visited_matching_results =
199 self.match_primary(VisitedHandlingMode::RelevantLinkVisited);
200 Some(visited_matching_results.rule_node)
201 } else {
202 None
203 };
204
205 self.cascade_primary_style(
206 CascadeInputs {
207 rules: Some(primary_results.rule_node),
208 visited_rules,
209 flags: primary_results.flags,
210 included_cascade_flags: RuleCascadeFlags::empty(),
211 },
212 parent_style,
213 layout_parent_style,
214 )
215 }
216
217 fn cascade_primary_style(
218 &mut self,
219 inputs: CascadeInputs,
220 parent_style: Option<&ComputedValues>,
221 layout_parent_style: Option<&ComputedValues>,
222 ) -> PrimaryStyle {
223 let may_reuse = self.element.matches_user_and_content_rules()
226 && parent_style.is_some()
227 && inputs.rules.is_some()
228 && inputs.included_cascade_flags.is_empty();
229
230 if may_reuse {
231 let cached = self.context.thread_local.sharing_cache.lookup_by_rules(
232 self.context.shared,
233 parent_style.unwrap(),
234 &inputs,
235 self.element,
236 );
237 if let Some(mut primary_style) = cached {
238 self.context.thread_local.statistics.styles_reused += 1;
239 primary_style.reused_via_rule_node |= true;
240 return primary_style;
241 }
242 }
243
244 PrimaryStyle {
247 style: self.cascade_style_and_visited(
248 inputs,
249 parent_style,
250 layout_parent_style,
251 None,
252 ),
253 reused_via_rule_node: false,
254 }
255 }
256
257 pub fn resolve_style(
259 &mut self,
260 parent_style: Option<&ComputedValues>,
261 layout_parent_style: Option<&ComputedValues>,
262 ) -> ResolvedElementStyles {
263 let primary_style = self.resolve_primary_style(parent_style, layout_parent_style);
264
265 let mut pseudo_styles = EagerPseudoStyles::default();
266
267 if !self
268 .element
269 .implemented_pseudo_element()
270 .is_some_and(|p| !p.is_element_backed())
271 {
272 let layout_parent_style_for_pseudo =
273 layout_parent_style_for_pseudo(&primary_style, layout_parent_style);
274 SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
275 let pseudo_style = self.resolve_pseudo_style(
276 &pseudo,
277 &primary_style,
278 layout_parent_style_for_pseudo,
279 );
280
281 if let Some(style) = pseudo_style {
282 if !matches!(self.pseudo_resolution, PseudoElementResolution::Force)
283 && eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
284 {
285 return;
286 }
287 pseudo_styles.set(&pseudo, style.0);
288 }
289 })
290 }
291
292 ResolvedElementStyles {
293 primary: primary_style,
294 pseudos: pseudo_styles,
295 }
296 }
297
298 pub fn resolve_style_with_default_parents(&mut self) -> ResolvedElementStyles {
301 with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
302 self.resolve_style(parent_style, layout_parent_style)
303 })
304 }
305
306 pub fn cascade_style_and_visited_with_default_parents(
308 &mut self,
309 inputs: CascadeInputs,
310 ) -> ResolvedStyle {
311 with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
312 self.cascade_style_and_visited(
313 inputs,
314 parent_style,
315 layout_parent_style,
316 None,
317 )
318 })
319 }
320
321 pub fn cascade_style_and_visited_for_pseudo_with_default_parents(
323 &mut self,
324 inputs: CascadeInputs,
325 pseudo: &PseudoElement,
326 primary_style: &PrimaryStyle,
327 ) -> ResolvedStyle {
328 with_default_parent_styles(self.element, |_, layout_parent_style| {
329 let layout_parent_style_for_pseudo =
330 layout_parent_style_for_pseudo(primary_style, layout_parent_style);
331
332 self.cascade_style_and_visited(
333 inputs,
334 Some(primary_style.style()),
335 layout_parent_style_for_pseudo,
336 Some(pseudo),
337 )
338 })
339 }
340
341 fn cascade_style_and_visited(
342 &mut self,
343 inputs: CascadeInputs,
344 parent_style: Option<&ComputedValues>,
345 layout_parent_style: Option<&ComputedValues>,
346 pseudo: Option<&PseudoElement>,
347 ) -> ResolvedStyle {
348 debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
349
350 let mut conditions = Default::default();
351 let values = self.context.shared.stylist.cascade_style_and_visited(
352 Some(self.element),
353 pseudo,
354 &inputs,
355 &self.context.shared.guards,
356 parent_style,
357 layout_parent_style,
358 FirstLineReparenting::No,
359 &Default::default(),
360 Some(&self.context.thread_local.rule_cache),
361 &mut conditions,
362 &mut self.context.thread_local.tree_counting_caches,
363 );
364
365 self.context.thread_local.rule_cache.insert_if_possible(
366 &self.context.shared.guards,
367 &values,
368 pseudo,
369 &inputs,
370 &conditions,
371 );
372
373 ResolvedStyle(values)
374 }
375
376 pub fn cascade_styles_with_default_parents(
378 &mut self,
379 inputs: ElementCascadeInputs,
380 ) -> ResolvedElementStyles {
381 with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
382 let primary_style =
383 self.cascade_primary_style(inputs.primary, parent_style, layout_parent_style);
384
385 let mut pseudo_styles = EagerPseudoStyles::default();
386 if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
387 let layout_parent_style_for_pseudo = if primary_style.style().is_display_contents()
388 {
389 layout_parent_style
390 } else {
391 Some(primary_style.style())
392 };
393
394 for (i, inputs) in pseudo_array.iter_mut().enumerate() {
395 if let Some(inputs) = inputs.take() {
396 let pseudo = PseudoElement::from_eager_index(i);
397
398 let style = self.cascade_style_and_visited(
399 inputs,
400 Some(primary_style.style()),
401 layout_parent_style_for_pseudo,
402 Some(&pseudo),
403 );
404
405 if !matches!(self.pseudo_resolution, PseudoElementResolution::Force)
406 && eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
407 {
408 continue;
409 }
410
411 pseudo_styles.set(&pseudo, style.0);
412 }
413 }
414 }
415
416 ResolvedElementStyles {
417 primary: primary_style,
418 pseudos: pseudo_styles,
419 }
420 })
421 }
422
423 fn resolve_pseudo_style(
424 &mut self,
425 pseudo: &PseudoElement,
426 originating_element_style: &PrimaryStyle,
427 layout_parent_style: Option<&ComputedValues>,
428 ) -> Option<ResolvedStyle> {
429 let MatchingResults {
430 rule_node,
431 mut flags,
432 } = self.match_pseudo(
433 &originating_element_style.style.0,
434 pseudo,
435 VisitedHandlingMode::AllLinksUnvisited,
436 )?;
437
438 let mut visited_rules = None;
439 if originating_element_style.style().visited_style().is_some() {
440 visited_rules = self
441 .match_pseudo(
442 &originating_element_style.style.0,
443 pseudo,
444 VisitedHandlingMode::RelevantLinkVisited,
445 )
446 .map(|results| {
447 flags |= results.flags;
448 results.rule_node
449 });
450 }
451
452 Some(self.cascade_style_and_visited(
453 CascadeInputs {
454 rules: Some(rule_node),
455 visited_rules,
456 flags,
457 included_cascade_flags: RuleCascadeFlags::empty(),
458 },
459 Some(originating_element_style.style()),
460 layout_parent_style,
461 Some(pseudo),
462 ))
463 }
464
465 fn match_primary(&mut self, visited_handling: VisitedHandlingMode) -> MatchingResults {
466 debug!(
467 "Match primary for {:?}, visited: {:?}",
468 self.element, visited_handling
469 );
470 let mut applicable_declarations = ApplicableDeclarationList::new();
471
472 let bloom_filter = self.context.thread_local.bloom_filter.filter();
473 let selector_caches = &mut self.context.thread_local.selector_caches;
474 let mut matching_context = MatchingContext::new_for_visited(
475 MatchingMode::Normal,
476 Some(bloom_filter),
477 selector_caches,
478 visited_handling,
479 self.context.shared.quirks_mode(),
480 NeedsSelectorFlags::Yes,
481 MatchingForInvalidation::No,
482 );
483
484 let stylist = &self.context.shared.stylist;
485 stylist.push_applicable_declarations(
487 self.element,
488 None,
489 self.element.style_attribute(),
490 self.element.smil_override(),
491 self.element.animation_declarations(self.context.shared),
492 self.rule_inclusion,
493 &mut applicable_declarations,
494 &mut matching_context,
495 );
496
497 let rule_node = stylist
498 .rule_tree()
499 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
500
501 if log_enabled!(Trace) {
502 trace!("Matched rules for {:?}:", self.element);
503 for rn in rule_node.self_and_ancestors() {
504 let source = rn.style_source();
505 if source.is_some() {
506 trace!(" > {:?}", source);
507 }
508 }
509 }
510
511 MatchingResults {
512 rule_node,
513 flags: matching_context.extra_data.cascade_input_flags,
514 }
515 }
516
517 fn match_pseudo(
518 &mut self,
519 originating_element_style: &ComputedValues,
520 pseudo_element: &PseudoElement,
521 visited_handling: VisitedHandlingMode,
522 ) -> Option<MatchingResults> {
523 debug!(
524 "Match pseudo {:?} for {:?}, visited: {:?}",
525 self.element, pseudo_element, visited_handling
526 );
527 debug_assert!(pseudo_element.is_eager());
528
529 let mut applicable_declarations = ApplicableDeclarationList::new();
530
531 let stylist = &self.context.shared.stylist;
532
533 if !self
534 .element
535 .may_generate_pseudo(pseudo_element, originating_element_style)
536 {
537 return None;
538 }
539
540 let bloom_filter = self.context.thread_local.bloom_filter.filter();
541 let selector_caches = &mut self.context.thread_local.selector_caches;
542
543 let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
544 MatchingMode::ForStatelessPseudoElement,
545 Some(bloom_filter),
546 selector_caches,
547 visited_handling,
548 self.context.shared.quirks_mode(),
549 NeedsSelectorFlags::Yes,
550 MatchingForInvalidation::No,
551 );
552 matching_context.extra_data.originating_element_style = Some(originating_element_style);
553
554 stylist.push_applicable_declarations(
557 self.element,
558 Some(pseudo_element),
559 None,
560 None,
561 Default::default(),
562 self.rule_inclusion,
563 &mut applicable_declarations,
564 &mut matching_context,
565 );
566
567 if applicable_declarations.is_empty() {
568 return None;
569 }
570
571 let rule_node = stylist
572 .rule_tree()
573 .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
574
575 Some(MatchingResults {
576 rule_node,
577 flags: matching_context.extra_data.cascade_input_flags,
578 })
579 }
580
581 pub fn resolve_starting_style(
584 &mut self,
585 primary_style: &Arc<ComputedValues>,
586 ) -> Option<ResolvedStyle> {
587 if !RuleTree::has_starting_style(primary_style.rules()) {
588 return None;
589 }
590 let inputs = CascadeInputs {
591 rules: Some(primary_style.rules().clone()),
592 visited_rules: primary_style.visited_rules().cloned(),
593 flags: primary_style.flags.for_cascade_inputs(),
594 included_cascade_flags: RuleCascadeFlags::STARTING_STYLE,
595 };
596 Some(self.cascade_style_and_visited_with_default_parents(inputs))
597 }
598
599 pub fn after_change_style(
601 &mut self,
602 primary_style: &Arc<ComputedValues>,
603 ) -> Option<Arc<ComputedValues>> {
604 let rule_node = primary_style.rules();
605 let without_transition_rules = RuleTree::remove_transition_rule_if_applicable(rule_node);
606 if without_transition_rules == *rule_node {
607 return None;
610 }
611
612 let inputs = CascadeInputs {
614 rules: Some(without_transition_rules),
615 visited_rules: primary_style.visited_rules().cloned(),
616 flags: primary_style.flags.for_cascade_inputs(),
617 included_cascade_flags: RuleCascadeFlags::empty(),
618 };
619
620 let style = self.cascade_style_and_visited_with_default_parents(inputs);
621 Some(style.0)
622 }
623}