Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  stylist.rs   Sprache: unbekannt

 
/* 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/. */

//! Selector matching.

use crate::applicable_declarations::{
    ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority, ScopeProximity,
};
use crate::computed_value_flags::ComputedValueFlags;
use crate::context::{CascadeInputs, QuirksMode};
use crate::custom_properties::ComputedCustomProperties;
use crate::dom::TElement;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
use crate::invalidation::element::invalidation_map::{
    note_selector_for_invalidation, InvalidationMap, RelativeSelectorInvalidationMap,
};
use crate::invalidation::media_queries::{
    EffectiveMediaQueryResults, MediaListKey, ToMediaListKey,
};
use crate::invalidation::stylesheets::RuleChangeKind;
use crate::media_queries::Device;
use crate::properties::{self, CascadeMode, ComputedValues, FirstLineReparenting};
use crate::properties::{AnimationDeclarations, PropertyDeclarationBlock};
use crate::properties_and_values::registry::{
    PropertyRegistration, PropertyRegistrationData, ScriptRegistry as CustomPropertyScriptRegistry,
};
use crate::rule_cache::{RuleCache, RuleCacheConditions};
use crate::rule_collector::RuleCollector;
use crate::rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, SelectorMap, SelectorMapEntry};
use crate::selector_parser::{
    NonTSPseudoClass, PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap,
};
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use crate::sharing::{RevalidationResult, ScopeRevalidationResult};
use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKind};
use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
use crate::stylesheets::container_rule::ContainerCondition;
use crate::stylesheets::import_rule::ImportLayer;
use crate::stylesheets::keyframes_rule::KeyframesAnimation;
use crate::stylesheets::layer_rule::{LayerName, LayerOrder};
use crate::stylesheets::scope_rule::{
    collect_scope_roots, element_is_outside_of_scope, scope_selector_list_is_trivial, ImplicitScopeRoot, ScopeRootCandidate, ScopeSubjectMap, ScopeTarget
};
#[cfg(feature = "gecko")]
use crate::stylesheets::{
    CounterStyleRule, FontFaceRule, FontFeatureValuesRule, FontPaletteValuesRule,
};
use crate::stylesheets::{
    CssRule, EffectiveRulesIterator, Origin, OriginSet, PagePseudoClassFlags, PageRule, PerOrigin,
    PerOriginIter, StylesheetContents, StylesheetInDocument,
};
use crate::values::{computed, AtomIdent};
use crate::AllocErr;
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
use dom::{DocumentState, ElementState};
use fxhash::FxHashMap;
use malloc_size_of::MallocSizeOf;
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
use selectors::attr::{CaseSensitivity, NamespaceConstraint};
use selectors::bloom::BloomFilter;
use selectors::matching::{
    matches_selector, selector_may_match, MatchingContext, MatchingMode, NeedsSelectorFlags, SelectorCaches
};
use selectors::matching::{MatchingForInvalidation, VisitedHandlingMode};
use selectors::parser::{
    AncestorHashes, Combinator, Component, FeaturelessHostMatches, Selector, SelectorIter,
    SelectorList,
};
use selectors::visitor::{SelectorListKind, SelectorVisitor};
use servo_arc::{Arc, ArcBorrow};
use smallvec::SmallVec;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::sync::Mutex;
use std::{mem, ops};

/// The type of the stylesheets that the stylist contains.
#[cfg(feature = "servo")]
pub type StylistSheet = crate::stylesheets::DocumentStyleSheet;

/// The type of the stylesheets that the stylist contains.
#[cfg(feature = "gecko")]
pub type StylistSheet = crate::gecko::data::GeckoStyleSheet;

#[derive(Debug, Clone)]
struct StylesheetContentsPtr(Arc<StylesheetContents>);

impl PartialEq for StylesheetContentsPtr {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.0, &other.0)
    }
}

impl Eq for StylesheetContentsPtr {}

impl Hash for StylesheetContentsPtr {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let contents: &StylesheetContents = &*self.0;
        (contents as *const StylesheetContents).hash(state)
    }
}

type StyleSheetContentList = Vec<StylesheetContentsPtr>;

/// A key in the cascade data cache.
#[derive(Debug, Hash, Default, PartialEq, Eq)]
struct CascadeDataCacheKey {
    media_query_results: Vec<MediaListKey>,
    contents: StyleSheetContentList,
}

unsafe impl Send for CascadeDataCacheKey {}
unsafe impl Sync for CascadeDataCacheKey {}

trait CascadeDataCacheEntry: Sized {
    /// Rebuilds the cascade data for the new stylesheet collection. The
    /// collection is guaranteed to be dirty.
    fn rebuild<S>(
        device: &Device,
        quirks_mode: QuirksMode,
        collection: SheetCollectionFlusher<S>,
        guard: &SharedRwLockReadGuard,
        old_entry: &Self,
    ) -> Result<Arc<Self>, AllocErr>
    where
        S: StylesheetInDocument + PartialEq + 'static;
    /// Measures heap memory usage.
    #[cfg(feature = "gecko")]
    fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes);
}

struct CascadeDataCache<Entry> {
    entries: FxHashMap<CascadeDataCacheKey, Arc<Entry>>,
}

impl<Entry> CascadeDataCache<Entry>
where
    Entry: CascadeDataCacheEntry,
{
    fn new() -> Self {
        Self {
            entries: Default::default(),
        }
    }

    fn len(&self) -> usize {
        self.entries.len()
    }

    // FIXME(emilio): This may need to be keyed on quirks-mode too, though for
    // UA sheets there aren't class / id selectors on those sheets, usually, so
    // it's probably ok... For the other cache the quirks mode shouldn't differ
    // so also should be fine.
    fn lookup<'a, S>(
        &'a mut self,
        device: &Device,
        quirks_mode: QuirksMode,
        collection: SheetCollectionFlusher<S>,
        guard: &SharedRwLockReadGuard,
        old_entry: &Entry,
    ) -> Result<Option<Arc<Entry>>, AllocErr>
    where
        S: StylesheetInDocument + PartialEq + 'static,
    {
        use std::collections::hash_map::Entry as HashMapEntry;
        debug!("StyleSheetCache::lookup({})", self.len());

        if !collection.dirty() {
            return Ok(None);
        }

        let mut key = CascadeDataCacheKey::default();
        for sheet in collection.sheets() {
            CascadeData::collect_applicable_media_query_results_into(
                device,
                sheet,
                guard,
                &mut key.media_query_results,
                &mut key.contents,
            )
        }

        let new_entry;
        match self.entries.entry(key) {
            HashMapEntry::Vacant(e) => {
                debug!("> Picking the slow path (not in the cache)");
                new_entry = Entry::rebuild(
                    device,
                    quirks_mode,
                    collection,
                    guard,
                    old_entry,
                )?;
                e.insert(new_entry.clone());
            },
            HashMapEntry::Occupied(mut e) => {
                // Avoid reusing our old entry (this can happen if we get
                // invalidated due to CSSOM mutations and our old stylesheet
                // contents were already unique, for example).
                if !std::ptr::eq(&**e.get(), old_entry) {
                    if log_enabled!(log::Level::Debug) {
                        debug!("cache hit for:");
                        for sheet in collection.sheets() {
                            debug!(" > {:?}", sheet);
                        }
                    }
                    // The line below ensures the "committed" bit is updated
                    // properly.
                    collection.each(|_, _, _| true);
                    return Ok(Some(e.get().clone()));
                }

                debug!("> Picking the slow path due to same entry as old");
                new_entry = Entry::rebuild(
                    device,
                    quirks_mode,
                    collection,
                    guard,
                    old_entry,
                )?;
                e.insert(new_entry.clone());
            },
        }

        Ok(Some(new_entry))
    }

    /// Returns all the cascade datas that are not being used (that is, that are
    /// held alive just by this cache).
    ///
    /// We return them instead of dropping in place because some of them may
    /// keep alive some other documents (like the SVG documents kept alive by
    /// URL references), and thus we don't want to drop them while locking the
    /// cache to not deadlock.
    fn take_unused(&mut self) -> SmallVec<[Arc<Entry>; 3]> {
        let mut unused = SmallVec::new();
        self.entries.retain(|_key, value| {
            // is_unique() returns false for static references, but we never
            // have static references to UserAgentCascadeDatas.  If we did, it
            // may not make sense to put them in the cache in the first place.
            if !value.is_unique() {
                return true;
            }
            unused.push(value.clone());
            false
        });
        unused
    }

    fn take_all(&mut self) -> FxHashMap<CascadeDataCacheKey, Arc<Entry>> {
        mem::take(&mut self.entries)
    }

    #[cfg(feature = "gecko")]
    fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
        sizes.mOther += self.entries.shallow_size_of(ops);
        for (_key, arc) in self.entries.iter() {
            // These are primary Arc references that can be measured
            // unconditionally.
            sizes.mOther += arc.unconditional_shallow_size_of(ops);
            arc.add_size_of(ops, sizes);
        }
    }
}

