tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

selector_parser.rs (23811B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! Gecko-specific bits for selector-parsing.
      6 
      7 use crate::computed_value_flags::ComputedValueFlags;
      8 use crate::derives::*;
      9 use crate::invalidation::element::document_state::InvalidationMatchingData;
     10 use crate::properties::ComputedValues;
     11 use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser};
     12 use crate::str::starts_with_ignore_ascii_case;
     13 use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
     14 use crate::values::{AtomIdent, AtomString, CSSInteger, CustomIdent};
     15 use cssparser::{match_ignore_ascii_case, CowRcStr, SourceLocation, ToCss, Token};
     16 use cssparser::{BasicParseError, BasicParseErrorKind, Parser};
     17 use dom::{DocumentState, ElementState, HEADING_LEVEL_OFFSET};
     18 use selectors::parser::SelectorParseErrorKind;
     19 use std::fmt;
     20 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
     21 use thin_vec::ThinVec;
     22 
     23 pub use crate::gecko::pseudo_element::{
     24    PseudoElement, Target, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT,
     25 };
     26 pub use crate::gecko::snapshot::SnapshotMap;
     27 
     28 bitflags! {
     29    // See NonTSPseudoClass::is_enabled_in()
     30    #[derive(Copy, Clone)]
     31    struct NonTSPseudoClassFlag: u8 {
     32        const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS = 1 << 0;
     33        const PSEUDO_CLASS_ENABLED_IN_CHROME = 1 << 1;
     34        const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME =
     35            NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS.bits() |
     36            NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_CHROME.bits();
     37    }
     38 }
     39 
     40 /// The type used to store the language argument to the `:lang` pseudo-class.
     41 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
     42 #[css(comma)]
     43 pub struct Lang(#[css(iterable)] pub ThinVec<AtomIdent>);
     44 
     45 /// The type used to store the state argument to the `:state` pseudo-class.
     46 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
     47 pub struct CustomState(pub AtomIdent);
     48 
     49 /// The properties that comprise a :heading() pseudoclass (e.g. a list of An+Bs).
     50 /// https://drafts.csswg.org/selectors-5/#headings
     51 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
     52 pub struct HeadingSelectorData(pub ThinVec<CSSInteger>);
     53 
     54 impl HeadingSelectorData {
     55    /// Matches the heading level from the given state against the list of
     56    /// heading level selectors. If AnPlusBs intersect with the level packed in
     57    /// ElementState then this will return true.
     58    pub fn matches_state(&self, state: ElementState) -> bool {
     59        let bits = state.intersection(ElementState::HEADING_LEVEL_BITS).bits();
     60        // If none of the HEADING_LEVEL_BITS are set on ElementState,
     61        // then this is not a heading level, so return false.
     62        if bits == 0 {
     63            return false;
     64        }
     65        // :heading selector will provide an empty levels list. It matches against any
     66        // heading level, so we can return true here.
     67        if self.0.is_empty() {
     68            return true;
     69        }
     70        let level = (bits >> HEADING_LEVEL_OFFSET) as i32;
     71        debug_assert!(level > 0 && level < 16);
     72        self.0.iter().any(|item| *item == level)
     73    }
     74 }
     75 
     76 macro_rules! pseudo_class_name {
     77    ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
     78        /// Our representation of a non tree-structural pseudo-class.
     79        #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
     80        pub enum NonTSPseudoClass {
     81            $(
     82                #[doc = $css]
     83                $name,
     84            )*
     85            /// The `:lang` pseudo-class.
     86            Lang(Lang),
     87            /// The `:dir` pseudo-class.
     88            Dir(Direction),
     89            /// The :state` pseudo-class.
     90            CustomState(CustomState),
     91            /// The `:heading` & `:heading()` pseudo-classes.
     92            Heading(HeadingSelectorData),
     93            /// The :active-view-transition-type() pseudo-class:
     94            /// https://drafts.csswg.org/css-view-transitions-2/#the-active-view-transition-type-pseudo
     95            ActiveViewTransitionType(ThinVec<CustomIdent>),
     96            /// The non-standard `:-moz-locale-dir` pseudo-class.
     97            MozLocaleDir(Direction),
     98        }
     99    }
    100 }
    101 apply_non_ts_list!(pseudo_class_name);
    102 
    103 impl ToCss for NonTSPseudoClass {
    104    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
    105    where
    106        W: fmt::Write,
    107    {
    108        macro_rules! pseudo_class_serialize {
    109            ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
    110                match *self {
    111                    $(NonTSPseudoClass::$name => concat!(":", $css),)*
    112                    NonTSPseudoClass::Lang(ref lang) => {
    113                        dest.write_str(":lang(")?;
    114                        lang.to_css(&mut CssWriter::new(dest))?;
    115                        return dest.write_char(')');
    116                    },
    117                    NonTSPseudoClass::CustomState(ref state) => {
    118                        dest.write_str(":state(")?;
    119                        state.to_css(&mut CssWriter::new(dest))?;
    120                        return dest.write_char(')');
    121                    },
    122                    NonTSPseudoClass::MozLocaleDir(ref dir) => {
    123                        dest.write_str(":-moz-locale-dir(")?;
    124                        dir.to_css(&mut CssWriter::new(dest))?;
    125                        return dest.write_char(')')
    126                    },
    127                    NonTSPseudoClass::Dir(ref dir) => {
    128                        dest.write_str(":dir(")?;
    129                        dir.to_css(&mut CssWriter::new(dest))?;
    130                        return dest.write_char(')')
    131                    },
    132                    NonTSPseudoClass::ActiveViewTransitionType(ref types) => {
    133                        dest.write_str(":active-view-transition-type(")?;
    134                        let mut first = true;
    135                        for ty in types.iter() {
    136                            if !first {
    137                                dest.write_str(", ")?;
    138                            }
    139                            first = false;
    140                            ty.to_css(&mut CssWriter::new(dest))?;
    141                        }
    142                        return dest.write_char(')')
    143                    },
    144                    NonTSPseudoClass::Heading(ref levels) => {
    145                        dest.write_str(":heading")?;
    146                        if levels.0.is_empty() {
    147                            return Ok(());
    148                        }
    149                        dest.write_str("(")?;
    150                        let mut first = true;
    151                        for item in levels.0.iter() {
    152                            if !first {
    153                                dest.write_str(", ")?;
    154                            }
    155                            first = false;
    156                            ToCss::to_css(item, dest)?;
    157                        }
    158                        return dest.write_str(")");
    159                    },
    160                }
    161            }
    162        }
    163        let ser = apply_non_ts_list!(pseudo_class_serialize);
    164        dest.write_str(ser)
    165    }
    166 }
    167 
    168 impl NonTSPseudoClass {
    169    /// Parses the name and returns a non-ts-pseudo-class if succeeds.
    170    /// None otherwise. It doesn't check whether the pseudo-class is enabled
    171    /// in a particular state.
    172    pub fn parse_non_functional(name: &str) -> Option<Self> {
    173        macro_rules! pseudo_class_parse {
    174            ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
    175                match_ignore_ascii_case! { &name,
    176                    $($css => Some(NonTSPseudoClass::$name),)*
    177                    "heading" => Some(NonTSPseudoClass::Heading(HeadingSelectorData([].into()))),
    178                    "-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen),
    179                    "-moz-read-only" => Some(NonTSPseudoClass::ReadOnly),
    180                    "-moz-read-write" => Some(NonTSPseudoClass::ReadWrite),
    181                    "-moz-focusring" => Some(NonTSPseudoClass::FocusVisible),
    182                    "-moz-ui-valid" => Some(NonTSPseudoClass::UserValid),
    183                    "-moz-ui-invalid" => Some(NonTSPseudoClass::UserInvalid),
    184                    "-webkit-autofill" => Some(NonTSPseudoClass::Autofill),
    185                    _ => None,
    186                }
    187            }
    188        }
    189        apply_non_ts_list!(pseudo_class_parse)
    190    }
    191 
    192    /// Returns true if this pseudo-class has any of the given flags set.
    193    fn has_any_flag(&self, flags: NonTSPseudoClassFlag) -> bool {
    194        macro_rules! check_flag {
    195            (_) => {
    196                false
    197            };
    198            ($flags:ident) => {
    199                NonTSPseudoClassFlag::$flags.intersects(flags)
    200            };
    201        }
    202        macro_rules! pseudo_class_check_is_enabled_in {
    203            ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
    204                match *self {
    205                    $(NonTSPseudoClass::$name => check_flag!($flags),)*
    206                    NonTSPseudoClass::MozLocaleDir(_) => check_flag!(PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
    207                    NonTSPseudoClass::CustomState(_) |
    208                    NonTSPseudoClass::Heading(_) |
    209                    NonTSPseudoClass::Lang(_) |
    210                    NonTSPseudoClass::ActiveViewTransitionType(_) |
    211                    NonTSPseudoClass::Dir(_) => false,
    212                }
    213            }
    214        }
    215        apply_non_ts_list!(pseudo_class_check_is_enabled_in)
    216    }
    217 
    218    /// Returns whether the pseudo-class is enabled in content sheets.
    219    #[inline]
    220    fn is_enabled_in_content(&self) -> bool {
    221        if matches!(
    222            *self,
    223            Self::ActiveViewTransition | Self::ActiveViewTransitionType(..)
    224        ) {
    225            return static_prefs::pref!("dom.viewTransitions.enabled");
    226        }
    227        if matches!(*self, Self::Heading(..)) {
    228            return static_prefs::pref!("layout.css.heading-selector.enabled");
    229        }
    230        !self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME)
    231    }
    232 
    233    /// Get the state flag associated with a pseudo-class, if any.
    234    pub fn state_flag(&self) -> ElementState {
    235        macro_rules! flag {
    236            (_) => {
    237                ElementState::empty()
    238            };
    239            ($state:ident) => {
    240                ElementState::$state
    241            };
    242        }
    243        macro_rules! pseudo_class_state {
    244            ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
    245                match *self {
    246                    $(NonTSPseudoClass::$name => flag!($state),)*
    247                    NonTSPseudoClass::Dir(ref dir) => dir.element_state(),
    248                    NonTSPseudoClass::Heading(..) => ElementState::HEADING_LEVEL_BITS,
    249                    NonTSPseudoClass::ActiveViewTransitionType(..) => ElementState::ACTIVE_VIEW_TRANSITION,
    250                    NonTSPseudoClass::MozLocaleDir(..) |
    251                    NonTSPseudoClass::CustomState(..) |
    252                    NonTSPseudoClass::Lang(..) => ElementState::empty(),
    253                }
    254            }
    255        }
    256        apply_non_ts_list!(pseudo_class_state)
    257    }
    258 
    259    /// Get the document state flag associated with a pseudo-class, if any.
    260    pub fn document_state_flag(&self) -> DocumentState {
    261        match *self {
    262            NonTSPseudoClass::MozLocaleDir(ref dir) => match dir.as_horizontal_direction() {
    263                Some(HorizontalDirection::Ltr) => DocumentState::LTR_LOCALE,
    264                Some(HorizontalDirection::Rtl) => DocumentState::RTL_LOCALE,
    265                None => DocumentState::empty(),
    266            },
    267            NonTSPseudoClass::MozWindowInactive => DocumentState::WINDOW_INACTIVE,
    268            _ => DocumentState::empty(),
    269        }
    270    }
    271 
    272    /// Returns true if the given pseudoclass should trigger style sharing cache
    273    /// revalidation.
    274    pub fn needs_cache_revalidation(&self) -> bool {
    275        self.state_flag().is_empty()
    276            && !matches!(
    277                *self,
    278                // :dir() depends on state only, but may have an empty state_flag for invalid
    279                // arguments.
    280                NonTSPseudoClass::Dir(_) |
    281                      // We prevent style sharing for NAC.
    282                      NonTSPseudoClass::MozNativeAnonymous |
    283                      // :-moz-placeholder is parsed but never matches.
    284                      NonTSPseudoClass::MozPlaceholder |
    285                      // :-moz-is-html, :-moz-locale-dir and :-moz-window-inactive
    286                      // depend only on the state of the document, which is invariant across all
    287                      // elements involved in a given style cache.
    288                      NonTSPseudoClass::MozIsHTML |
    289                      NonTSPseudoClass::MozLocaleDir(_) |
    290                      NonTSPseudoClass::MozWindowInactive
    291            )
    292    }
    293 }
    294 
    295 impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
    296    type Impl = SelectorImpl;
    297 
    298    #[inline]
    299    fn is_active_or_hover(&self) -> bool {
    300        matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
    301    }
    302 
    303    /// We intentionally skip the link-related ones.
    304    #[inline]
    305    fn is_user_action_state(&self) -> bool {
    306        matches!(
    307            *self,
    308            NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus
    309        )
    310    }
    311 }
    312 
    313 /// The dummy struct we use to implement our selector parsing.
    314 #[derive(Clone, Debug, Eq, PartialEq)]
    315 pub struct SelectorImpl;
    316 
    317 /// A set of extra data to carry along with the matching context, either for
    318 /// selector-matching or invalidation.
    319 #[derive(Default)]
    320 pub struct ExtraMatchingData<'a> {
    321    /// The invalidation data to invalidate doc-state pseudo-classes correctly.
    322    pub invalidation_data: InvalidationMatchingData,
    323 
    324    /// The invalidation bits from matching container queries. These are here
    325    /// just for convenience mostly.
    326    pub cascade_input_flags: ComputedValueFlags,
    327 
    328    /// The style of the originating element in order to evaluate @container
    329    /// size queries affecting pseudo-elements.
    330    pub originating_element_style: Option<&'a ComputedValues>,
    331 }
    332 
    333 impl ::selectors::SelectorImpl for SelectorImpl {
    334    type ExtraMatchingData<'a> = ExtraMatchingData<'a>;
    335    type AttrValue = AtomString;
    336    type Identifier = AtomIdent;
    337    type LocalName = AtomIdent;
    338    type NamespacePrefix = AtomIdent;
    339    type NamespaceUrl = Namespace;
    340    type BorrowedNamespaceUrl = WeakNamespace;
    341    type BorrowedLocalName = WeakAtom;
    342 
    343    type PseudoElement = PseudoElement;
    344    type NonTSPseudoClass = NonTSPseudoClass;
    345 
    346    fn should_collect_attr_hash(name: &AtomIdent) -> bool {
    347        !crate::bloom::is_attr_name_excluded_from_filter(name)
    348    }
    349 }
    350 
    351 impl<'a> SelectorParser<'a> {
    352    fn is_pseudo_class_enabled(&self, pseudo_class: &NonTSPseudoClass) -> bool {
    353        if pseudo_class.is_enabled_in_content() {
    354            return true;
    355        }
    356 
    357        if self.in_user_agent_stylesheet()
    358            && pseudo_class.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS)
    359        {
    360            return true;
    361        }
    362 
    363        if self.chrome_rules_enabled()
    364            && pseudo_class.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_CHROME)
    365        {
    366            return true;
    367        }
    368 
    369        if matches!(*pseudo_class, NonTSPseudoClass::MozBroken) {
    370            return static_prefs::pref!("layout.css.moz-broken.content.enabled");
    371        }
    372 
    373        return false;
    374    }
    375 
    376    fn is_pseudo_element_enabled(&self, pseudo_element: &PseudoElement) -> bool {
    377        if pseudo_element.enabled_in_content() {
    378            return true;
    379        }
    380 
    381        if self.in_user_agent_stylesheet() && pseudo_element.enabled_in_ua_sheets() {
    382            return true;
    383        }
    384 
    385        if self.chrome_rules_enabled() && pseudo_element.enabled_in_chrome() {
    386            return true;
    387        }
    388 
    389        return false;
    390    }
    391 }
    392 
    393 /// Parse the functional pseudo-element with the function name.
    394 pub fn parse_functional_pseudo_element_with_name<'i, 't>(
    395    name: CowRcStr<'i>,
    396    parser: &mut Parser<'i, 't>,
    397    target: Target,
    398 ) -> Result<PseudoElement, ParseError<'i>> {
    399    use crate::gecko::pseudo_element::PtNameAndClassSelector;
    400 
    401    if matches!(target, Target::Selector) && starts_with_ignore_ascii_case(&name, "-moz-tree-") {
    402        // Tree pseudo-elements can have zero or more arguments, separated
    403        // by either comma or space.
    404        let mut args = ThinVec::new();
    405        loop {
    406            let location = parser.current_source_location();
    407            match parser.next() {
    408                Ok(&Token::Ident(ref ident)) => args.push(Atom::from(ident.as_ref())),
    409                Ok(&Token::Comma) => {},
    410                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
    411                Err(BasicParseError {
    412                    kind: BasicParseErrorKind::EndOfInput,
    413                    ..
    414                }) => break,
    415                _ => unreachable!("Parser::next() shouldn't return any other error"),
    416            }
    417        }
    418        return PseudoElement::tree_pseudo_element(&name, args).ok_or(parser.new_custom_error(
    419            SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
    420        ));
    421    }
    422 
    423    Ok(match_ignore_ascii_case! { &name,
    424        "highlight" => {
    425            PseudoElement::Highlight(AtomIdent::from(parser.expect_ident()?.as_ref()))
    426        },
    427        "view-transition-group" => {
    428            PseudoElement::ViewTransitionGroup(PtNameAndClassSelector::parse(parser, target)?)
    429        },
    430        "view-transition-image-pair" => {
    431            PseudoElement::ViewTransitionImagePair(PtNameAndClassSelector::parse(parser, target)?)
    432        },
    433        "view-transition-old" => {
    434            PseudoElement::ViewTransitionOld(PtNameAndClassSelector::parse(parser, target)?)
    435        },
    436        "view-transition-new" => {
    437            PseudoElement::ViewTransitionNew(PtNameAndClassSelector::parse(parser, target)?)
    438        },
    439        _ => return Err(parser.new_custom_error(
    440            SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)
    441        ))
    442    })
    443 }
    444 
    445 impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
    446    type Impl = SelectorImpl;
    447    type Error = StyleParseErrorKind<'i>;
    448 
    449    #[inline]
    450    fn parse_parent_selector(&self) -> bool {
    451        true
    452    }
    453 
    454    #[inline]
    455    fn parse_slotted(&self) -> bool {
    456        true
    457    }
    458 
    459    #[inline]
    460    fn parse_host(&self) -> bool {
    461        true
    462    }
    463 
    464    #[inline]
    465    fn parse_nth_child_of(&self) -> bool {
    466        true
    467    }
    468 
    469    #[inline]
    470    fn parse_is_and_where(&self) -> bool {
    471        true
    472    }
    473 
    474    #[inline]
    475    fn parse_has(&self) -> bool {
    476        true
    477    }
    478 
    479    #[inline]
    480    fn parse_part(&self) -> bool {
    481        true
    482    }
    483 
    484    #[inline]
    485    fn is_is_alias(&self, function: &str) -> bool {
    486        function.eq_ignore_ascii_case("-moz-any")
    487    }
    488 
    489    #[inline]
    490    fn allow_forgiving_selectors(&self) -> bool {
    491        !self.for_supports_rule
    492    }
    493 
    494    fn parse_non_ts_pseudo_class(
    495        &self,
    496        location: SourceLocation,
    497        name: CowRcStr<'i>,
    498    ) -> Result<NonTSPseudoClass, ParseError<'i>> {
    499        if let Some(pseudo_class) = NonTSPseudoClass::parse_non_functional(&name) {
    500            if self.is_pseudo_class_enabled(&pseudo_class) {
    501                return Ok(pseudo_class);
    502            }
    503        }
    504        Err(
    505            location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
    506                name,
    507            )),
    508        )
    509    }
    510 
    511    fn parse_non_ts_functional_pseudo_class<'t>(
    512        &self,
    513        name: CowRcStr<'i>,
    514        parser: &mut Parser<'i, 't>,
    515        _after_part: bool,
    516    ) -> Result<NonTSPseudoClass, ParseError<'i>> {
    517        let pseudo_class = match_ignore_ascii_case! { &name,
    518            "lang" => {
    519                let result = parser.parse_comma_separated(|input| {
    520                    Ok(AtomIdent::from(input.expect_ident_or_string()?.as_ref()))
    521                })?;
    522                if result.is_empty() {
    523                    return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    524                }
    525                NonTSPseudoClass::Lang(Lang(result.into()))
    526            },
    527            "state" => {
    528                let result = AtomIdent::from(parser.expect_ident()?.as_ref());
    529                NonTSPseudoClass::CustomState(CustomState(result))
    530            },
    531            "heading" => {
    532                let result = parser.parse_comma_separated(|input| Ok(input.expect_integer()?))?;
    533                if result.is_empty() {
    534                    return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    535                }
    536                NonTSPseudoClass::Heading(HeadingSelectorData(result.into()))
    537            },
    538            "active-view-transition-type" => {
    539                let result = parser.parse_comma_separated(|input| CustomIdent::parse(input, &[]))?;
    540                if result.is_empty() {
    541                    return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    542                }
    543                NonTSPseudoClass::ActiveViewTransitionType(result.into())
    544            },
    545            "-moz-locale-dir" => {
    546                NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?)
    547            },
    548            "dir" => {
    549                NonTSPseudoClass::Dir(Direction::parse(parser)?)
    550            },
    551            _ => return Err(parser.new_custom_error(
    552                SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())
    553            ))
    554        };
    555        if self.is_pseudo_class_enabled(&pseudo_class) {
    556            Ok(pseudo_class)
    557        } else {
    558            Err(
    559                parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
    560                    name,
    561                )),
    562            )
    563        }
    564    }
    565 
    566    fn parse_pseudo_element(
    567        &self,
    568        location: SourceLocation,
    569        name: CowRcStr<'i>,
    570    ) -> Result<PseudoElement, ParseError<'i>> {
    571        let allow_unkown_webkit = !self.for_supports_rule;
    572        if let Some(pseudo) = PseudoElement::from_slice(&name, allow_unkown_webkit) {
    573            if self.is_pseudo_element_enabled(&pseudo) {
    574                return Ok(pseudo);
    575            }
    576        }
    577 
    578        Err(
    579            location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
    580                name,
    581            )),
    582        )
    583    }
    584 
    585    fn parse_functional_pseudo_element<'t>(
    586        &self,
    587        name: CowRcStr<'i>,
    588        parser: &mut Parser<'i, 't>,
    589    ) -> Result<PseudoElement, ParseError<'i>> {
    590        let pseudo =
    591            parse_functional_pseudo_element_with_name(name.clone(), parser, Target::Selector)?;
    592        if self.is_pseudo_element_enabled(&pseudo) {
    593            return Ok(pseudo);
    594        }
    595 
    596        Err(
    597            parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
    598                name,
    599            )),
    600        )
    601    }
    602 
    603    fn default_namespace(&self) -> Option<Namespace> {
    604        self.namespaces.default.clone()
    605    }
    606 
    607    fn namespace_for_prefix(&self, prefix: &AtomIdent) -> Option<Namespace> {
    608        self.namespaces.prefixes.get(prefix).cloned()
    609    }
    610 }
    611 
    612 impl SelectorImpl {
    613    /// A helper to traverse each eagerly cascaded pseudo-element, executing
    614    /// `fun` on it.
    615    #[inline]
    616    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
    617    where
    618        F: FnMut(PseudoElement),
    619    {
    620        for pseudo in &EAGER_PSEUDOS {
    621            fun(pseudo.clone())
    622        }
    623    }
    624 }
    625 
    626 // Selector and component sizes are important for matching performance.
    627 size_of_test!(selectors::parser::Selector<SelectorImpl>, 8);
    628 size_of_test!(selectors::parser::Component<SelectorImpl>, 24);
    629 size_of_test!(PseudoElement, 16);
    630 size_of_test!(NonTSPseudoClass, 16);