tor-browser

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

attr.rs (6686B)


      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 use crate::parser::SelectorImpl;
      6 use cssparser::ToCss;
      7 use std::fmt;
      8 
      9 #[cfg(feature = "to_shmem")]
     10 use to_shmem_derive::ToShmem;
     11 
     12 #[derive(Clone, Eq, PartialEq)]
     13 #[cfg_attr(feature = "to_shmem", derive(ToShmem))]
     14 #[cfg_attr(feature = "to_shmem", shmem(no_bounds))]
     15 pub struct AttrSelectorWithOptionalNamespace<Impl: SelectorImpl> {
     16    #[cfg_attr(feature = "to_shmem", shmem(field_bound))]
     17    pub namespace: Option<NamespaceConstraint<(Impl::NamespacePrefix, Impl::NamespaceUrl)>>,
     18    #[cfg_attr(feature = "to_shmem", shmem(field_bound))]
     19    pub local_name: Impl::LocalName,
     20    pub local_name_lower: Impl::LocalName,
     21    #[cfg_attr(feature = "to_shmem", shmem(field_bound))]
     22    pub operation: ParsedAttrSelectorOperation<Impl::AttrValue>,
     23 }
     24 
     25 impl<Impl: SelectorImpl> AttrSelectorWithOptionalNamespace<Impl> {
     26    pub fn namespace(&self) -> Option<NamespaceConstraint<&Impl::NamespaceUrl>> {
     27        self.namespace.as_ref().map(|ns| match ns {
     28            NamespaceConstraint::Any => NamespaceConstraint::Any,
     29            NamespaceConstraint::Specific((_, ref url)) => NamespaceConstraint::Specific(url),
     30        })
     31    }
     32 }
     33 
     34 #[derive(Clone, Eq, PartialEq)]
     35 #[cfg_attr(feature = "to_shmem", derive(ToShmem))]
     36 pub enum NamespaceConstraint<NamespaceUrl> {
     37    Any,
     38 
     39    /// Empty string for no namespace
     40    Specific(NamespaceUrl),
     41 }
     42 
     43 #[derive(Clone, Eq, PartialEq)]
     44 #[cfg_attr(feature = "to_shmem", derive(ToShmem))]
     45 pub enum ParsedAttrSelectorOperation<AttrValue> {
     46    Exists,
     47    WithValue {
     48        operator: AttrSelectorOperator,
     49        case_sensitivity: ParsedCaseSensitivity,
     50        value: AttrValue,
     51    },
     52 }
     53 
     54 #[derive(Clone, Eq, PartialEq)]
     55 pub enum AttrSelectorOperation<AttrValue> {
     56    Exists,
     57    WithValue {
     58        operator: AttrSelectorOperator,
     59        case_sensitivity: CaseSensitivity,
     60        value: AttrValue,
     61    },
     62 }
     63 
     64 impl<AttrValue> AttrSelectorOperation<AttrValue> {
     65    pub fn eval_str(&self, element_attr_value: &str) -> bool
     66    where
     67        AttrValue: AsRef<str>,
     68    {
     69        match *self {
     70            AttrSelectorOperation::Exists => true,
     71            AttrSelectorOperation::WithValue {
     72                operator,
     73                case_sensitivity,
     74                ref value,
     75            } => operator.eval_str(element_attr_value, value.as_ref(), case_sensitivity),
     76        }
     77    }
     78 }
     79 
     80 #[derive(Clone, Copy, Eq, PartialEq)]
     81 #[cfg_attr(feature = "to_shmem", derive(ToShmem))]
     82 pub enum AttrSelectorOperator {
     83    Equal,
     84    Includes,
     85    DashMatch,
     86    Prefix,
     87    Substring,
     88    Suffix,
     89 }
     90 
     91 impl ToCss for AttrSelectorOperator {
     92    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
     93    where
     94        W: fmt::Write,
     95    {
     96        // https://drafts.csswg.org/cssom/#serializing-selectors
     97        // See "attribute selector".
     98        dest.write_str(match *self {
     99            AttrSelectorOperator::Equal => "=",
    100            AttrSelectorOperator::Includes => "~=",
    101            AttrSelectorOperator::DashMatch => "|=",
    102            AttrSelectorOperator::Prefix => "^=",
    103            AttrSelectorOperator::Substring => "*=",
    104            AttrSelectorOperator::Suffix => "$=",
    105        })
    106    }
    107 }
    108 
    109 impl AttrSelectorOperator {
    110    pub fn eval_str(
    111        self,
    112        element_attr_value: &str,
    113        attr_selector_value: &str,
    114        case_sensitivity: CaseSensitivity,
    115    ) -> bool {
    116        let e = element_attr_value.as_bytes();
    117        let s = attr_selector_value.as_bytes();
    118        let case = case_sensitivity;
    119        match self {
    120            AttrSelectorOperator::Equal => case.eq(e, s),
    121            AttrSelectorOperator::Prefix => {
    122                !s.is_empty() && e.len() >= s.len() && case.eq(&e[..s.len()], s)
    123            },
    124            AttrSelectorOperator::Suffix => {
    125                !s.is_empty() && e.len() >= s.len() && case.eq(&e[(e.len() - s.len())..], s)
    126            },
    127            AttrSelectorOperator::Substring => {
    128                !s.is_empty() && case.contains(element_attr_value, attr_selector_value)
    129            },
    130            AttrSelectorOperator::Includes => {
    131                !s.is_empty()
    132                    && element_attr_value
    133                        .split(SELECTOR_WHITESPACE)
    134                        .any(|part| case.eq(part.as_bytes(), s))
    135            },
    136            AttrSelectorOperator::DashMatch => {
    137                case.eq(e, s) || (e.get(s.len()) == Some(&b'-') && case.eq(&e[..s.len()], s))
    138            },
    139        }
    140    }
    141 }
    142 
    143 /// The definition of whitespace per CSS Selectors Level 3 ยง 4.
    144 pub static SELECTOR_WHITESPACE: &[char] = &[' ', '\t', '\n', '\r', '\x0C'];
    145 
    146 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    147 #[cfg_attr(feature = "to_shmem", derive(ToShmem))]
    148 pub enum ParsedCaseSensitivity {
    149    /// 's' was specified.
    150    ExplicitCaseSensitive,
    151    /// 'i' was specified.
    152    AsciiCaseInsensitive,
    153    /// No flags were specified and HTML says this is a case-sensitive attribute.
    154    CaseSensitive,
    155    /// No flags were specified and HTML says this is a case-insensitive attribute.
    156    AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
    157 }
    158 
    159 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    160 pub enum CaseSensitivity {
    161    CaseSensitive,
    162    AsciiCaseInsensitive,
    163 }
    164 
    165 impl CaseSensitivity {
    166    pub fn eq(self, a: &[u8], b: &[u8]) -> bool {
    167        match self {
    168            CaseSensitivity::CaseSensitive => a == b,
    169            CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b),
    170        }
    171    }
    172 
    173    pub fn contains(self, haystack: &str, needle: &str) -> bool {
    174        match self {
    175            CaseSensitivity::CaseSensitive => haystack.contains(needle),
    176            CaseSensitivity::AsciiCaseInsensitive => {
    177                if let Some((&n_first_byte, n_rest)) = needle.as_bytes().split_first() {
    178                    haystack.bytes().enumerate().any(|(i, byte)| {
    179                        if !byte.eq_ignore_ascii_case(&n_first_byte) {
    180                            return false;
    181                        }
    182                        let after_this_byte = &haystack.as_bytes()[i + 1..];
    183                        match after_this_byte.get(..n_rest.len()) {
    184                            None => false,
    185                            Some(haystack_slice) => haystack_slice.eq_ignore_ascii_case(n_rest),
    186                        }
    187                    })
    188                } else {
    189                    // any_str.contains("") == true,
    190                    // though these cases should be handled with *NeverMatches and never go here.
    191                    true
    192                }
    193            },
    194        }
    195    }
    196 }