/// Measure heap usage of UA_CASCADE_DATA_CACHE.
#[cfg(feature = "gecko")]
pub fn add_size_of_ua_cache(ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
    UA_CASCADE_DATA_CACHE
        .lock()
        .unwrap()
        .add_size_of(ops, sizes);
}

lazy_static! {
    /// A cache of computed user-agent data, to be shared across documents.
    static ref UA_CASCADE_DATA_CACHE: Mutex<UserAgentCascadeDataCache> =
        Mutex::new(UserAgentCascadeDataCache::new());
}

impl CascadeDataCacheEntry for UserAgentCascadeData {
    fn rebuild<S>(
        device: &Device,
        quirks_mode: QuirksMode,
        collection: SheetCollectionFlusher<S>,
        guard: &SharedRwLockReadGuard,
        _old: &Self,
    ) -> Result<Arc<Self>, AllocErr>
    where
        S: StylesheetInDocument + PartialEq + 'static,
    {
        // TODO: Maybe we should support incremental rebuilds, though they seem
        // uncommon and rebuild() doesn't deal with
        // precomputed_pseudo_element_decls for now so...
        let mut new_data = Self {
            cascade_data: CascadeData::new(),
            precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
        };

        for (index, sheet) in collection.sheets().enumerate() {
            new_data.cascade_data.add_stylesheet(
                device,
                quirks_mode,
                sheet,
                index,
                guard,
                SheetRebuildKind::Full,
                Some(&mut new_data.precomputed_pseudo_element_decls),
            )?;
        }

        new_data.cascade_data.did_finish_rebuild();

        Ok(Arc::new(new_data))
    }

    #[cfg(feature = "gecko")]
    fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
        self.cascade_data.add_size_of(ops, sizes);
        sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops);
    }
}

type UserAgentCascadeDataCache = CascadeDataCache<UserAgentCascadeData>;

type PrecomputedPseudoElementDeclarations = PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>;

#[derive(Default)]
struct UserAgentCascadeData {
    cascade_data: CascadeData,

    /// Applicable declarations for a given non-eagerly cascaded pseudo-element.
    ///
    /// These are eagerly computed once, and then used to resolve the new
    /// computed values on the fly on layout.
    ///
    /// These are only filled from UA stylesheets.
    precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations,
}

lazy_static! {
    /// The empty UA cascade data for un-filled stylists.
    static ref EMPTY_UA_CASCADE_DATA: Arc<UserAgentCascadeData> = {
        let arc = Arc::new(UserAgentCascadeData::default());
        arc.mark_as_intentionally_leaked();
        arc
    };
}

/// All the computed information for all the stylesheets that apply to the
/// document.
#[derive(MallocSizeOf)]
pub struct DocumentCascadeData {
    #[ignore_malloc_size_of = "Arc, owned by UserAgentCascadeDataCache or empty"]
    user_agent: Arc<UserAgentCascadeData>,
    user: CascadeData,
    author: CascadeData,
    per_origin: PerOrigin<()>,
}

impl Default for DocumentCascadeData {
    fn default() -> Self {
        Self {
            user_agent: EMPTY_UA_CASCADE_DATA.clone(),
            user: Default::default(),
            author: Default::default(),
            per_origin: Default::default(),
        }
    }
}

/// An iterator over the cascade data of a given document.
pub struct DocumentCascadeDataIter<'a> {
    iter: PerOriginIter<'a, ()>,
    cascade_data: &'a DocumentCascadeData,
}

impl<'a> Iterator for DocumentCascadeDataIter<'a> {
    type Item = (&'a CascadeData, Origin);

    fn next(&mut self) -> Option<Self::Item> {
        let (_, origin) = self.iter.next()?;
        Some((self.cascade_data.borrow_for_origin(origin), origin))
    }
}

impl DocumentCascadeData {
    /// Borrows the cascade data for a given origin.
    #[inline]
    pub fn borrow_for_origin(&self, origin: Origin) -> &CascadeData {
        match origin {
            Origin::UserAgent => &self.user_agent.cascade_data,
            Origin::Author => &self.author,
            Origin::User => &self.user,
        }
    }

    fn iter_origins(&self) -> DocumentCascadeDataIter {
        DocumentCascadeDataIter {
            iter: self.per_origin.iter_origins(),
            cascade_data: self,
        }
    }

    fn iter_origins_rev(&self) -> DocumentCascadeDataIter {
        DocumentCascadeDataIter {
            iter: self.per_origin.iter_origins_rev(),
            cascade_data: self,
        }
    }

    /// Rebuild the cascade data for the given document stylesheets, and
    /// optionally with a set of user agent stylesheets.  Returns Err(..)
    /// to signify OOM.
    fn rebuild<'a, S>(
        &mut self,
        device: &Device,
        quirks_mode: QuirksMode,
        mut flusher: DocumentStylesheetFlusher<'a, S>,
        guards: &StylesheetGuards,
    ) -> Result<(), AllocErr>
    where
        S: StylesheetInDocument + PartialEq + 'static,
    {
        // First do UA sheets.
        {
            let origin_flusher = flusher.flush_origin(Origin::UserAgent);
            // Dirty check is just a minor optimization (no need to grab the
            // lock if nothing has changed).
            if origin_flusher.dirty() {
                let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
                let new_data = ua_cache.lookup(
                    device,
                    quirks_mode,
                    origin_flusher,
                    guards.ua_or_user,
                    &self.user_agent,
                )?;
                if let Some(new_data) = new_data {
                    self.user_agent = new_data;
                }
                let _unused_entries = ua_cache.take_unused();
                // See the comments in take_unused() as for why the following
                // line.
                std::mem::drop(ua_cache);
            }
        }

        // Now do the user sheets.
        self.user.rebuild(
            device,
            quirks_mode,
            flusher.flush_origin(Origin::User),
            guards.ua_or_user,
        )?;

        // And now the author sheets.
        self.author.rebuild(
            device,
            quirks_mode,
            flusher.flush_origin(Origin::Author),
            guards.author,
        )?;

        Ok(())
    }

