Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/servo/components/style/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 20 kB image not shown  

Quelle  style_resolver.rs

  Sprache: Rust
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */


//! Style resolution for a given element or pseudo-element.

use crate::applicable_declarations::ApplicableDeclarationList;
use crate::computed_value_flags::ComputedValueFlags;
use crate::context::{CascadeInputs, ElementCascadeInputs, StyleContext};
use crate::data::{EagerPseudoStyles, ElementStyles};
use crate::dom::TElement;
use crate::matching::MatchMethods;
use crate::properties::longhands::display::computed_value::T as Display;
use crate::properties::{ComputedValues, FirstLineReparenting};
use crate::rule_tree::{RuleCascadeFlags, RuleTree, StrongRuleNode};
use crate::selector_parser::{PseudoElement, SelectorImpl};
use crate::stylist::RuleInclusion;
use log::Level::Trace;
use selectors::matching::{
    MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, VisitedHandlingMode,
};
#[cfg(feature = "gecko")]
use selectors::parser::PseudoElement as PseudoElementTrait;
use servo_arc::Arc;

/// Whether pseudo-elements should be resolved or not.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum 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.
pub struct 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>,
}

struct MatchingResults {
    rule_node: StrongRuleNode,
    flags: ComputedValueFlags,
}

/// A style returned from the resolver machinery.
pub struct ResolvedStyle(pub Arc<ComputedValues>);

impl ResolvedStyle {
    /// Convenience accessor for the style.
    #[inline]
    pub fn style(&self) -> &ComputedValues {
        &*self.0
    }
}

/// The primary style of an element or an element-backed pseudo-element.
pub struct 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,
}

/// A set of style returned from the resolver machinery.
pub struct ResolvedElementStyles {
    /// Primary style.
    pub primary: PrimaryStyle,
    /// Pseudo styles.
    pub pseudos: EagerPseudoStyles,
}

impl ResolvedElementStyles {
    /// Convenience accessor for the primary style.
    pub fn primary_style(&self) -> &Arc<ComputedValues> {
        &self.primary.style.0
    }

    /// Convenience mutable accessor for the style.
    pub fn primary_style_mut(&mut self) -> &mut Arc<ComputedValues> {
        &mut self.primary.style.0
    }
}

impl PrimaryStyle {
    /// Convenience accessor for the style.
    pub fn style(&self) -> &ComputedValues {
        &*self.style.0
    }
}

impl From<ResolvedElementStyles> for ElementStyles {
    fn from(r: ResolvedElementStyles) -> ElementStyles {
        ElementStyles {
            primary: Some(r.primary.style.0),
            pseudos: r.pseudos,
        }
    }
}

pub(cratefn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
where
    E: TElement,
    F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,
{
    let parent_el = 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 mut layout_parent_el = parent_el.clone();
    let layout_parent_data;
    let mut layout_parent_style = parent_style;
    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();
        layout_parent_style = Some(layout_parent_data.styles.primary());
    }

    f(
        parent_style.map(|x| &**x),
        layout_parent_style.map(|s| &**s),
    )
}

fn layout_parent_style_for_pseudo<'a>(
    primary_style: &'a PrimaryStyle,
    layout_parent_style: Option<&'a ComputedValues>,
) -> Option<&'a ComputedValues> {
    if primary_style.style().is_display_contents() {
        layout_parent_style
    } else {
        Some(primary_style.style())
    }
}

fn eager_pseudo_is_definitely_not_generated(
    pseudo: &PseudoElement,
    style: &ComputedValues,
) -> bool {
    if !pseudo.is_before_or_after() {
        return false;
    }

    if style
        .flags
        .intersects(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE)
    {
        return false;
    }

    style.get_box().clone_display() == Display::None || style.ineffective_content_property()
}

impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
where
    'ctx: 'a,
    'le: 'ctx,
    E: TElement + MatchMethods + 'le,
{
    /// Trivially construct a new StyleResolverForElement.
    pub fn new(
        element: E,
        context: &'a mut StyleContext<'ctx, E>,
        rule_inclusion: RuleInclusion,
        pseudo_resolution: PseudoElementResolution,
    ) -> Self {
        Self {
            element,
            context,
            rule_inclusion,
            pseudo_resolution,
            _marker: ::std::marker::PhantomData,
        }
    }

    /// Resolve just the style of a given element.
    pub fn resolve_primary_style(
        &mut self,
        parent_style: Option<&ComputedValues>,
        layout_parent_style: Option<&ComputedValues>,
    ) -> PrimaryStyle {
        let primary_results = self.match_primary(VisitedHandlingMode::AllLinksUnvisited);

        let inside_link = parent_style.map_or(false, |s| s.visited_style().is_some());

        let visited_rules = if self.context.shared.visited_styles_enabled
            && (inside_link || self.element.is_link())
        {
            let visited_matching_results =
                self.match_primary(VisitedHandlingMode::RelevantLinkVisited);
            Some(visited_matching_results.rule_node)
        } else {
            None
        };

        self.cascade_primary_style(
            CascadeInputs {
                rules: Some(primary_results.rule_node),
                visited_rules,
                flags: primary_results.flags,
                included_cascade_flags: RuleCascadeFlags::empty(),
            },
            parent_style,
            layout_parent_style,
        )
    }

    fn cascade_primary_style(
        &mut self,
        inputs: CascadeInputs,
        parent_style: Option<&ComputedValues>,
        layout_parent_style: Option<&ComputedValues>,
    ) -> 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()
            && inputs.included_cascade_flags.is_empty();

        if may_reuse {
            let cached = self.context.thread_local.sharing_cache.lookup_by_rules(
                self.context.shared,
                parent_style.unwrap(),
                &inputs,
                self.element,
            );
            if let Some(mut primary_style) = cached {
                self.context.thread_local.statistics.styles_reused += 1;
                primary_style.reused_via_rule_node |= true;
                return primary_style;
            }
        }

        // 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,
        }
    }

    /// Resolve the style of a given element, and all its eager pseudo-elements.
    pub fn resolve_style(
        &mut self,
        parent_style: Option<&ComputedValues>,
        layout_parent_style: Option<&ComputedValues>,
    ) -> ResolvedElementStyles {
        let primary_style = self.resolve_primary_style(parent_style, layout_parent_style);

        let mut pseudo_styles = EagerPseudoStyles::default();

        if !self
            .element
            .implemented_pseudo_element()
            .is_some_and(|p| !p.is_element_backed())
        {
            let layout_parent_style_for_pseudo =
                layout_parent_style_for_pseudo(&primary_style, layout_parent_style);
            SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
                let pseudo_style = self.resolve_pseudo_style(
                    &pseudo,
                    &primary_style,
                    layout_parent_style_for_pseudo,
                );

                if let Some(style) = pseudo_style {
                    if !matches!(self.pseudo_resolution, PseudoElementResolution::Force)
                        && eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
                    {
                        return;
                    }
                    pseudo_styles.set(&pseudo, style.0);
                }
            })
        }

        ResolvedElementStyles {
            primary: primary_style,
            pseudos: pseudo_styles,
        }
    }

    /// Resolve an element's styles with the default inheritance parent/layout
    /// parents.
    pub fn resolve_style_with_default_parents(&mut self) -> 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.
    pub fn cascade_style_and_visited_with_default_parents(
        &mut self,
        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.
    pub fn cascade_style_and_visited_for_pseudo_with_default_parents(
        &mut self,
        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);

            self.cascade_style_and_visited(
                inputs,
                Some(primary_style.style()),
                layout_parent_style_for_pseudo,
                Some(pseudo),
            )
        })
    }

    fn cascade_style_and_visited(
        &mut self,
        inputs: CascadeInputs,
        parent_style: Option<&ComputedValues>,
        layout_parent_style: Option<&ComputedValues>,
        pseudo: Option<&PseudoElement>,
    ) -> ResolvedStyle {
        debug_assert!(pseudo.map_or(true, |p| p.is_eager()));

        let mut conditions = Default::default();
        let values = self.context.shared.stylist.cascade_style_and_visited(
            Some(self.element),
            pseudo,
            &inputs,
            &self.context.shared.guards,
            parent_style,
            layout_parent_style,
            FirstLineReparenting::No,
            /* try_tactic = */ &Default::default(),
            Some(&self.context.thread_local.rule_cache),
            &mut conditions,
            &mut self.context.thread_local.tree_counting_caches,
        );

        self.context.thread_local.rule_cache.insert_if_possible(
            &self.context.shared.guards,
            &values,
            pseudo,
            &inputs,
            &conditions,
        );

        ResolvedStyle(values)
    }

    /// Cascade the element and pseudo-element styles with the default parents.
    pub fn cascade_styles_with_default_parents(
        &mut self,
        inputs: ElementCascadeInputs,
    ) -> ResolvedElementStyles {
        with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
            let primary_style =
                self.cascade_primary_style(inputs.primary, parent_style, layout_parent_style);

            let mut pseudo_styles = EagerPseudoStyles::default();
            if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
                let layout_parent_style_for_pseudo = if primary_style.style().is_display_contents()
                {
                    layout_parent_style
                } else {
                    Some(primary_style.style())
                };

                for (i, inputs) in pseudo_array.iter_mut().enumerate() {
                    if let Some(inputs) = inputs.take() {
                        let pseudo = PseudoElement::from_eager_index(i);

                        let style = self.cascade_style_and_visited(
                            inputs,
                            Some(primary_style.style()),
                            layout_parent_style_for_pseudo,
                            Some(&pseudo),
                        );

                        if !matches!(self.pseudo_resolution, PseudoElementResolution::Force)
                            && eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
                        {
                            continue;
                        }

                        pseudo_styles.set(&pseudo, style.0);
                    }
                }
            }

            ResolvedElementStyles {
                primary: primary_style,
                pseudos: pseudo_styles,
            }
        })
    }

    fn resolve_pseudo_style(
        &mut self,
        pseudo: &PseudoElement,
        originating_element_style: &PrimaryStyle,
        layout_parent_style: Option<&ComputedValues>,
    ) -> Option<ResolvedStyle> {
        let MatchingResults {
            rule_node,
            mut flags,
        } = self.match_pseudo(
            &originating_element_style.style.0,
            pseudo,
            VisitedHandlingMode::AllLinksUnvisited,
        )?;

        let mut visited_rules = None;
        if originating_element_style.style().visited_style().is_some() {
            visited_rules = self
                .match_pseudo(
                    &originating_element_style.style.0,
                    pseudo,
                    VisitedHandlingMode::RelevantLinkVisited,
                )
                .map(|results| {
                    flags |= results.flags;
                    results.rule_node
                });
        }

        Some(self.cascade_style_and_visited(
            CascadeInputs {
                rules: Some(rule_node),
                visited_rules,
                flags,
                included_cascade_flags: RuleCascadeFlags::empty(),
            },
            Some(originating_element_style.style()),
            layout_parent_style,
            Some(pseudo),
        ))
    }

    fn match_primary(&mut self, visited_handling: VisitedHandlingMode) -> MatchingResults {
        debug!(
            "Match primary for {:?}, visited: {:?}",
            self.element, visited_handling
        );
        let mut applicable_declarations = ApplicableDeclarationList::new();

        let bloom_filter = self.context.thread_local.bloom_filter.filter();
        let selector_caches = &mut self.context.thread_local.selector_caches;
        let mut matching_context = MatchingContext::new_for_visited(
            MatchingMode::Normal,
            Some(bloom_filter),
            selector_caches,
            visited_handling,
            self.context.shared.quirks_mode(),
            NeedsSelectorFlags::Yes,
            MatchingForInvalidation::No,
        );

        let stylist = &self.context.shared.stylist;
        // Compute the primary rule node.
        stylist.push_applicable_declarations(
            self.element,
            None,
            self.element.style_attribute(),
            self.element.smil_override(),
            self.element.animation_declarations(self.context.shared),
            self.rule_inclusion,
            &mut applicable_declarations,
            &mut matching_context,
        );

        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);
                }
            }
        }

        MatchingResults {
            rule_node,
            flags: matching_context.extra_data.cascade_input_flags,
        }
    }

    fn match_pseudo(
        &mut self,
        originating_element_style: &ComputedValues,
        pseudo_element: &PseudoElement,
        visited_handling: VisitedHandlingMode,
    ) -> Option<MatchingResults> {
        debug!(
            "Match pseudo {:?} for {:?}, visited: {:?}",
            self.element, pseudo_element, visited_handling
        );
        debug_assert!(pseudo_element.is_eager());

        let mut applicable_declarations = ApplicableDeclarationList::new();

        let stylist = &self.context.shared.stylist;

        if !self
            .element
            .may_generate_pseudo(pseudo_element, originating_element_style)
        {
            return None;
        }

        let bloom_filter = self.context.thread_local.bloom_filter.filter();
        let selector_caches = &mut self.context.thread_local.selector_caches;

        let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
            MatchingMode::ForStatelessPseudoElement,
            Some(bloom_filter),
            selector_caches,
            visited_handling,
            self.context.shared.quirks_mode(),
            NeedsSelectorFlags::Yes,
            MatchingForInvalidation::No,
        );
        matching_context.extra_data.originating_element_style = Some(originating_element_style);

        // NB: We handle animation rules for ::before and ::after when
        // traversing them.
        stylist.push_applicable_declarations(
            self.element,
            Some(pseudo_element),
            None,
            None,
            /* animation_declarations = */ Default::default(),
            self.rule_inclusion,
            &mut applicable_declarations,
            &mut matching_context,
        );

        if applicable_declarations.is_empty() {
            return None;
        }

        let rule_node = stylist
            .rule_tree()
            .compute_rule_node(&mut applicable_declarations, &'color:red'>self.context.shared.guards);

        Some(MatchingResults {
            rule_node,
            flags: matching_context.extra_data.cascade_input_flags,
        })
    }

    /// Resolve the starting style by recascading with @starting-style rules included, similar to
    /// how after_change_style works.
    pub fn resolve_starting_style(
        &mut self,
        primary_style: &Arc<ComputedValues>,
    ) -> Option<ResolvedStyle> {
        if !RuleTree::has_starting_style(primary_style.rules()) {
            return None;
        }
        let inputs = CascadeInputs {
            rules: Some(primary_style.rules().clone()),
            visited_rules: primary_style.visited_rules().cloned(),
            flags: primary_style.flags.for_cascade_inputs(),
            included_cascade_flags: RuleCascadeFlags::STARTING_STYLE,
        };
        Some(self.cascade_style_and_visited_with_default_parents(inputs))
    }

    /// If there is no transition rule in the ComputedValues, it returns None.
    pub fn after_change_style(
        &mut self,
        primary_style: &Arc<ComputedValues>,
    ) -> Option<Arc<ComputedValues>> {
        let rule_node = primary_style.rules();
        let without_transition_rules = RuleTree::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(),
            included_cascade_flags: RuleCascadeFlags::empty(),
        };

        let style = self.cascade_style_and_visited_with_default_parents(inputs);
        Some(style.0)
    }
}

Messung V0.5 in Prozent
C=91 H=94 G=92

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

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.