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/. */
5 //! The pseudo-classes and pseudo-elements supported by the style system.
9 use crate::element_state::ElementState;
10 use crate::stylesheets::{Namespaces, Origin, UrlExtraData};
11 use crate::values::serialize_atom_identifier;
13 use cssparser::{Parser as CssParser, ParserInput};
14 use selectors::parser::SelectorList;
15 use std::fmt::{self, Debug, Write};
16 use style_traits::{CssWriter, ParseError, ToCss};
18 /// A convenient alias for the type that represents an attribute value used for
19 /// selector parser implementation.
20 pub type AttrValue = <SelectorImpl as ::selectors::SelectorImpl>::AttrValue;
22 #[cfg(feature = "servo")]
23 pub use crate::servo::selector_parser::*;
25 #[cfg(feature = "gecko")]
26 pub use crate::gecko::selector_parser::*;
28 #[cfg(feature = "servo")]
29 pub use crate::servo::selector_parser::ServoElementSnapshot as Snapshot;
31 #[cfg(feature = "gecko")]
32 pub use crate::gecko::snapshot::GeckoElementSnapshot as Snapshot;
34 #[cfg(feature = "servo")]
35 pub use crate::servo::restyle_damage::ServoRestyleDamage as RestyleDamage;
37 #[cfg(feature = "gecko")]
38 pub use crate::gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage;
40 /// Servo's selector parser.
41 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
42 pub struct SelectorParser<'a> {
43 /// The origin of the stylesheet we're parsing.
44 pub stylesheet_origin: Origin,
45 /// The namespace set of the stylesheet.
46 pub namespaces: &'a Namespaces,
47 /// The extra URL data of the stylesheet, which is used to look up
48 /// whether we are parsing a chrome:// URL style sheet.
49 pub url_data: &'a UrlExtraData,
52 impl<'a> SelectorParser<'a> {
53 /// Parse a selector list with an author origin and without taking into
54 /// account namespaces.
56 /// This is used for some DOM APIs like `querySelector`.
57 pub fn parse_author_origin_no_namespace<'i>(
59 url_data: &UrlExtraData,
60 ) -> Result<SelectorList<SelectorImpl>, ParseError<'i>> {
61 let namespaces = Namespaces::default();
62 let parser = SelectorParser {
63 stylesheet_origin: Origin::Author,
64 namespaces: &namespaces,
67 let mut input = ParserInput::new(input);
68 SelectorList::parse(&parser, &mut CssParser::new(&mut input))
71 /// Whether we're parsing selectors in a user-agent stylesheet.
72 pub fn in_user_agent_stylesheet(&self) -> bool {
73 matches!(self.stylesheet_origin, Origin::UserAgent)
76 /// Whether we're parsing selectors in a stylesheet that has chrome
78 pub fn chrome_rules_enabled(&self) -> bool {
79 self.url_data.chrome_rules_enabled() || self.stylesheet_origin == Origin::User
83 /// This enumeration determines if a pseudo-element is eagerly cascaded or not.
85 /// If you're implementing a public selector for `Servo` that the end-user might
86 /// customize, then you probably need to make it eager.
87 #[derive(Clone, Debug, Eq, PartialEq)]
88 pub enum PseudoElementCascadeType {
89 /// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
90 /// `::before` and `::after`). They inherit styles normally as another
91 /// selector would do, and they're computed as part of the cascade.
93 /// Lazy pseudo-elements are affected by selector matching, but they're only
94 /// computed when needed, and not before. They're useful for general
95 /// pseudo-elements that are not very common.
97 /// Note that in Servo lazy pseudo-elements are restricted to a subset of
98 /// selectors, so you can't use it for public pseudo-elements. This is not
99 /// the case with Gecko though.
101 /// Precomputed pseudo-elements skip the cascade process entirely, mostly as
102 /// an optimisation since they are private pseudo-elements (like
103 /// `::-servo-details-content`).
105 /// This pseudo-elements are resolved on the fly using *only* global rules
106 /// (rules of the form `*|*`), and applying them to the parent style.
110 /// A per-pseudo map, from a given pseudo to a `T`.
111 #[derive(Clone, MallocSizeOf)]
112 pub struct PerPseudoElementMap<T> {
113 entries: [Option<T>; PSEUDO_COUNT],
116 impl<T> Default for PerPseudoElementMap<T> {
117 fn default() -> Self {
119 entries: PseudoElement::pseudo_none_array(),
124 impl<T> Debug for PerPseudoElementMap<T>
128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
130 let mut first = true;
131 for entry in self.entries.iter() {
142 impl<T> PerPseudoElementMap<T> {
143 /// Get an entry in the map.
144 pub fn get(&self, pseudo: &PseudoElement) -> Option<&T> {
145 self.entries[pseudo.index()].as_ref()
148 /// Clear this enumerated array.
149 pub fn clear(&mut self) {
150 *self = Self::default();
153 /// Set an entry value.
155 /// Returns an error if the element is not a simple pseudo.
156 pub fn set(&mut self, pseudo: &PseudoElement, value: T) {
157 self.entries[pseudo.index()] = Some(value);
160 /// Get an entry for `pseudo`, or create it with calling `f`.
161 pub fn get_or_insert_with<F>(&mut self, pseudo: &PseudoElement, f: F) -> &mut T
165 let index = pseudo.index();
166 if self.entries[index].is_none() {
167 self.entries[index] = Some(f());
169 self.entries[index].as_mut().unwrap()
172 /// Get an iterator for the entries.
173 pub fn iter(&self) -> std::slice::Iter<Option<T>> {
177 /// Get a mutable iterator for the entries.
178 pub fn iter_mut(&mut self) -> std::slice::IterMut<Option<T>> {
179 self.entries.iter_mut()
183 /// Values for the :dir() pseudo class
185 /// "ltr" and "rtl" values are normalized to lowercase.
186 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
187 pub struct Direction(pub Atom);
189 /// Horizontal values for the :dir() pseudo class
190 #[derive(Clone, Debug, Eq, PartialEq)]
191 pub enum HorizontalDirection {
199 /// Parse a direction value.
200 pub fn parse<'i, 't>(parser: &mut CssParser<'i, 't>) -> Result<Self, ParseError<'i>> {
201 let ident = parser.expect_ident()?;
202 Ok(Direction(match_ignore_ascii_case! { &ident,
203 "rtl" => atom!("rtl"),
204 "ltr" => atom!("ltr"),
205 _ => Atom::from(ident.as_ref()),
209 /// Convert this Direction into a HorizontalDirection, if applicable
210 pub fn as_horizontal_direction(&self) -> Option<HorizontalDirection> {
211 if self.0 == atom!("ltr") {
212 Some(HorizontalDirection::Ltr)
213 } else if self.0 == atom!("rtl") {
214 Some(HorizontalDirection::Rtl)
220 /// Gets the element state relevant to this :dir() selector.
221 pub fn element_state(&self) -> ElementState {
222 match self.as_horizontal_direction() {
223 Some(HorizontalDirection::Ltr) => ElementState::IN_LTR_STATE,
224 Some(HorizontalDirection::Rtl) => ElementState::IN_RTL_STATE,
225 None => ElementState::empty(),
230 impl ToCss for Direction {
231 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
235 serialize_atom_identifier(&self.0, dest)