    /// Measures heap usage.
    #[cfg(feature = "gecko")]
    pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
        self.user.add_size_of(ops, sizes);
        self.author.add_size_of(ops, sizes);
    }
}

/// Whether author styles are enabled.
///
/// This is used to support Gecko.
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
pub enum AuthorStylesEnabled {
    Yes,
    No,
}

/// A wrapper over a DocumentStylesheetSet that can be `Sync`, since it's only
/// used and exposed via mutable methods in the `Stylist`.
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
struct StylistStylesheetSet(DocumentStylesheetSet<StylistSheet>);
// Read above to see why this is fine.
unsafe impl Sync for StylistStylesheetSet {}

impl StylistStylesheetSet {
    fn new() -> Self {
        StylistStylesheetSet(DocumentStylesheetSet::new())
    }
}

impl ops::Deref for StylistStylesheetSet {
    type Target = DocumentStylesheetSet<StylistSheet>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl ops::DerefMut for StylistStylesheetSet {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

/// This structure holds all the selectors and device characteristics
/// for a given document. The selectors are converted into `Rule`s
/// and sorted into `SelectorMap`s keyed off stylesheet origin and
/// pseudo-element (see `CascadeData`).
///
/// This structure is effectively created once per pipeline, in the
/// LayoutThread corresponding to that pipeline.
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
pub struct Stylist {
    /// Device that the stylist is currently evaluating against.
    ///
    /// This field deserves a bigger comment due to the different use that Gecko
    /// and Servo give to it (that we should eventually unify).
    ///
    /// With Gecko, the device is never changed. Gecko manually tracks whether
    /// the device data should be reconstructed, and "resets" the state of the
    /// device.
    ///
    /// On Servo, on the other hand, the device is a really cheap representation
    /// that is recreated each time some constraint changes and calling
    /// `set_device`.
    device: Device,

    /// The list of stylesheets.
    stylesheets: StylistStylesheetSet,

    /// A cache of CascadeDatas for AuthorStylesheetSets (i.e., shadow DOM).
    author_data_cache: CascadeDataCache<CascadeData>,

    /// If true, the quirks-mode stylesheet is applied.
    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "defined in selectors")]
    quirks_mode: QuirksMode,

    /// Selector maps for all of the style sheets in the stylist, after
    /// evalutaing media rules against the current device, split out per
    /// cascade level.
    cascade_data: DocumentCascadeData,

    /// Whether author styles are enabled.
    author_styles_enabled: AuthorStylesEnabled,

    /// The rule tree, that stores the results of selector matching.
    rule_tree: RuleTree,

    /// The set of registered custom properties from script.
    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#dom-window-registeredpropertyset-slot>
    script_custom_properties: CustomPropertyScriptRegistry,

    /// Initial values for registered custom properties.
    initial_values_for_custom_properties: ComputedCustomProperties,

    /// Flags set from computing registered custom property initial values.
    initial_values_for_custom_properties_flags: ComputedValueFlags,

    /// The total number of times the stylist has been rebuilt.
    num_rebuilds: usize,
}

/// What cascade levels to include when styling elements.
#[derive(Clone, Copy, PartialEq)]
pub enum RuleInclusion {
    /// Include rules for style sheets at all cascade levels.  This is the
    /// normal rule inclusion mode.
    All,
    /// Only include rules from UA and user level sheets.  Used to implement
    /// `getDefaultComputedStyle`.
    DefaultOnly,
}

#[cfg(feature = "gecko")]
impl From<StyleRuleInclusion> for RuleInclusion {
    fn from(value: StyleRuleInclusion) -> Self {
        match value {
            StyleRuleInclusion::All => RuleInclusion::All,
            StyleRuleInclusion::DefaultOnly => RuleInclusion::DefaultOnly,
        }
    }
}

/// `:scope` selector, depending on the use case, can match a shadow host.
/// If used outside of `@scope`, it cannot possibly match the host.
/// Even when inside of `@scope`, it's conditional if the selector will
/// match the shadow host.
#[derive(Clone, Copy, Eq, PartialEq)]
enum ScopeMatchesShadowHost {
    NotApplicable,
    No,
    Yes,
}

impl Default for ScopeMatchesShadowHost {
    fn default() -> Self {
        Self::NotApplicable
    }
}

impl ScopeMatchesShadowHost {
    fn nest_for_scope(&mut self, matches_shadow_host: bool) {
        match *self {
            Self::NotApplicable => {
                // We're at the outermost `@scope`.
                *self = if matches_shadow_host {
                    Self::Yes
                } else {
                    Self::No
                };
            },
            Self::Yes if !matches_shadow_host => {
                // Inner `@scope` will not be able to match the shadow host.
                *self = Self::No;
            },
            _ => (),
        }
    }
}

/// Nested declarations have effectively two behaviors:
///  * Inside style rules (where they behave as the containing selector).
///  * Inside @scope (where they behave as :where(:scope)).
/// It is a bit unfortunate ideally we wouldn't need this, because scope also pushes to the
/// ancestor_selector_lists, but the behavior isn't quite the same as wrapping in `&`, see
/// https://github.com/w3c/csswg-drafts/issues/10431
#[derive(Copy, Clone)]
enum NestedDeclarationsContext {
    Style,
    Scope,
}

/// A struct containing state from ancestor rules like @layer / @import /
/// @container / nesting / @scope.
struct ContainingRuleState {
    layer_name: LayerName,
    layer_id: LayerId,
    container_condition_id: ContainerConditionId,
    in_starting_style: bool,
    scope_condition_id: ScopeConditionId,
    scope_matches_shadow_host: ScopeMatchesShadowHost,
    ancestor_selector_lists: SmallVec<[SelectorList<SelectorImpl>; 2]>,
    nested_declarations_context: NestedDeclarationsContext,
}

impl Default for ContainingRuleState {
    fn default() -> Self {
        Self {
            layer_name: LayerName::new_empty(),
            layer_id: LayerId::root(),
            container_condition_id: ContainerConditionId::none(),
            in_starting_style: false,
            ancestor_selector_lists: Default::default(),
            scope_condition_id: ScopeConditionId::none(),
            scope_matches_shadow_host: Default::default(),
            nested_declarations_context: NestedDeclarationsContext::Style,
        }
    }
}

struct SavedContainingRuleState {
    ancestor_selector_lists_len: usize,
    layer_name_len: usize,
    layer_id: LayerId,
    container_condition_id: ContainerConditionId,
    in_starting_style: bool,
    scope_condition_id: ScopeConditionId,
    scope_matches_shadow_host: ScopeMatchesShadowHost,
    nested_declarations_context: NestedDeclarationsContext,
}

impl ContainingRuleState {
    fn save(&self) -> SavedContainingRuleState {
        SavedContainingRuleState {
            ancestor_selector_lists_len: self.ancestor_selector_lists.len(),
            layer_name_len: self.layer_name.0.len(),
            layer_id: self.layer_id,
            container_condition_id: self.container_condition_id,
            in_starting_style: self.in_starting_style,
            scope_condition_id: self.scope_condition_id,
            scope_matches_shadow_host: self.scope_matches_shadow_host,
            nested_declarations_context: self.nested_declarations_context,
        }
    }

