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 http://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 cssparser::ToCss;
12 use gecko_bindings::structs::{self, CSSPseudoElementType};
13 use properties::{CascadeFlags, ComputedValues, PropertyFlags};
14 use properties::longhands::display::computed_value::T as Display;
15 use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
16 use std::fmt;
17 use string_cache::Atom;
18 use values::serialize_atom_identifier;
19 
20 include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_definition.rs"));
21 
22 impl ::selectors::parser::PseudoElement for PseudoElement {
23     type Impl = SelectorImpl;
24 
supports_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool25     fn supports_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool {
26         if !self.supports_user_action_state() {
27             return false;
28         }
29 
30         return pseudo_class.is_safe_user_action_state();
31     }
32 }
33 
34 impl PseudoElement {
35     /// Returns the kind of cascade type that a given pseudo is going to use.
36     ///
37     /// In Gecko we only compute ::before and ::after eagerly. We save the rules
38     /// for anonymous boxes separately, so we resolve them as precomputed
39     /// pseudos.
40     ///
41     /// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
cascade_type(&self) -> PseudoElementCascadeType42     pub fn cascade_type(&self) -> PseudoElementCascadeType {
43         if self.is_eager() {
44             debug_assert!(!self.is_anon_box());
45             return PseudoElementCascadeType::Eager
46         }
47 
48         if self.is_precomputed() {
49             return PseudoElementCascadeType::Precomputed
50         }
51 
52         PseudoElementCascadeType::Lazy
53     }
54 
55     /// The CascadeFlags needed to cascade this pseudo-element.
56     ///
57     /// This is only needed to support the broken INHERIT_ALL pseudo mode for
58     /// Servo.
59     #[inline]
cascade_flags(&self) -> CascadeFlags60     pub fn cascade_flags(&self) -> CascadeFlags {
61         CascadeFlags::empty()
62     }
63 
64     /// Whether the pseudo-element should inherit from the default computed
65     /// values instead of from the parent element.
66     ///
67     /// This is not the common thing, but there are some pseudos (namely:
68     /// ::backdrop), that shouldn't inherit from the parent element.
inherits_from_default_values(&self) -> bool69     pub fn inherits_from_default_values(&self) -> bool {
70         matches!(*self, PseudoElement::Backdrop)
71     }
72 
73     /// Gets the canonical index of this eagerly-cascaded pseudo-element.
74     #[inline]
eager_index(&self) -> usize75     pub fn eager_index(&self) -> usize {
76         EAGER_PSEUDOS.iter().position(|p| p == self)
77             .expect("Not an eager pseudo")
78     }
79 
80     /// Creates a pseudo-element from an eager index.
81     #[inline]
from_eager_index(i: usize) -> Self82     pub fn from_eager_index(i: usize) -> Self {
83         EAGER_PSEUDOS[i].clone()
84     }
85 
86     /// Whether the current pseudo element is ::before or ::after.
87     #[inline]
is_before_or_after(&self) -> bool88     pub fn is_before_or_after(&self) -> bool {
89         self.is_before() || self.is_after()
90     }
91 
92     /// Whether this pseudo-element is the ::before pseudo.
93     #[inline]
is_before(&self) -> bool94     pub fn is_before(&self) -> bool {
95         *self == PseudoElement::Before
96     }
97 
98     /// Whether this pseudo-element is the ::after pseudo.
99     #[inline]
is_after(&self) -> bool100     pub fn is_after(&self) -> bool {
101         *self == PseudoElement::After
102     }
103 
104     /// Whether this pseudo-element is ::first-letter.
105     #[inline]
is_first_letter(&self) -> bool106     pub fn is_first_letter(&self) -> bool {
107         *self == PseudoElement::FirstLetter
108     }
109 
110     /// Whether this pseudo-element is ::first-line.
111     #[inline]
is_first_line(&self) -> bool112     pub fn is_first_line(&self) -> bool {
113         *self == PseudoElement::FirstLine
114     }
115 
116     /// Whether this pseudo-element is ::-moz-fieldset-content.
117     #[inline]
is_fieldset_content(&self) -> bool118     pub fn is_fieldset_content(&self) -> bool {
119         *self == PseudoElement::FieldsetContent
120     }
121 
122     /// Whether this pseudo-element is lazily-cascaded.
123     #[inline]
is_lazy(&self) -> bool124     pub fn is_lazy(&self) -> bool {
125         !self.is_eager() && !self.is_precomputed()
126     }
127 
128     /// Whether this pseudo-element supports user action selectors.
supports_user_action_state(&self) -> bool129     pub fn supports_user_action_state(&self) -> bool {
130         (self.flags() & structs::CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) != 0
131     }
132 
133     /// Whether this pseudo-element is enabled for all content.
enabled_in_content(&self) -> bool134     pub fn enabled_in_content(&self) -> bool {
135         (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME) == 0
136     }
137 
138     /// Whether this pseudo is enabled explicitly in UA sheets.
enabled_in_ua_sheets(&self) -> bool139     pub fn enabled_in_ua_sheets(&self) -> bool {
140         (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS) != 0
141     }
142 
143     /// Whether this pseudo is enabled explicitly in chrome sheets.
enabled_in_chrome(&self) -> bool144     pub fn enabled_in_chrome(&self) -> bool {
145         (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_CHROME) != 0
146     }
147 
148     /// Whether this pseudo-element skips flex/grid container display-based
149     /// fixup.
150     #[inline]
skip_item_display_fixup(&self) -> bool151     pub fn skip_item_display_fixup(&self) -> bool {
152         (self.flags() & structs::CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM) == 0
153     }
154 
155     /// Whether this pseudo-element is precomputed.
156     #[inline]
is_precomputed(&self) -> bool157     pub fn is_precomputed(&self) -> bool {
158         self.is_anon_box() && !self.is_tree_pseudo_element()
159     }
160 
161     /// Covert non-canonical pseudo-element to canonical one, and keep a
162     /// canonical one as it is.
canonical(&self) -> PseudoElement163     pub fn canonical(&self) -> PseudoElement {
164         match *self {
165             PseudoElement::MozPlaceholder => PseudoElement::Placeholder,
166             _ => self.clone(),
167         }
168     }
169 
170     /// Property flag that properties must have to apply to this pseudo-element.
171     #[inline]
property_restriction(&self) -> Option<PropertyFlags>172     pub fn property_restriction(&self) -> Option<PropertyFlags> {
173         match *self {
174             PseudoElement::FirstLetter => Some(PropertyFlags::APPLIES_TO_FIRST_LETTER),
175             PseudoElement::FirstLine => Some(PropertyFlags::APPLIES_TO_FIRST_LINE),
176             PseudoElement::Placeholder => Some(PropertyFlags::APPLIES_TO_PLACEHOLDER),
177             _ => None,
178         }
179     }
180 
181     /// Whether this pseudo-element should actually exist if it has
182     /// the given styles.
should_exist(&self, style: &ComputedValues) -> bool183     pub fn should_exist(&self, style: &ComputedValues) -> bool {
184         if style.get_box().clone_display() == Display::None {
185             return false;
186         }
187 
188         if self.is_before_or_after() && style.ineffective_content_property() {
189             return false;
190         }
191 
192         true
193     }
194 }
195