Source code
Revision control
Copy as Markdown
Other Tools
/* 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/. */
use crate::derives::*;
type AtomThinVec = thin_vec::ThinVec<Atom>;
/// Gecko's pseudo-element definition.
///
/// We intentionally double-box legacy ::-moz-tree pseudo-elements to keep the
/// size of PseudoElement (and thus selector components) small.
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
#[repr(u8)]
pub enum PseudoElement {
% for pseudo in PSEUDOS:
/// ${pseudo.name}
% if pseudo.argument:
${pseudo.capitalized}(${pseudo.argument}),
% else:
${pseudo.capitalized},
% endif
% endfor
/// ::-webkit-* that we don't recognize
UnknownWebkit(Atom),
}
<% EAGER_PSEUDOS = list(filter(lambda p: getattr(p, 'is_eager', False), PSEUDOS)) %>
/// The number of eager pseudo-elements.
pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)};
/// The number of all pseudo-elements.
pub const PSEUDO_COUNT: usize = ${len(PSEUDOS)};
/// The list of eager pseudos.
pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
% for eager_pseudo in EAGER_PSEUDOS:
PseudoElement::${eager_pseudo.capitalized},
% endfor
];
<%def name="pseudo_element_variant(pseudo, arg='..')">\
PseudoElement::${pseudo.capitalized}${"({})".format(arg) if pseudo.argument else ""}\
</%def>
impl PseudoElement {
/// Whether this pseudo-element is tree pseudo-element.
#[inline]
pub fn is_tree_pseudo_element(&self) -> bool {
match *self {
% for pseudo in PSEUDOS:
% if pseudo.name.startswith("-moz-tree-"):
${pseudo_element_variant(pseudo)} => true,
% endif
% endfor
_ => false,
}
}
#[inline]
fn flags_for_index(i: usize) -> PseudoStyleTypeFlags {
static FLAGS: [PseudoStyleTypeFlags; PSEUDO_COUNT + 1] = [
% for pseudo in PSEUDOS:
PseudoStyleTypeFlags::from_bits_truncate(${' | '.join(f"PseudoStyleTypeFlags::{flag}.bits()" for flag in pseudo.flags())}),
% endfor
PseudoStyleTypeFlags::NONE, // :-webkit-*
];
FLAGS[i]
}
/// Gets the flags associated to this pseudo-element or anon box.
#[inline]
pub fn flags(&self) -> PseudoStyleTypeFlags {
Self::flags_for_index(self.index())
}
/// If this pseudo is pref-gated, returns the pref value, otherwise false.
pub fn type_enabled_in_content(ty: PseudoStyleType) -> bool {
let flags = Self::flags_for_index(ty as usize);
// Common path, not enabled explicitly in UA sheets (note that chrome implies UA), and not
// pref-gated.
if !flags.intersects(PseudoStyleTypeFlags::ENABLED_IN_UA | PseudoStyleTypeFlags::ENABLED_BY_PREF) {
return true;
}
if !flags.intersects(PseudoStyleTypeFlags::ENABLED_BY_PREF) {
return false;
}
match ty {
% for pseudo in PSEUDOS:
% if pseudo.pref:
PseudoStyleType::${pseudo.capitalized} => pref!("${pseudo.pref}"),
% endif
% endfor
_ => false,
}
}
/// Construct a pseudo-element from a `PseudoStyleType`.
#[inline]
pub fn from_pseudo_type(type_: PseudoStyleType, functional_pseudo_parameter: Option<AtomIdent>) -> Option<Self> {
match type_ {
% for pseudo in PSEUDOS:
% if not pseudo.argument:
PseudoStyleType::${pseudo.capitalized} => {
debug_assert!(functional_pseudo_parameter.is_none());
Some(${pseudo_element_variant(pseudo)})
},
% elif pseudo.argument == "AtomThinVec":
PseudoStyleType::${pseudo.capitalized} => {
debug_assert!(functional_pseudo_parameter.is_none());
Some(${pseudo_element_variant(pseudo, "Default::default()")})
},
% elif pseudo.argument == "PtNameAndClassSelector":
PseudoStyleType::${pseudo.capitalized} => functional_pseudo_parameter.map(|p| {
PseudoElement::${pseudo.capitalized}(PtNameAndClassSelector::from_name(p.0))
}),
% else:
<% assert pseudo.argument == "AtomIdent", f"Unhandled argument type {pseudo.argument}" %>
PseudoStyleType::${pseudo.capitalized} => {
functional_pseudo_parameter.map(PseudoElement::${pseudo.capitalized})
},
% endif
% endfor
_ => None,
}
}
/// Construct a `PseudoStyleType`.
#[inline]
pub fn pseudo_type(&self) -> PseudoStyleType {
// SAFETY: PseudoStyleType has the same variants as PseudoElement
unsafe { std::mem::transmute::<u8, PseudoStyleType>(self.discriminant()) }
}
/// Returns the relevant PseudoStyleType, and an atom as an argument, if any.
/// FIXME: we probably have to return the arguments of -moz-tree. However, they are multiple
/// names, so we skip them for now (until we really need them).
#[inline]
pub fn pseudo_type_and_argument(&self) -> (PseudoStyleType, Option<&Atom>) {
let ty = self.pseudo_type();
let arg = match *self {
% for pseudo in PSEUDOS:
% if pseudo.argument == "PtNameAndClassSelector":
PseudoElement::${pseudo.capitalized}(ref val) => Some(val.name()),
% elif pseudo.argument == "AtomIdent":
PseudoElement::${pseudo.capitalized}(ref val) => Some(&val.0),
% endif
% endfor
_ => None,
};
(ty, arg)
}
/// Get the argument list of a tree pseudo-element.
#[inline]
pub fn tree_pseudo_args(&self) -> &[Atom] {
match *self {
% for pseudo in PSEUDOS:
% if pseudo.name.startswith("-moz-tree-"):
PseudoElement::${pseudo.capitalized}(ref args) => &args,
% endif
% endfor
_ => &[],
}
}
/// Constructs a pseudo-element from a string of text.
///
/// Returns `None` if the pseudo-element is not recognised.
#[inline]
pub fn from_slice(name: &str, allow_unkown_webkit: bool) -> Option<Self> {
// We don't need to support tree pseudos because functional
// pseudo-elements needs arguments, and thus should be created
// via other methods.
cssparser::ascii_case_insensitive_phf_map! {
pseudo -> PseudoElement = {
% for pseudo in PSEUDOS:
% if not pseudo.argument:
"${pseudo.name}" => ${pseudo_element_variant(pseudo)},
% endif
% endfor
// Alias some legacy prefixed pseudos to their standardized name at parse time:
"-moz-selection" => PseudoElement::Selection,
"-moz-placeholder" => PseudoElement::Placeholder,
"-moz-list-bullet" => PseudoElement::Marker,
"-moz-list-number" => PseudoElement::Marker,
}
}
if let Some(p) = pseudo::get(name) {
return Some(p.clone());
}
if starts_with_ignore_ascii_case(name, "-moz-tree-") {
return PseudoElement::tree_pseudo_element(name, Default::default())
}
const WEBKIT_PREFIX: &str = "-webkit-";
if allow_unkown_webkit && starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) {
let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]);
return Some(PseudoElement::UnknownWebkit(part.into()));
}
None
}
/// Constructs a tree pseudo-element from the given name and arguments.
/// "name" must start with "-moz-tree-".
///
/// Returns `None` if the pseudo-element is not recognized.
#[inline]
pub fn tree_pseudo_element(name: &str, args: thin_vec::ThinVec<Atom>) -> Option<Self> {
debug_assert!(starts_with_ignore_ascii_case(name, "-moz-tree-"));
let tree_part = &name[10..];
% for pseudo in PSEUDOS:
% if pseudo.name.startswith("-moz-tree-"):
if tree_part.eq_ignore_ascii_case("${pseudo.name[10:]}") {
return Some(${pseudo_element_variant(pseudo, "args")});
}
% endif
% endfor
None
}
/// Returns true if this pseudo-element matches the given selector.
pub fn matches(
&self,
pseudo_selector: &PseudoElement,
element: &super::wrapper::GeckoElement,
) -> bool {
if *self == *pseudo_selector {
return true;
}
if std::mem::discriminant(self) != std::mem::discriminant(pseudo_selector) {
return false;
}
// Check named view transition pseudo-elements.
self.matches_named_view_transition_pseudo_element(pseudo_selector, element)
}
}
impl ToCss for PseudoElement {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
% for pseudo in PSEUDOS:
% if pseudo.argument == "AtomThinVec":
${pseudo_element_variant(pseudo, "ref args")} => {
dest.write_str("::${pseudo.name}")?;
let mut iter = args.iter();
if let Some(first) = iter.next() {
dest.write_char('(')?;
serialize_atom_identifier(&first, dest)?;
for item in iter {
dest.write_str(", ")?;
serialize_atom_identifier(item, dest)?;
}
dest.write_char(')')?;
}
Ok(())
},
% elif pseudo.argument:
PseudoElement::${pseudo.capitalized}(ref arg) => {
dest.write_str("::${pseudo.name}(")?;
arg.to_css(dest)?;
dest.write_char(')')
}
% else:
${pseudo_element_variant(pseudo)} => dest.write_str("::${pseudo.name}"),
% endif
% endfor
PseudoElement::UnknownWebkit(ref atom) => {
dest.write_str("::-webkit-")?;
serialize_atom_identifier(atom, dest)
},
}
}
}