    fn restore(&mut self, saved: &SavedContainingRuleState) {
        debug_assert!(self.layer_name.0.len() >= saved.layer_name_len);
        debug_assert!(self.ancestor_selector_lists.len() >= saved.ancestor_selector_lists_len);
        self.ancestor_selector_lists
            .truncate(saved.ancestor_selector_lists_len);
        self.layer_name.0.truncate(saved.layer_name_len);
        self.layer_id = saved.layer_id;
        self.container_condition_id = saved.container_condition_id;
        self.in_starting_style = saved.in_starting_style;
        self.scope_condition_id = saved.scope_condition_id;
        self.scope_matches_shadow_host = saved.scope_matches_shadow_host;
        self.nested_declarations_context = saved.nested_declarations_context;
    }
}

type ReplacedSelectors = SmallVec<[Selector<SelectorImpl>; 4]>;

impl Stylist {
    /// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
    /// If more members are added here, think about whether they should
    /// be reset in clear().
    #[inline]
    pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
        Self {
            device,
            quirks_mode,
            stylesheets: StylistStylesheetSet::new(),
            author_data_cache: CascadeDataCache::new(),
            cascade_data: Default::default(),
            author_styles_enabled: AuthorStylesEnabled::Yes,
            rule_tree: RuleTree::new(),
            script_custom_properties: Default::default(),
            initial_values_for_custom_properties: Default::default(),
            initial_values_for_custom_properties_flags: Default::default(),
            num_rebuilds: 0,
        }
    }

    /// Returns the document cascade data.
    #[inline]
    pub fn cascade_data(&self) -> &DocumentCascadeData {
        &self.cascade_data
    }

    /// Returns whether author styles are enabled or not.
    #[inline]
    pub fn author_styles_enabled(&self) -> AuthorStylesEnabled {
        self.author_styles_enabled
    }

    /// Iterate through all the cascade datas from the document.
    #[inline]
    pub fn iter_origins(&self) -> DocumentCascadeDataIter {
        self.cascade_data.iter_origins()
    }

    /// Does what the name says, to prevent author_data_cache to grow without
    /// bound.
    pub fn remove_unique_author_data_cache_entries(&mut self) {
        self.author_data_cache.take_unused();
    }

    /// Returns the custom property registration for this property's name.
    /// https://drafts.css-houdini.org/css-properties-values-api-1/#determining-registration
    pub fn get_custom_property_registration(&self, name: &Atom) -> &PropertyRegistrationData {
        if let Some(registration) = self.custom_property_script_registry().get(name) {
            return ®istration.data;
        }
        for (data, _) in self.iter_origins() {
            if let Some(registration) = data.custom_property_registrations.get(name) {
                return ®istration.data;
            }
        }
        PropertyRegistrationData::unregistered()
    }

    /// Returns custom properties with their registered initial values.
    pub fn get_custom_property_initial_values(&self) -> &ComputedCustomProperties {
        &self.initial_values_for_custom_properties
    }

    /// Returns flags set from computing the registered custom property initial values.
    pub fn get_custom_property_initial_values_flags(&self) -> ComputedValueFlags {
        self.initial_values_for_custom_properties_flags
    }

    /// Rebuild custom properties with their registered initial values.
    /// https://drafts.css-houdini.org/css-properties-values-api-1/#determining-registration
    pub fn rebuild_initial_values_for_custom_properties(&mut self) {
        let mut initial_values = ComputedCustomProperties::default();
        let initial_values_flags;
        {
            let mut seen_names = PrecomputedHashSet::default();
            let mut rule_cache_conditions = RuleCacheConditions::default();
            let context = computed::Context::new_for_initial_at_property_value(
                self,
                &mut rule_cache_conditions,
            );

            for (k, v) in self.custom_property_script_registry().properties().iter() {
                seen_names.insert(k.clone());
                let Ok(value) = v.compute_initial_value(&context) else {
                    continue;
                };
                let map = if v.inherits() {
                    &mut initial_values.inherited
                } else {
                    &mut initial_values.non_inherited
                };
                map.insert(k, value);
            }
            for (data, _) in self.iter_origins() {
                for (k, v) in data.custom_property_registrations.iter() {
                    if seen_names.insert(k.clone()) {
                        let last_value = &v.last().unwrap().0;
                        let Ok(value) = last_value.compute_initial_value(&context) else {
                            continue;
                        };
                        let map = if last_value.inherits() {
                            &mut initial_values.inherited
                        } else {
                            &mut initial_values.non_inherited
                        };
                        map.insert(k, value);
                    }
                }
            }
            initial_values_flags = context.builder.flags();
        }
        self.initial_values_for_custom_properties_flags = initial_values_flags;
        self.initial_values_for_custom_properties = initial_values;
    }

    /// Rebuilds (if needed) the CascadeData given a sheet collection.
    pub fn rebuild_author_data<S>(
        &mut self,
        old_data: &CascadeData,
        collection: SheetCollectionFlusher<S>,
        guard: &SharedRwLockReadGuard,
    ) -> Result<Option<Arc<CascadeData>>, AllocErr>
    where
        S: StylesheetInDocument + PartialEq + 'static,
    {
        self.author_data_cache.lookup(
            &self.device,
            self.quirks_mode,
            collection,
            guard,
            old_data,
        )
    }

    /// Iterate over the extra data in origin order.
    #[inline]
    pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator {
        ExtraStyleDataIterator(self.cascade_data.iter_origins())
    }

    /// Iterate over the extra data in reverse origin order.
    #[inline]
    pub fn iter_extra_data_origins_rev(&self) -> ExtraStyleDataIterator {
        ExtraStyleDataIterator(self.cascade_data.iter_origins_rev())
    }

    /// Returns the number of selectors.
    pub fn num_selectors(&self) -> usize {
        self.cascade_data
            .iter_origins()
            .map(|(d, _)| d.num_selectors)
            .sum()
    }

    /// Returns the number of declarations.
    pub fn num_declarations(&self) -> usize {
        self.cascade_data
            .iter_origins()
            .map(|(d, _)| d.num_declarations)
            .sum()
    }

    /// Returns the number of times the stylist has been rebuilt.
    pub fn num_rebuilds(&self) -> usize {
        self.num_rebuilds
    }

    /// Returns the number of revalidation_selectors.
    pub fn num_revalidation_selectors(&self) -> usize {
        self.cascade_data
            .iter_origins()
            .map(|(data, _)| data.selectors_for_cache_revalidation.len())
            .sum()
    }

    /// Returns the number of entries in invalidation maps.
    pub fn num_invalidations(&self) -> usize {
        self.cascade_data
            .iter_origins()
            .map(|(data, _)| {
                data.invalidation_map.len() + data.relative_selector_invalidation_map.len()
            })
            .sum()
    }

    /// Returns whether the given DocumentState bit is relied upon by a selector
    /// of some rule.
    pub fn has_document_state_dependency(&self, state: DocumentState) -> bool {
        self.cascade_data
            .iter_origins()
            .any(|(d, _)| d.document_state_dependencies.intersects(state))
    }

    /// Flush the list of stylesheets if they changed, ensuring the stylist is
    /// up-to-date.
    pub fn flush<E>(
        &mut self,
        guards: &StylesheetGuards,
        document_element: Option<E>,
        snapshots: Option<&SnapshotMap>,
    ) -> bool
    where
        E: TElement,
    {
        if !self.stylesheets.has_changed() {
            return false;
        }

        self.num_rebuilds += 1;

        let flusher = self.stylesheets.flush(document_element, snapshots);

        let had_invalidations = flusher.had_invalidations();

        self.cascade_data
            .rebuild(&self.device, self.quirks_mode, flusher, guards)
            .unwrap_or_else(|_| warn!("OOM in Stylist::flush"));

        self.rebuild_initial_values_for_custom_properties();

        had_invalidations
    }

    /// Insert a given stylesheet before another stylesheet in the document.
    pub fn insert_stylesheet_before(
        &mut self,
        sheet: StylistSheet,
        before_sheet: StylistSheet,
        guard: &SharedRwLockReadGuard,
    ) {
        self.stylesheets
            .insert_stylesheet_before(Some(&self.device), sheet, before_sheet, guard)
    }

    /// Marks a given stylesheet origin as dirty, due to, for example, changes
    /// in the declarations that affect a given rule.
    ///
    /// FIXME(emilio): Eventually it'd be nice for this to become more
    /// fine-grained.
    pub fn force_stylesheet_origins_dirty(&mut self, origins: OriginSet) {
        self.stylesheets.force_dirty(origins)
    }

    /// Sets whether author style is enabled or not.
    pub fn set_author_styles_enabled(&mut self, enabled: AuthorStylesEnabled) {
        self.author_styles_enabled = enabled;
    }

    /// Returns whether we've recorded any stylesheet change so far.
    pub fn stylesheets_have_changed(&self) -> bool {
        self.stylesheets.has_changed()
    }

    /// Appends a new stylesheet to the current set.
    pub fn append_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {
        self.stylesheets
            .append_stylesheet(Some(&self.device), sheet, guard)
    }

    /// Remove a given stylesheet to the current set.
    pub fn remove_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {
        self.stylesheets
            .remove_stylesheet(Some(&self.device), sheet, guard)
    }

    /// Notify of a change of a given rule.
    pub fn rule_changed(
        &mut self,
        sheet: &StylistSheet,
        rule: &CssRule,
        guard: &SharedRwLockReadGuard,
        change_kind: RuleChangeKind,
    ) {
        self.stylesheets
            .rule_changed(Some(&self.device), sheet, rule, guard, change_kind)
    }

    /// Appends a new stylesheet to the current set.
    #[inline]
    pub fn sheet_count(&self, origin: Origin) -> usize {
        self.stylesheets.sheet_count(origin)
    }

    /// Appends a new stylesheet to the current set.
    #[inline]
    pub fn sheet_at(&self, origin: Origin, index: usize) -> Option<&StylistSheet> {
        self.stylesheets.get(origin, index)
    }

    /// Returns whether for any of the applicable style rule data a given
    /// condition is true.
    pub fn any_applicable_rule_data<E, F>(&self, element: E, mut f: F) -> bool
    where
        E: TElement,
        F: FnMut(&CascadeData) -> bool,
    {
        if f(&self.cascade_data.user_agent.cascade_data) {
            return true;
        }

        let mut maybe = false;

        let doc_author_rules_apply =
            element.each_applicable_non_document_style_rule_data(|data, _| {
                maybe = maybe || f(&*data);
            });

        if maybe || f(&self.cascade_data.user) {
            return true;
        }

        doc_author_rules_apply && f(&self.cascade_data.author)
    }

    /// Execute callback for all applicable style rule data.
    pub fn for_each_cascade_data_with_scope<'a, E, F>(&'a self, element: E, mut f: F)
    where
        E: TElement + 'a,
        F: FnMut(&'a CascadeData, Option<E>),
    {
        f(&self.cascade_data.user_agent.cascade_data, None);
        element.each_applicable_non_document_style_rule_data(|data, scope| {
            f(data, Some(scope));
        });
        f(&self.cascade_data.user, None);
        f(&self.cascade_data.author, None);
    }

    /// Computes the style for a given "precomputed" pseudo-element, taking the
    /// universal rules and applying them.
    pub fn precomputed_values_for_pseudo<E>(
        &self,
        guards: &StylesheetGuards,
        pseudo: &PseudoElement,
        parent: Option<&ComputedValues>,
    ) -> Arc<ComputedValues>
    where
        E: TElement,
    {
        debug_assert!(pseudo.is_precomputed());

        let rule_node = self.rule_node_for_precomputed_pseudo(guards, pseudo, vec![]);

        self.precomputed_values_for_pseudo_with_rule_node::<E>(guards, pseudo, parent, rule_node)
    }

    /// Computes the style for a given "precomputed" pseudo-element with
    /// given rule node.
    ///
    /// TODO(emilio): The type parameter could go away with a void type
    /// implementing TElement.
    pub fn precomputed_values_for_pseudo_with_rule_node<E>(
        &self,
        guards: &StylesheetGuards,
        pseudo: &PseudoElement,
        parent: Option<&ComputedValues>,
        rules: StrongRuleNode,
    ) -> Arc<ComputedValues>
    where
        E: TElement,
    {
        self.compute_pseudo_element_style_with_inputs::<E>(
            CascadeInputs {
                rules: Some(rules),
                visited_rules: None,
                flags: Default::default(),
            },
            pseudo,
            guards,
            parent,
            /* element */ None,
        )
    }

    /// Returns the rule node for a given precomputed pseudo-element.
    ///
    /// If we want to include extra declarations to this precomputed
    /// pseudo-element, we can provide a vector of ApplicableDeclarationBlocks
    /// to extra_declarations. This is useful for @page rules.
    pub fn rule_node_for_precomputed_pseudo(
        &self,
        guards: &StylesheetGuards,
        pseudo: &PseudoElement,
        mut extra_declarations: Vec<ApplicableDeclarationBlock>,
    ) -> StrongRuleNode {
        let mut declarations_with_extra;
        let declarations = match self
            .cascade_data
            .user_agent
            .precomputed_pseudo_element_decls
            .get(pseudo)
        {
            Some(declarations) => {
                if !extra_declarations.is_empty() {
                    declarations_with_extra = declarations.clone();
                    declarations_with_extra.append(&mut extra_declarations);
                    &*declarations_with_extra
                } else {
                    &**declarations
                }
            },
            None => &[],
        };

        self.rule_tree.insert_ordered_rules_with_important(
            declarations.into_iter().map(|a| a.clone().for_rule_tree()),
            guards,
        )
    }

    /// Returns the style for an anonymous box of the given type.
    ///
    /// TODO(emilio): The type parameter could go away with a void type
    /// implementing TElement.
    #[cfg(feature = "servo")]
    pub fn style_for_anonymous<E>(
        &self,
        guards: &StylesheetGuards,
        pseudo: &PseudoElement,
        parent_style: &ComputedValues,
    ) -> Arc<ComputedValues>
    where
        E: TElement,
    {
        self.precomputed_values_for_pseudo::<E>(guards, &pseudo, Some(parent_style))
    }

    /// Computes a pseudo-element style lazily during layout.
    ///
    /// This can only be done for a certain set of pseudo-elements, like
    /// :selection.
    ///
    /// Check the documentation on lazy pseudo-elements in
    /// docs/components/style.md
    pub fn lazily_compute_pseudo_element_style<E>(
        &self,
        guards: &StylesheetGuards,
        element: E,
        pseudo: &PseudoElement,
        rule_inclusion: RuleInclusion,
        originating_element_style: &ComputedValues,
        is_probe: bool,
        matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
    ) -> Option<Arc<ComputedValues>>
    where
        E: TElement,
    {
        let cascade_inputs = self.lazy_pseudo_rules(
            guards,
            element,
            originating_element_style,
            pseudo,
            is_probe,
            rule_inclusion,
            matching_fn,
        )?;

        Some(self.compute_pseudo_element_style_with_inputs(
            cascade_inputs,
            pseudo,
            guards,
            Some(originating_element_style),
            Some(element),
        ))
    }

    /// Computes a pseudo-element style lazily using the given CascadeInputs.
    /// This can be used for truly lazy pseudo-elements or to avoid redoing
    /// selector matching for eager pseudo-elements when we need to recompute
    /// their style with a new parent style.
    pub fn compute_pseudo_element_style_with_inputs<E>(
        &self,
        inputs: CascadeInputs,
        pseudo: &PseudoElement,
        guards: &StylesheetGuards,
        parent_style: Option<&ComputedValues>,
        element: Option<E>,
    ) -> Arc<ComputedValues>
    where
        E: TElement,
    {
        // FIXME(emilio): The lack of layout_parent_style here could be
        // worrying, but we're probably dropping the display fixup for
        // pseudos other than before and after, so it's probably ok.
        //
        // (Though the flags don't indicate so!)
        //
        // It'd be fine to assert that this isn't called with a parent style
        // where display contents is in effect, but in practice this is hard to
        // do for stuff like :-moz-fieldset-content with a
        // <fieldset style="display: contents">. That is, the computed value of
        // display for the fieldset is "contents", even though it's not the used
        // value, so we don't need to adjust in a different way anyway.
        self.cascade_style_and_visited(
            element,
            Some(pseudo),
            inputs,
            guards,
            parent_style,
            parent_style,
            FirstLineReparenting::No,
            /* rule_cache = */ None,
            &mut RuleCacheConditions::default(),
        )
    }

    /// Computes a style using the given CascadeInputs.  This can be used to
    /// compute a style any time we know what rules apply and just need to use
    /// the given parent styles.
    ///
    /// parent_style is the style to inherit from for properties affected by
    /// first-line ancestors.
    ///
    /// parent_style_ignoring_first_line is the style to inherit from for
    /// properties not affected by first-line ancestors.
    ///
    /// layout_parent_style is the style used for some property fixups.  It's
    /// the style of the nearest ancestor with a layout box.
    pub fn cascade_style_and_visited<E>(
        &self,
        element: Option<E>,
        pseudo: Option<&PseudoElement>,
        inputs: CascadeInputs,
        guards: &StylesheetGuards,
        parent_style: Option<&ComputedValues>,
        layout_parent_style: Option<&ComputedValues>,
        first_line_reparenting: FirstLineReparenting,
        rule_cache: Option<&RuleCache>,
        rule_cache_conditions: &mut RuleCacheConditions,
    ) -> Arc<ComputedValues>
    where
        E: TElement,
    {
        debug_assert!(pseudo.is_some() || element.is_some(), "Huh?");

        // We need to compute visited values if we have visited rules or if our
        // parent has visited values.
        let visited_rules = match inputs.visited_rules.as_ref() {
            Some(rules) => Some(rules),
            None => {
                if parent_style.and_then(|s| s.visited_style()).is_some() {
                    Some(inputs.rules.as_ref().unwrap_or(self.rule_tree.root()))
                } else {
                    None
                }
            },
        };

        // Read the comment on `precomputed_values_for_pseudo` to see why it's
        // difficult to assert that display: contents nodes never arrive here
        // (tl;dr: It doesn't apply for replaced elements and such, but the
        // computed value is still "contents").
        //
        // FIXME(emilio): We should assert that it holds if pseudo.is_none()!
        properties::cascade::<E>(
            &self,
            pseudo,
            inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
            guards,
            parent_style,
            layout_parent_style,
            first_line_reparenting,
            visited_rules,
            inputs.flags,
            rule_cache,
            rule_cache_conditions,
            element,
        )
    }

    /// Computes the cascade inputs for a lazily-cascaded pseudo-element.
    ///
    /// See the documentation on lazy pseudo-elements in
    /// docs/components/style.md
    fn lazy_pseudo_rules<E>(
        &self,
        guards: &StylesheetGuards,
        element: E,
        originating_element_style: &ComputedValues,
        pseudo: &PseudoElement,
        is_probe: bool,
        rule_inclusion: RuleInclusion,
        matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
    ) -> Option<CascadeInputs>
    where
        E: TElement,
    {
        debug_assert!(pseudo.is_lazy());

        let mut selector_caches = SelectorCaches::default();
        // No need to bother setting the selector flags when we're computing
        // default styles.
        let needs_selector_flags = if rule_inclusion == RuleInclusion::DefaultOnly {
            NeedsSelectorFlags::No
        } else {
            NeedsSelectorFlags::Yes
        };

        let mut declarations = ApplicableDeclarationList::new();
        let mut matching_context = MatchingContext::<'_, E::Impl>::new(
            MatchingMode::ForStatelessPseudoElement,
            None,
            &mut selector_caches,
            self.quirks_mode,
            needs_selector_flags,
            MatchingForInvalidation::No,
        );

        matching_context.pseudo_element_matching_fn = matching_fn;
        matching_context.extra_data.originating_element_style = Some(originating_element_style);

        self.push_applicable_declarations(
            element,
            Some(&pseudo),
            None,
            None,
            /* animation_declarations = */ Default::default(),
            rule_inclusion,
            &mut declarations,
            &mut matching_context,
        );

        if declarations.is_empty() && is_probe {
            return None;
        }

        let rules = self.rule_tree.compute_rule_node(&mut declarations, guards);

        let mut visited_rules = None;
        if originating_element_style.visited_style().is_some() {
            let mut declarations = ApplicableDeclarationList::new();
            let mut selector_caches = SelectorCaches::default();

            let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
                MatchingMode::ForStatelessPseudoElement,
                None,
                &mut selector_caches,
                VisitedHandlingMode::RelevantLinkVisited,
                selectors::matching::IncludeStartingStyle::No,
                self.quirks_mode,
                needs_selector_flags,
                MatchingForInvalidation::No,
            );
            matching_context.pseudo_element_matching_fn = matching_fn;
            matching_context.extra_data.originating_element_style = Some(originating_element_style);

            self.push_applicable_declarations(
                element,
                Some(&pseudo),
                None,
                None,
                /* animation_declarations = */ Default::default(),
                rule_inclusion,
                &mut declarations,
                &mut matching_context,
            );
            if !declarations.is_empty() {
                let rule_node = self.rule_tree.insert_ordered_rules_with_important(
                    declarations.drain(..).map(|a| a.for_rule_tree()),
                    guards,
                );
                if rule_node != *self.rule_tree.root() {
                    visited_rules = Some(rule_node);
                }
            }
        }

        Some(CascadeInputs {
            rules: Some(rules),
            visited_rules,
            flags: matching_context.extra_data.cascade_input_flags,
        })
    }

    /// Set a given device, which may change the styles that apply to the
    /// document.
    ///
    /// Returns the sheet origins that were actually affected.
    ///
    /// This means that we may need to rebuild style data even if the
    /// stylesheets haven't changed.
    ///
    /// Also, the device that arrives here may need to take the viewport rules
    /// into account.
    pub fn set_device(&mut self, device: Device, guards: &StylesheetGuards) -> OriginSet {
        self.device = device;
        self.media_features_change_changed_style(guards, &self.device)
    }

    /// Returns whether, given a media feature change, any previously-applicable
    /// style has become non-applicable, or vice-versa for each origin, using
    /// `device`.
    pub fn media_features_change_changed_style(
        &self,
        guards: &StylesheetGuards,
        device: &Device,
    ) -> OriginSet {
        debug!("Stylist::media_features_change_changed_style {:?}", device);

        let mut origins = OriginSet::empty();
        let stylesheets = self.stylesheets.iter();

        for (stylesheet, origin) in stylesheets {
            if origins.contains(origin.into()) {
                continue;
            }

            let guard = guards.for_origin(origin);
            let origin_cascade_data = self.cascade_data.borrow_for_origin(origin);

            let affected_changed = !origin_cascade_data.media_feature_affected_matches(
                stylesheet,
                guard,
                device,
                self.quirks_mode,
            );

            if affected_changed {
                origins |= origin;
            }
        }

        origins
    }

    /// Returns the Quirks Mode of the document.
    pub fn quirks_mode(&self) -> QuirksMode {
        self.quirks_mode
    }

    /// Sets the quirks mode of the document.
    pub fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {
        if self.quirks_mode == quirks_mode {
            return;
        }
        self.quirks_mode = quirks_mode;
        self.force_stylesheet_origins_dirty(OriginSet::all());
    }

    /// Returns the applicable CSS declarations for the given element.
    pub fn push_applicable_declarations<E>(
        &self,
        element: E,
        pseudo_element: Option<&PseudoElement>,
        style_attribute: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
        smil_override: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
        animation_declarations: AnimationDeclarations,
        rule_inclusion: RuleInclusion,
        applicable_declarations: &mut ApplicableDeclarationList,
        context: &mut MatchingContext<E::Impl>,
    ) where
        E: TElement,
    {
        RuleCollector::new(
            self,
            element,
            pseudo_element,
            style_attribute,
            smil_override,
            animation_declarations,
            rule_inclusion,
            applicable_declarations,
            context,
        )
        .collect_all();
    }

    /// Given an id, returns whether there might be any rules for that id in any
    /// of our rule maps.
    #[inline]
    pub fn may_have_rules_for_id<E>(&self, id: &WeakAtom, element: E) -> bool
    where
        E: TElement,
    {
        // If id needs to be compared case-insensitively, the logic below
        // wouldn't work. Just conservatively assume it may have such rules.
        match self.quirks_mode().classes_and_ids_case_sensitivity() {
            CaseSensitivity::AsciiCaseInsensitive => return true,
            CaseSensitivity::CaseSensitive => {},
        }

        self.any_applicable_rule_data(element, |data| data.mapped_ids.contains(id))
    }

    /// Returns the registered `@keyframes` animation for the specified name.
    #[inline]
    pub fn get_animation<'a, E>(&'a self, name: &Atom, element: E) -> Option<&'a KeyframesAnimation>
    where
        E: TElement + 'a,
    {
        macro_rules! try_find_in {
            ($data:expr) => {
                if let Some(animation) = $data.animations.get(name) {
                    return Some(animation);
                }
            };
        }

        // NOTE(emilio): This is a best-effort thing, the right fix is a bit TBD because it
        // involves "recording" which tree the name came from, see [1][2].
        //
        // [1]: https://github.com/w3c/csswg-drafts/issues/1995
        // [2]: https://bugzil.la/1458189
        let mut animation = None;
        let doc_rules_apply =
            element.each_applicable_non_document_style_rule_data(|data, _host| {
                if animation.is_none() {
                    animation = data.animations.get(name);
                }
            });

        if animation.is_some() {
            return animation;
        }

        if doc_rules_apply {
            try_find_in!(self.cascade_data.author);
        }
        try_find_in!(self.cascade_data.user);
        try_find_in!(self.cascade_data.user_agent.cascade_data);

        None
    }

    /// Computes the match results of a given element against the set of
    /// revalidation selectors.
    pub fn match_revalidation_selectors<E>(
        &self,
        element: E,
        bloom: Option<&BloomFilter>,
        selector_caches: &mut SelectorCaches,
        needs_selector_flags: NeedsSelectorFlags,
    ) -> RevalidationResult
    where
        E: TElement,
    {
        // NB: `MatchingMode` doesn't really matter, given we don't share style
        // between pseudos.
        let mut matching_context = MatchingContext::new(
            MatchingMode::Normal,
            bloom,
            selector_caches,
            self.quirks_mode,
            needs_selector_flags,
            MatchingForInvalidation::No,
        );

        // Note that, by the time we're revalidating, we're guaranteed that the
        // candidate and the entry have the same id, classes, and local name.
        // This means we're guaranteed to get the same rulehash buckets for all
        // the lookups, which means that the bitvecs are comparable. We verify
        // this in the caller by asserting that the bitvecs are same-length.
        let mut result = RevalidationResult::default();
        let mut relevant_attributes = &mut result.relevant_attributes;
        let selectors_matched = &mut result.selectors_matched;

        let matches_document_rules =
            element.each_applicable_non_document_style_rule_data(|data, host| {
                matching_context.with_shadow_host(Some(host), |matching_context| {
                    data.selectors_for_cache_revalidation.lookup(
                        element,
                        self.quirks_mode,
                        Some(&mut relevant_attributes),
                        |selector_and_hashes| {
                            selectors_matched.push(matches_selector(
                                &selector_and_hashes.selector,
                                selector_and_hashes.selector_offset,
                                Some(&selector_and_hashes.hashes),
                                &element,
                                matching_context,
                            ));
                            true
                        },
                    );
                })
            });

        for (data, origin) in self.cascade_data.iter_origins() {
            if origin == Origin::Author && !matches_document_rules {
                continue;
            }

            data.selectors_for_cache_revalidation.lookup(
                element,
                self.quirks_mode,
                Some(&mut relevant_attributes),
                |selector_and_hashes| {
                    selectors_matched.push(matches_selector(
                        &selector_and_hashes.selector,
                        selector_and_hashes.selector_offset,
                        Some(&selector_and_hashes.hashes),
                        &element,
                        &mut matching_context,
                    ));
                    true
                },
            );
        }

        result
    }

    /// Computes currently active scopes for the given element for revalidation purposes.
    pub fn revalidate_scopes<E: TElement>(
        &self,
        element: &E,
        selector_caches: &mut SelectorCaches,
        needs_selector_flags: NeedsSelectorFlags,
    ) -> ScopeRevalidationResult {
        let mut matching_context = MatchingContext::new(
            MatchingMode::Normal,
            None,
            selector_caches,
            self.quirks_mode,
            needs_selector_flags,
            MatchingForInvalidation::No,
        );

        let mut result = ScopeRevalidationResult::default();
        let matches_document_rules =
            element.each_applicable_non_document_style_rule_data(|data, host| {
                matching_context.with_shadow_host(Some(host), |matching_context| {
                    data.revalidate_scopes(self, element, matching_context, &mut result);
                })
            });

        for (data, origin) in self.cascade_data.iter_origins() {
            if origin == Origin::Author && !matches_document_rules {
                continue;
            }

            data.revalidate_scopes(self, element, &mut matching_context, &mut result);
        }

        result
    }

    /// Computes styles for a given declaration with parent_style.
    ///
    /// FIXME(emilio): the lack of pseudo / cascade flags look quite dubious,
    /// hopefully this is only used for some canvas font stuff.
    ///
    /// TODO(emilio): The type parameter can go away when
    /// https://github.com/rust-lang/rust/issues/35121 is fixed.
    pub fn compute_for_declarations<E>(
        &self,
        guards: &StylesheetGuards,
        parent_style: &ComputedValues,
        declarations: Arc<Locked<PropertyDeclarationBlock>>,
    ) -> Arc<ComputedValues>
    where
        E: TElement,
    {
        let block = declarations.read_with(guards.author);

        // We don't bother inserting these declarations in the rule tree, since
        // it'd be quite useless and slow.
        //
        // TODO(emilio): Now that we fixed bug 1493420, we should consider
        // reversing this as it shouldn't be slow anymore, and should avoid
        // generating two instantiations of apply_declarations.
        properties::apply_declarations::<E, _>(
            &self,
            /* pseudo = */ None,
            self.rule_tree.root(),
            guards,
            block.declaration_importance_iter().map(|(declaration, _)| {
                (
                    declaration,
                    CascadePriority::new(
                        CascadeLevel::same_tree_author_normal(),
                        LayerOrder::root(),
                    ),
                )
            }),
            Some(parent_style),
            Some(parent_style),
            FirstLineReparenting::No,
            CascadeMode::Unvisited {
                visited_rules: None,
            },
            Default::default(),
            /* rule_cache = */ None,
            &mut Default::default(),
            /* element = */ None,
        )
    }

    /// Accessor for a shared reference to the device.
    #[inline]
    pub fn device(&self) -> &Device {
        &self.device
    }

    /// Accessor for a mutable reference to the device.
    #[inline]
    pub fn device_mut(&mut self) -> &mut Device {
        &mut self.device
    }

    /// Accessor for a shared reference to the rule tree.
    #[inline]
    pub fn rule_tree(&self) -> &RuleTree {
        &self.rule_tree
    }

    /// Returns the script-registered custom property registry.
    #[inline]
    pub fn custom_property_script_registry(&self) -> &CustomPropertyScriptRegistry {
        &self.script_custom_properties
    }

    /// Returns the script-registered custom property registry, as a mutable ref.
    #[inline]
    pub fn custom_property_script_registry_mut(&mut self) -> &mut CustomPropertyScriptRegistry {
        &mut self.script_custom_properties
    }

    /// Measures heap usage.
    #[cfg(feature = "gecko")]
    pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
        self.cascade_data.add_size_of(ops, sizes);
        self.author_data_cache.add_size_of(ops, sizes);
        sizes.mRuleTree += self.rule_tree.size_of(ops);

        // We may measure other fields in the future if DMD says it's worth it.
    }

    /// Shutdown the static data that this module stores.
    pub fn shutdown() {
        let _entries = UA_CASCADE_DATA_CACHE.lock().unwrap().take_all();
    }
}

