/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Style resolution for a given element or pseudo-element.
usecrate::applicable_declarations::ApplicableDeclarationList; usecrate::computed_value_flags::ComputedValueFlags; usecrate::context::{CascadeInputs, ElementCascadeInputs, StyleContext}; usecrate::data::{EagerPseudoStyles, ElementStyles}; usecrate::dom::TElement; usecrate::matching::MatchMethods; usecrate::properties::longhands::display::computed_value::T as Display; usecrate::properties::{ComputedValues, FirstLineReparenting}; usecrate::rule_tree::StrongRuleNode; usecrate::selector_parser::{PseudoElement, SelectorImpl}; usecrate::stylist::RuleInclusion; use log::Level::Trace; use selectors::matching::{
IncludeStartingStyle, MatchingContext, MatchingForInvalidation, MatchingMode,
NeedsSelectorFlags, VisitedHandlingMode,
}; use servo_arc::Arc;
/// Whether pseudo-elements should be resolved or not. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubenum PseudoElementResolution { /// Only resolve pseudo-styles if possibly applicable.
IfApplicable, /// Force pseudo-element resolution.
Force,
}
/// A struct that takes care of resolving the style of a given element. pubstruct StyleResolverForElement<'a, 'ctx, 'le, E> where 'ctx: 'a, 'le: 'ctx,
E: TElement + MatchMethods + 'le,
{
element: E,
context: &'a mut StyleContext<'ctx, E>,
rule_inclusion: RuleInclusion,
pseudo_resolution: PseudoElementResolution,
_marker: ::std::marker::PhantomData<&'le E>,
}
/// A style returned from the resolver machinery. pubstruct ResolvedStyle(pub Arc<ComputedValues>);
impl ResolvedStyle { /// Convenience accessor for the style. #[inline] pubfn style(&self) -> &ComputedValues {
&*self.0
}
}
/// The primary style of an element or an element-backed pseudo-element. pubstruct PrimaryStyle { /// The style itself. pub style: ResolvedStyle, /// Whether the style was reused from another element via the rule node (see /// `StyleSharingCache::lookup_by_rules`). pub reused_via_rule_node: bool, /// The element may have matched rules inside @starting-style. /// Basically, we don't apply @starting-style rules to |style|. This is a sugar to let us know /// if we should resolve the element again for starting style, which is the after-change style /// with @starting-style rules applied in addition. pub may_have_starting_style: bool,
}
/// A set of style returned from the resolver machinery. pubstruct ResolvedElementStyles { /// Primary style. pub primary: PrimaryStyle, /// Pseudo styles. pub pseudos: EagerPseudoStyles,
}
impl ResolvedElementStyles { /// Convenience accessor for the primary style. pubfn primary_style(&self) -> &Arc<ComputedValues> {
&self.primary.style.0
}
/// Convenience mutable accessor for the style. pubfn primary_style_mut(&mutself) -> &mut Arc<ComputedValues> {
&mutself.primary.style.0
}
/// Returns true if this element may have starting style rules. #[inline] pubfn may_have_starting_style(&self) -> bool { self.primary.may_have_starting_style
}
}
impl PrimaryStyle { /// Convenience accessor for the style. pubfn style(&self) -> &ComputedValues {
&*self.style.0
}
}
/// Resolve just the style of a given element. pubfn resolve_primary_style(
&mutself,
parent_style: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
include_starting_style: IncludeStartingStyle,
) -> PrimaryStyle { let primary_results = self.match_primary(
VisitedHandlingMode::AllLinksUnvisited,
include_starting_style,
);
let inside_link = parent_style.map_or(false, |s| s.visited_style().is_some());
fn cascade_primary_style(
&mutself,
inputs: CascadeInputs,
parent_style: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
include_starting_style: IncludeStartingStyle,
may_have_starting_style: bool,
) -> PrimaryStyle { // Before doing the cascade, check the sharing cache and see if we can // reuse the style via rule node identity. let may_reuse = self.element.matches_user_and_content_rules() &&
parent_style.is_some() &&
inputs.rules.is_some() &&
include_starting_style == IncludeStartingStyle::No;
// No style to reuse. Cascade the style, starting with visited style // if necessary.
PrimaryStyle {
style: self.cascade_style_and_visited(
inputs,
parent_style,
layout_parent_style, /* pseudo = */ None,
),
reused_via_rule_node: false,
may_have_starting_style,
}
}
/// Resolve the style of a given element, and all its eager pseudo-elements. pubfn resolve_style(
&mutself,
parent_style: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
) -> ResolvedElementStyles { let primary_style = self.resolve_primary_style(parent_style, layout_parent_style, IncludeStartingStyle::No);
/// Resolve an element's styles with the default inheritance parent/layout /// parents. pubfn resolve_style_with_default_parents(&mutself) -> ResolvedElementStyles {
with_default_parent_styles(self.element, |parent_style, layout_parent_style| { self.resolve_style(parent_style, layout_parent_style)
})
}
/// Cascade a set of rules, using the default parent for inheritance. pubfn cascade_style_and_visited_with_default_parents(
&mutself,
inputs: CascadeInputs,
) -> ResolvedStyle {
with_default_parent_styles(self.element, |parent_style, layout_parent_style| { self.cascade_style_and_visited(
inputs,
parent_style,
layout_parent_style, /* pseudo = */ None,
)
})
}
/// Cascade a set of rules for pseudo element, using the default parent for inheritance. pubfn cascade_style_and_visited_for_pseudo_with_default_parents(
&mutself,
inputs: CascadeInputs,
pseudo: &PseudoElement,
primary_style: &PrimaryStyle,
) -> ResolvedStyle {
with_default_parent_styles(self.element, |_, layout_parent_style| { let layout_parent_style_for_pseudo =
layout_parent_style_for_pseudo(primary_style, layout_parent_style);
let stylist = &self.context.shared.stylist; let implemented_pseudo = self.element.implemented_pseudo_element(); // Compute the primary rule node.
stylist.push_applicable_declarations( self.element,
implemented_pseudo.as_ref(), self.element.style_attribute(), self.element.smil_override(), self.element.animation_declarations(self.context.shared), self.rule_inclusion,
&mut applicable_declarations,
&mut matching_context,
);
// FIXME(emilio): This is a hack for animations, and should go away. self.element.unset_dirty_style_attribute();
let rule_node = stylist
.rule_tree()
.compute_rule_node(&mut applicable_declarations, &'color:red'>self.context.shared.guards);
if log_enabled!(Trace) {
trace!("Matched rules for {:?}:", self.element); for rn in rule_node.self_and_ancestors() { let source = rn.style_source(); if source.is_some() {
trace!(" > {:?}", source);
}
}
}
/// Resolve the starting style. pubfn resolve_starting_style(&mutself) -> PrimaryStyle { // Compute after-change style for the parent and the layout parent. // Per spec, starting style inherits from the parent’s after-change style just like // after-change style does. let parent_el = self.element.inheritance_parent(); let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data()); let parent_style = parent_data.as_ref().map(|d| d.styles.primary()); let parent_after_change_style =
parent_style.and_then(|s| self.after_change_style(s)); let parent_values = parent_after_change_style
.as_ref()
.or(parent_style)
.map(|x| &**x);
letmut layout_parent_el = parent_el.clone(); let layout_parent_data; let layout_parent_after_change_style; let layout_parent_values = if parent_style.map_or(false, |s| s.is_display_contents()) {
layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap(); let layout_parent_style = Some(layout_parent_data.styles.primary());
layout_parent_after_change_style =
layout_parent_style.and_then(|s| self.after_change_style(s));
layout_parent_after_change_style
.as_ref()
.or(layout_parent_style)
.map(|x| &**x)
} else {
parent_values
};
/// If there is no transition rule in the ComputedValues, it returns None. pubfn after_change_style(
&mutself,
primary_style: &Arc<ComputedValues>,
) -> Option<Arc<ComputedValues>> { let rule_node = primary_style.rules(); let without_transition_rules = self.context
.shared
.stylist
.rule_tree()
.remove_transition_rule_if_applicable(rule_node); if without_transition_rules == *rule_node { // We don't have transition rule in this case, so return None to let // the caller use the original ComputedValues. return None;
}
// FIXME(bug 868975): We probably need to transition visited style as // well. let inputs = CascadeInputs {
rules: Some(without_transition_rules),
visited_rules: primary_style.visited_rules().cloned(),
flags: primary_style.flags.for_cascade_inputs(),
};
let style = self.cascade_style_and_visited_with_default_parents(inputs);
Some(style.0)
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet am 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.