tor-browser

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

pseudo_element_definition.mako.rs (12540B)


      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::derives::*;
      6 
      7 /// Gecko's pseudo-element definition.
      8 ///
      9 /// We intentionally double-box legacy ::-moz-tree pseudo-elements to keep the
     10 /// size of PseudoElement (and thus selector components) small.
     11 #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
     12 pub enum PseudoElement {
     13    % for pseudo in PSEUDOS:
     14        /// ${pseudo.value}
     15        % if pseudo.is_tree_pseudo_element():
     16        ${pseudo.capitalized_pseudo()}(thin_vec::ThinVec<Atom>),
     17        % elif pseudo.pseudo_ident == "highlight":
     18        ${pseudo.capitalized_pseudo()}(AtomIdent),
     19        % elif pseudo.is_named_view_transition_pseudo():
     20        ${pseudo.capitalized_pseudo()}(PtNameAndClassSelector),
     21        % else:
     22        ${pseudo.capitalized_pseudo()},
     23        % endif
     24    % endfor
     25    /// ::-webkit-* that we don't recognize
     26    /// https://github.com/whatwg/compat/issues/103
     27    UnknownWebkit(Atom),
     28 }
     29 
     30 /// Important: If you change this, you should also update Gecko's
     31 /// nsCSSPseudoElements::IsEagerlyCascadedInServo.
     32 <% EAGER_PSEUDOS = ["Before", "After", "FirstLine", "FirstLetter"] %>
     33 <% TREE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_tree_pseudo_element()] %>
     34 <% NAMED_VT_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_named_view_transition_pseudo()] %>
     35 <% SIMPLE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_simple_pseudo_element()] %>
     36 
     37 /// The number of eager pseudo-elements.
     38 pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)};
     39 
     40 /// The number of non-functional pseudo-elements.
     41 pub const SIMPLE_PSEUDO_COUNT: usize = ${len(SIMPLE_PSEUDOS)};
     42 
     43 /// The number of tree pseudo-elements.
     44 pub const TREE_PSEUDO_COUNT: usize = ${len(TREE_PSEUDOS)};
     45 
     46 /// The number of all pseudo-elements.
     47 pub const PSEUDO_COUNT: usize = ${len(PSEUDOS)};
     48 
     49 /// The list of eager pseudos.
     50 pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
     51    % for eager_pseudo_name in EAGER_PSEUDOS:
     52    PseudoElement::${eager_pseudo_name},
     53    % endfor
     54 ];
     55 
     56 <%def name="pseudo_element_variant(pseudo, tree_arg='..')">\
     57 PseudoElement::${pseudo.capitalized_pseudo()}${"({})".format(tree_arg) if not pseudo.is_simple_pseudo_element() else ""}\
     58 </%def>
     59 
     60 impl PseudoElement {
     61    /// Returns an index of the pseudo-element.
     62    #[inline]
     63    pub fn index(&self) -> usize {
     64        match *self {
     65            % for i, pseudo in enumerate(PSEUDOS):
     66            ${pseudo_element_variant(pseudo)} => ${i},
     67            % endfor
     68            PseudoElement::UnknownWebkit(..) => unreachable!(),
     69        }
     70    }
     71 
     72    /// Returns an array of `None` values.
     73    ///
     74    /// FIXME(emilio): Integer generics can't come soon enough.
     75    pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
     76        [
     77            ${",\n            ".join(["None" for pseudo in PSEUDOS])}
     78        ]
     79    }
     80 
     81    /// Whether this pseudo-element is an anonymous box.
     82    #[inline]
     83    pub fn is_anon_box(&self) -> bool {
     84        match *self {
     85            % for pseudo in PSEUDOS:
     86                % if pseudo.is_anon_box():
     87                    ${pseudo_element_variant(pseudo)} => true,
     88                % endif
     89            % endfor
     90            _ => false,
     91        }
     92    }
     93 
     94    /// Whether this pseudo-element is eagerly-cascaded.
     95    #[inline]
     96    pub fn is_eager(&self) -> bool {
     97        matches!(*self,
     98                 ${" | ".join(map(lambda name: "PseudoElement::{}".format(name), EAGER_PSEUDOS))})
     99    }
    100 
    101    /// Whether this pseudo-element is tree pseudo-element.
    102    #[inline]
    103    pub fn is_tree_pseudo_element(&self) -> bool {
    104        match *self {
    105            % for pseudo in TREE_PSEUDOS:
    106            ${pseudo_element_variant(pseudo)} => true,
    107            % endfor
    108            _ => false,
    109        }
    110    }
    111 
    112    /// Whether this pseudo-element is a named view transition pseudo-element.
    113    #[inline]
    114    pub fn is_named_view_transition_pseudo_element(&self) -> bool {
    115        match *self {
    116            % for pseudo in NAMED_VT_PSEUDOS:
    117            ${pseudo_element_variant(pseudo)} => true,
    118            % endfor
    119            _ => false,
    120        }
    121    }
    122 
    123    /// Whether this pseudo-element is an unknown Webkit-prefixed pseudo-element.
    124    #[inline]
    125    pub fn is_unknown_webkit_pseudo_element(&self) -> bool {
    126        matches!(*self, PseudoElement::UnknownWebkit(..))
    127    }
    128 
    129    /// Gets the flags associated to this pseudo-element, or 0 if it's an
    130    /// anonymous box.
    131    pub fn flags(&self) -> u32 {
    132        match *self {
    133            % for pseudo in PSEUDOS:
    134                ${pseudo_element_variant(pseudo)} =>
    135                % if pseudo.is_tree_pseudo_element():
    136                    structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME,
    137                % elif pseudo.is_anon_box():
    138                    structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS,
    139                % else:
    140                    structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.pseudo_ident},
    141                % endif
    142            % endfor
    143            PseudoElement::UnknownWebkit(..) => 0,
    144        }
    145    }
    146 
    147    /// Construct a pseudo-element from a `PseudoStyleType`.
    148    #[inline]
    149    pub fn from_pseudo_type(type_: PseudoStyleType, functional_pseudo_parameter: Option<AtomIdent>) -> Option<Self> {
    150        match type_ {
    151            % for pseudo in PSEUDOS:
    152            % if pseudo.is_simple_pseudo_element():
    153                PseudoStyleType::${pseudo.pseudo_ident} => {
    154                    debug_assert!(functional_pseudo_parameter.is_none());
    155                    Some(${pseudo_element_variant(pseudo)})
    156                },
    157            % elif pseudo.is_named_view_transition_pseudo():
    158                PseudoStyleType::${pseudo.pseudo_ident} => functional_pseudo_parameter.map(|p| {
    159                    PseudoElement::${pseudo.capitalized_pseudo()}(PtNameAndClassSelector::from_name(p.0))
    160                }),
    161            % endif
    162            % endfor
    163            PseudoStyleType::highlight => functional_pseudo_parameter.map(PseudoElement::Highlight),
    164            _ => None,
    165        }
    166    }
    167 
    168    /// Construct a `PseudoStyleType` from a pseudo-element
    169    // FIXME: we probably have to return the arguments of -moz-tree. However, they are multiple
    170    // names, so we skip them for now (until we really need them).
    171    #[inline]
    172    pub fn pseudo_type_and_argument(&self) -> (PseudoStyleType, Option<&Atom>) {
    173        match *self {
    174            % for pseudo in PSEUDOS:
    175            % if pseudo.is_tree_pseudo_element():
    176                PseudoElement::${pseudo.capitalized_pseudo()}(..) => (PseudoStyleType::XULTree, None),
    177            % elif pseudo.pseudo_ident == "highlight":
    178                PseudoElement::${pseudo.capitalized_pseudo()}(ref value) => (PseudoStyleType::${pseudo.pseudo_ident}, Some(&value.0)),
    179            % elif pseudo.is_named_view_transition_pseudo():
    180                PseudoElement::${pseudo.capitalized_pseudo()}(ref value) => (PseudoStyleType::${pseudo.pseudo_ident}, Some(value.name())),
    181            % else:
    182                PseudoElement::${pseudo.capitalized_pseudo()} => (PseudoStyleType::${pseudo.pseudo_ident}, None),
    183            % endif
    184            % endfor
    185            PseudoElement::UnknownWebkit(..) => unreachable!(),
    186        }
    187    }
    188 
    189    /// Get the argument list of a tree pseudo-element.
    190    #[inline]
    191    pub fn tree_pseudo_args(&self) -> Option<&[Atom]> {
    192        match *self {
    193            % for pseudo in TREE_PSEUDOS:
    194            PseudoElement::${pseudo.capitalized_pseudo()}(ref args) => Some(args),
    195            % endfor
    196            _ => None,
    197        }
    198    }
    199 
    200    /// Construct a tree pseudo-element from atom and args.
    201    #[inline]
    202    pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option<Self> {
    203        % for pseudo in PSEUDOS:
    204        % if pseudo.is_tree_pseudo_element():
    205            if atom == &atom!("${pseudo.value}") {
    206                return Some(PseudoElement::${pseudo.capitalized_pseudo()}(args.into()));
    207            }
    208        % endif
    209        % endfor
    210        None
    211    }
    212 
    213    /// Constructs a pseudo-element from a string of text.
    214    ///
    215    /// Returns `None` if the pseudo-element is not recognised.
    216    #[inline]
    217    pub fn from_slice(name: &str, allow_unkown_webkit: bool) -> Option<Self> {
    218        // We don't need to support tree pseudos because functional
    219        // pseudo-elements needs arguments, and thus should be created
    220        // via other methods.
    221        cssparser::ascii_case_insensitive_phf_map! {
    222            pseudo -> PseudoElement = {
    223                % for pseudo in SIMPLE_PSEUDOS:
    224                "${pseudo.value[1:]}" => ${pseudo_element_variant(pseudo)},
    225                % endfor
    226                // Alias some legacy prefixed pseudos to their standardized name at parse time:
    227                "-moz-selection" => PseudoElement::Selection,
    228                "-moz-placeholder" => PseudoElement::Placeholder,
    229                "-moz-list-bullet" => PseudoElement::Marker,
    230                "-moz-list-number" => PseudoElement::Marker,
    231            }
    232        }
    233        if let Some(p) = pseudo::get(name) {
    234            return Some(p.clone());
    235        }
    236        if starts_with_ignore_ascii_case(name, "-moz-tree-") {
    237            return PseudoElement::tree_pseudo_element(name, Default::default())
    238        }
    239        const WEBKIT_PREFIX: &str = "-webkit-";
    240        if allow_unkown_webkit && starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) {
    241            let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]);
    242            return Some(PseudoElement::UnknownWebkit(part.into()));
    243        }
    244        None
    245    }
    246 
    247    /// Constructs a tree pseudo-element from the given name and arguments.
    248    /// "name" must start with "-moz-tree-".
    249    ///
    250    /// Returns `None` if the pseudo-element is not recognized.
    251    #[inline]
    252    pub fn tree_pseudo_element(name: &str, args: thin_vec::ThinVec<Atom>) -> Option<Self> {
    253        debug_assert!(starts_with_ignore_ascii_case(name, "-moz-tree-"));
    254        let tree_part = &name[10..];
    255        % for pseudo in TREE_PSEUDOS:
    256            if tree_part.eq_ignore_ascii_case("${pseudo.value[11:]}") {
    257                return Some(${pseudo_element_variant(pseudo, "args")});
    258            }
    259        % endfor
    260        None
    261    }
    262 
    263    /// Returns true if this pseudo-element matches the given selector.
    264    pub fn matches(
    265        &self,
    266        pseudo_selector: &PseudoElement,
    267        element: &super::wrapper::GeckoElement,
    268    ) -> bool {
    269        if *self == *pseudo_selector {
    270            return true;
    271        }
    272 
    273        if std::mem::discriminant(self) != std::mem::discriminant(pseudo_selector) {
    274            return false;
    275        }
    276 
    277        // Check named view transition pseudo-elements.
    278        self.matches_named_view_transition_pseudo_element(pseudo_selector, element)
    279    }
    280 }
    281 
    282 impl ToCss for PseudoElement {
    283    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
    284        dest.write_char(':')?;
    285        match *self {
    286            % for pseudo in (p for p in PSEUDOS if p.pseudo_ident != "highlight"):
    287            %if pseudo.is_named_view_transition_pseudo():
    288                PseudoElement::${pseudo.capitalized_pseudo()}(ref name_and_class) => {
    289                    dest.write_str("${pseudo.value}(")?;
    290                    name_and_class.to_css(dest)?;
    291                    dest.write_char(')')?;
    292                }
    293            %else:
    294                ${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?,
    295            %endif
    296            % endfor
    297            PseudoElement::Highlight(ref name) => {
    298                dest.write_str(":highlight(")?;
    299                serialize_atom_identifier(name, dest)?;
    300                dest.write_char(')')?;
    301            }
    302            PseudoElement::UnknownWebkit(ref atom) => {
    303                dest.write_str(":-webkit-")?;
    304                serialize_atom_identifier(atom, dest)?;
    305            }
    306        }
    307        if let Some(args) = self.tree_pseudo_args() {
    308            if !args.is_empty() {
    309                dest.write_char('(')?;
    310                let mut iter = args.iter();
    311                if let Some(first) = iter.next() {
    312                    serialize_atom_identifier(&first, dest)?;
    313                    for item in iter {
    314                        dest.write_str(", ")?;
    315                        serialize_atom_identifier(item, dest)?;
    316                    }
    317                }
    318                dest.write_char(')')?;
    319            }
    320        }
    321        Ok(())
    322    }
    323 }