/// A vector that is sorted in layer order.
#[derive(Clone, Debug, Deref, MallocSizeOf)]
pub struct LayerOrderedVec<T>(Vec<(T, LayerId)>);
impl<T> Default for LayerOrderedVec<T> {
    fn default() -> Self {
        Self(Default::default())
    }
}

/// A map that is sorted in layer order.
#[derive(Clone, Debug, Deref, MallocSizeOf)]
pub struct LayerOrderedMap<T>(PrecomputedHashMap<Atom, SmallVec<[(T, LayerId); 1]>>);
impl<T> Default for LayerOrderedMap<T> {
    fn default() -> Self {
        Self(Default::default())
    }
}

#[cfg(feature = "gecko")]
impl<T: 'static> LayerOrderedVec<T> {
    fn clear(&mut self) {
        self.0.clear();
    }
    fn push(&mut self, v: T, id: LayerId) {
        self.0.push((v, id));
    }
    fn sort(&mut self, layers: &[CascadeLayer]) {
        self.0
            .sort_by_key(|&(_, ref id)| layers[id.0 as usize].order)
    }
}

impl<T: 'static> LayerOrderedMap<T> {
    fn shrink_if_needed(&mut self) {
        self.0.shrink_if_needed();
    }
    fn clear(&mut self) {
        self.0.clear();
    }
    fn try_insert(&mut self, name: Atom, v: T, id: LayerId) -> Result<(), AllocErr> {
        self.try_insert_with(name, v, id, |_, _| Ordering::Equal)
    }
    fn try_insert_with(
        &mut self,
        name: Atom,
        v: T,
        id: LayerId,
        cmp: impl Fn(&T, &T) -> Ordering,
    ) -> Result<(), AllocErr> {
        self.0.try_reserve(1)?;
        let vec = self.0.entry(name).or_default();
        if let Some(&mut (ref mut val, ref last_id)) = vec.last_mut() {
            if *last_id == id {
                if cmp(&val, &v) != Ordering::Greater {
                    *val = v;
                }
                return Ok(());
            }
        }
        vec.push((v, id));
        Ok(())
    }
    fn sort(&mut self, layers: &[CascadeLayer]) {
        self.sort_with(layers, |_, _| Ordering::Equal)
    }
    fn sort_with(&mut self, layers: &[CascadeLayer], cmp: impl Fn(&T, &T) -> Ordering) {
        for (_, v) in self.0.iter_mut() {
            v.sort_by(|&(ref v1, ref id1), &(ref v2, ref id2)| {
                let order1 = layers[id1.0 as usize].order;
                let order2 = layers[id2.0 as usize].order;
                order1.cmp(&order2).then_with(|| cmp(v1, v2))
            })
        }
    }
    /// Get an entry on the LayerOrderedMap by name.
    pub fn get(&self, name: &Atom) -> Option<&T> {
        let vec = self.0.get(name)?;
        Some(&vec.last()?.0)
    }
}

