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