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's definition of a pseudo-element. 6 //! 7 //! Note that a few autogenerated bits of this live in 8 //! `pseudo_element_definition.mako.rs`. If you touch that file, you probably 9 //! need to update the checked-in files for Servo. 10 11 use crate::gecko_bindings::structs::{self, PseudoStyleType}; 12 use crate::properties::longhands::display::computed_value::T as Display; 13 use crate::properties::{ComputedValues, PropertyFlags}; 14 use crate::selector_parser::{PseudoElementCascadeType, SelectorImpl}; 15 use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase}; 16 use crate::string_cache::Atom; 17 use crate::values::serialize_atom_identifier; 18 use cssparser::ToCss; 19 use std::fmt; 20 use thin_slice::ThinBoxedSlice; 21 22 include!(concat!( 23 env!("OUT_DIR"), 24 "/gecko/pseudo_element_definition.rs" 25 )); 26 27 impl ::selectors::parser::PseudoElement for PseudoElement { 28 type Impl = SelectorImpl; 29 30 // ::slotted() should support all tree-abiding pseudo-elements, see 31 // https://drafts.csswg.org/css-scoping/#slotted-pseudo 32 // https://drafts.csswg.org/css-pseudo-4/#treelike 33 #[inline] valid_after_slotted(&self) -> bool34 fn valid_after_slotted(&self) -> bool { 35 matches!( 36 *self, 37 PseudoElement::Before | 38 PseudoElement::After | 39 PseudoElement::Marker | 40 PseudoElement::Placeholder | 41 PseudoElement::FileSelectorButton 42 ) 43 } 44 45 #[inline] accepts_state_pseudo_classes(&self) -> bool46 fn accepts_state_pseudo_classes(&self) -> bool { 47 self.supports_user_action_state() 48 } 49 } 50 51 impl PseudoElement { 52 /// Returns the kind of cascade type that a given pseudo is going to use. 53 /// 54 /// In Gecko we only compute ::before and ::after eagerly. We save the rules 55 /// for anonymous boxes separately, so we resolve them as precomputed 56 /// pseudos. 57 /// 58 /// We resolve the others lazily, see `Servo_ResolvePseudoStyle`. cascade_type(&self) -> PseudoElementCascadeType59 pub fn cascade_type(&self) -> PseudoElementCascadeType { 60 if self.is_eager() { 61 debug_assert!(!self.is_anon_box()); 62 return PseudoElementCascadeType::Eager; 63 } 64 65 if self.is_precomputed() { 66 return PseudoElementCascadeType::Precomputed; 67 } 68 69 PseudoElementCascadeType::Lazy 70 } 71 72 /// Whether the pseudo-element should inherit from the default computed 73 /// values instead of from the parent element. 74 /// 75 /// This is not the common thing, but there are some pseudos (namely: 76 /// ::backdrop), that shouldn't inherit from the parent element. inherits_from_default_values(&self) -> bool77 pub fn inherits_from_default_values(&self) -> bool { 78 matches!(*self, PseudoElement::Backdrop) 79 } 80 81 /// Gets the canonical index of this eagerly-cascaded pseudo-element. 82 #[inline] eager_index(&self) -> usize83 pub fn eager_index(&self) -> usize { 84 EAGER_PSEUDOS 85 .iter() 86 .position(|p| p == self) 87 .expect("Not an eager pseudo") 88 } 89 90 /// Creates a pseudo-element from an eager index. 91 #[inline] from_eager_index(i: usize) -> Self92 pub fn from_eager_index(i: usize) -> Self { 93 EAGER_PSEUDOS[i].clone() 94 } 95 96 /// Whether the current pseudo element is animatable. 97 #[inline] is_animatable(&self) -> bool98 pub fn is_animatable(&self) -> bool { 99 matches!(*self, Self::Before | Self::After | Self::Marker) 100 } 101 102 /// Whether the current pseudo element is ::before or ::after. 103 #[inline] is_before_or_after(&self) -> bool104 pub fn is_before_or_after(&self) -> bool { 105 self.is_before() || self.is_after() 106 } 107 108 /// Whether this pseudo-element is the ::before pseudo. 109 #[inline] is_before(&self) -> bool110 pub fn is_before(&self) -> bool { 111 *self == PseudoElement::Before 112 } 113 114 /// Whether this pseudo-element is the ::after pseudo. 115 #[inline] is_after(&self) -> bool116 pub fn is_after(&self) -> bool { 117 *self == PseudoElement::After 118 } 119 120 /// Whether this pseudo-element is the ::marker pseudo. 121 #[inline] is_marker(&self) -> bool122 pub fn is_marker(&self) -> bool { 123 *self == PseudoElement::Marker 124 } 125 126 /// Whether this pseudo-element is the ::selection pseudo. 127 #[inline] is_selection(&self) -> bool128 pub fn is_selection(&self) -> bool { 129 *self == PseudoElement::Selection 130 } 131 132 /// Whether this pseudo-element is ::first-letter. 133 #[inline] is_first_letter(&self) -> bool134 pub fn is_first_letter(&self) -> bool { 135 *self == PseudoElement::FirstLetter 136 } 137 138 /// Whether this pseudo-element is ::first-line. 139 #[inline] is_first_line(&self) -> bool140 pub fn is_first_line(&self) -> bool { 141 *self == PseudoElement::FirstLine 142 } 143 144 /// Whether this pseudo-element is the ::-moz-color-swatch pseudo. 145 #[inline] is_color_swatch(&self) -> bool146 pub fn is_color_swatch(&self) -> bool { 147 *self == PseudoElement::MozColorSwatch 148 } 149 150 /// Whether this pseudo-element is lazily-cascaded. 151 #[inline] is_lazy(&self) -> bool152 pub fn is_lazy(&self) -> bool { 153 !self.is_eager() && !self.is_precomputed() 154 } 155 156 /// Whether this pseudo-element supports user action selectors. supports_user_action_state(&self) -> bool157 pub fn supports_user_action_state(&self) -> bool { 158 (self.flags() & structs::CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) != 0 159 } 160 161 /// Whether this pseudo-element is enabled for all content. enabled_in_content(&self) -> bool162 pub fn enabled_in_content(&self) -> bool { 163 self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME == 0 164 } 165 166 /// Whether this pseudo is enabled explicitly in UA sheets. enabled_in_ua_sheets(&self) -> bool167 pub fn enabled_in_ua_sheets(&self) -> bool { 168 (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS) != 0 169 } 170 171 /// Whether this pseudo is enabled explicitly in chrome sheets. enabled_in_chrome(&self) -> bool172 pub fn enabled_in_chrome(&self) -> bool { 173 (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_CHROME) != 0 174 } 175 176 /// Whether this pseudo-element skips flex/grid container display-based 177 /// fixup. 178 #[inline] skip_item_display_fixup(&self) -> bool179 pub fn skip_item_display_fixup(&self) -> bool { 180 (self.flags() & structs::CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM) == 0 181 } 182 183 /// Whether this pseudo-element is precomputed. 184 #[inline] is_precomputed(&self) -> bool185 pub fn is_precomputed(&self) -> bool { 186 self.is_anon_box() && !self.is_tree_pseudo_element() 187 } 188 189 /// Property flag that properties must have to apply to this pseudo-element. 190 #[inline] property_restriction(&self) -> Option<PropertyFlags>191 pub fn property_restriction(&self) -> Option<PropertyFlags> { 192 Some(match *self { 193 PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER, 194 PseudoElement::FirstLine => PropertyFlags::APPLIES_TO_FIRST_LINE, 195 PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER, 196 PseudoElement::Cue => PropertyFlags::APPLIES_TO_CUE, 197 PseudoElement::Marker if static_prefs::pref!("layout.css.marker.restricted") => { 198 PropertyFlags::APPLIES_TO_MARKER 199 }, 200 _ => return None, 201 }) 202 } 203 204 /// Whether this pseudo-element should actually exist if it has 205 /// the given styles. should_exist(&self, style: &ComputedValues) -> bool206 pub fn should_exist(&self, style: &ComputedValues) -> bool { 207 debug_assert!(self.is_eager()); 208 209 if style.get_box().clone_display() == Display::None { 210 return false; 211 } 212 213 if self.is_before_or_after() && style.ineffective_content_property() { 214 return false; 215 } 216 217 true 218 } 219 } 220