/// Wrapper to allow better tracking of memory usage by page rule lists.
///
/// This includes the layer ID for use with the named page table.
#[derive(Clone, Debug, MallocSizeOf)]
pub struct PageRuleData {
    /// Layer ID for sorting page rules after matching.
    pub layer: LayerId,
    /// Page rule
    #[ignore_malloc_size_of = "Arc, stylesheet measures as primary ref"]
    pub rule: Arc<Locked<PageRule>>,
}

/// Stores page rules indexed by page names.
#[derive(Clone, Debug, Default, MallocSizeOf)]
pub struct PageRuleMap {
    /// Page rules, indexed by page name. An empty atom indicates no page name.
    pub rules: PrecomputedHashMap<Atom, SmallVec<[PageRuleData; 1]>>,
}

#[cfg(feature = "gecko")]
impl PageRuleMap {
    #[inline]
    fn clear(&mut self) {
        self.rules.clear();
    }

    /// Uses page-name and pseudo-classes to match all applicable
    /// page-rules and append them to the matched_rules vec.
    /// This will ensure correct rule order for cascading.
    pub fn match_and_append_rules(
        &self,
        matched_rules: &mut Vec<ApplicableDeclarationBlock>,
        origin: Origin,
        guards: &StylesheetGuards,
        cascade_data: &DocumentCascadeData,
        name: &Option<Atom>,
        pseudos: PagePseudoClassFlags,
    ) {
        let level = match origin {
            Origin::UserAgent => CascadeLevel::UANormal,
            Origin::User => CascadeLevel::UserNormal,
--> --------------------

--> maximum size reached

--> --------------------

[ zur Elbe Produktseite wechseln0.48Quellennavigators  Analyse erneut starten  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge