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