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 }