Source code

Revision control

Other Tools

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
//! The pseudo-classes and pseudo-elements supported by the style system.
6
7
#![deny(missing_docs)]
8
9
use crate::element_state::ElementState;
10
use crate::stylesheets::{Namespaces, Origin, UrlExtraData};
11
use crate::values::serialize_atom_identifier;
12
use crate::Atom;
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};
17
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;
21
22
#[cfg(feature = "servo")]
23
pub use crate::servo::selector_parser::*;
24
25
#[cfg(feature = "gecko")]
26
pub use crate::gecko::selector_parser::*;
27
28
#[cfg(feature = "servo")]
29
pub use crate::servo::selector_parser::ServoElementSnapshot as Snapshot;
30
31
#[cfg(feature = "gecko")]
32
pub use crate::gecko::snapshot::GeckoElementSnapshot as Snapshot;
33
34
#[cfg(feature = "servo")]
35
pub use crate::servo::restyle_damage::ServoRestyleDamage as RestyleDamage;
36
37
#[cfg(feature = "gecko")]
38
pub use crate::gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage;
39
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: Option<&'a UrlExtraData>,
50
}
51
52
impl<'a> SelectorParser<'a> {
53
/// Parse a selector list with an author origin and without taking into
54
/// account namespaces.
55
///
56
/// This is used for some DOM APIs like `querySelector`.
57
pub fn parse_author_origin_no_namespace(
58
input: &str,
59
) -> Result<SelectorList<SelectorImpl>, ParseError> {
60
let namespaces = Namespaces::default();
61
let parser = SelectorParser {
62
stylesheet_origin: Origin::Author,
63
namespaces: &namespaces,
64
url_data: None,
65
};
66
let mut input = ParserInput::new(input);
67
SelectorList::parse(&parser, &mut CssParser::new(&mut input))
68
}
69
70
/// Whether we're parsing selectors in a user-agent stylesheet.
71
pub fn in_user_agent_stylesheet(&self) -> bool {
72
matches!(self.stylesheet_origin, Origin::UserAgent)
73
}
74
75
/// Whether we're parsing selectors in a stylesheet that has chrome
76
/// privilege.
77
pub fn chrome_rules_enabled(&self) -> bool {
78
self.url_data.map_or(false, |d| d.is_chrome()) || self.stylesheet_origin == Origin::User
79
}
80
}
81
82
/// This enumeration determines if a pseudo-element is eagerly cascaded or not.
83
///
84
/// If you're implementing a public selector for `Servo` that the end-user might
85
/// customize, then you probably need to make it eager.
86
#[derive(Clone, Debug, Eq, PartialEq)]
87
pub enum PseudoElementCascadeType {
88
/// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
89
/// `::before` and `::after`). They inherit styles normally as another
90
/// selector would do, and they're computed as part of the cascade.
91
Eager,
92
/// Lazy pseudo-elements are affected by selector matching, but they're only
93
/// computed when needed, and not before. They're useful for general
94
/// pseudo-elements that are not very common.
95
///
96
/// Note that in Servo lazy pseudo-elements are restricted to a subset of
97
/// selectors, so you can't use it for public pseudo-elements. This is not
98
/// the case with Gecko though.
99
Lazy,
100
/// Precomputed pseudo-elements skip the cascade process entirely, mostly as
101
/// an optimisation since they are private pseudo-elements (like
102
/// `::-servo-details-content`).
103
///
104
/// This pseudo-elements are resolved on the fly using *only* global rules
105
/// (rules of the form `*|*`), and applying them to the parent style.
106
Precomputed,
107
}
108
109
/// A per-pseudo map, from a given pseudo to a `T`.
110
#[derive(MallocSizeOf)]
111
pub struct PerPseudoElementMap<T> {
112
entries: [Option<T>; PSEUDO_COUNT],
113
}
114
115
impl<T> Default for PerPseudoElementMap<T> {
116
fn default() -> Self {
117
Self {
118
entries: PseudoElement::pseudo_none_array(),
119
}
120
}
121
}
122
123
impl<T> Debug for PerPseudoElementMap<T>
124
where
125
T: Debug,
126
{
127
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128
f.write_str("[")?;
129
let mut first = true;
130
for entry in self.entries.iter() {
131
if !first {
132
f.write_str(", ")?;
133
}
134
first = false;
135
entry.fmt(f)?;
136
}
137
f.write_str("]")
138
}
139
}
140
141
impl<T> PerPseudoElementMap<T> {
142
/// Get an entry in the map.
143
pub fn get(&self, pseudo: &PseudoElement) -> Option<&T> {
144
self.entries[pseudo.index()].as_ref()
145
}
146
147
/// Clear this enumerated array.
148
pub fn clear(&mut self) {
149
*self = Self::default();
150
}
151
152
/// Set an entry value.
153
///
154
/// Returns an error if the element is not a simple pseudo.
155
pub fn set(&mut self, pseudo: &PseudoElement, value: T) {
156
self.entries[pseudo.index()] = Some(value);
157
}
158
159
/// Get an entry for `pseudo`, or create it with calling `f`.
160
pub fn get_or_insert_with<F>(&mut self, pseudo: &PseudoElement, f: F) -> &mut T
161
where
162
F: FnOnce() -> T,
163
{
164
let index = pseudo.index();
165
if self.entries[index].is_none() {
166
self.entries[index] = Some(f());
167
}
168
self.entries[index].as_mut().unwrap()
169
}
170
171
/// Get an iterator for the entries.
172
pub fn iter(&self) -> ::std::slice::Iter<Option<T>> {
173
self.entries.iter()
174
}
175
}
176
177
/// Values for the :dir() pseudo class
178
///
179
/// "ltr" and "rtl" values are normalized to lowercase.
180
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
181
pub struct Direction(pub Atom);
182
183
/// Horizontal values for the :dir() pseudo class
184
#[derive(Clone, Debug, Eq, PartialEq)]
185
pub enum HorizontalDirection {
186
/// :dir(ltr)
187
Ltr,
188
/// :dir(rtl)
189
Rtl,
190
}
191
192
impl Direction {
193
/// Parse a direction value.
194
pub fn parse<'i, 't>(parser: &mut CssParser<'i, 't>) -> Result<Self, ParseError<'i>> {
195
let ident = parser.expect_ident()?;
196
Ok(Direction(match_ignore_ascii_case! { &ident,
197
"rtl" => atom!("rtl"),
198
"ltr" => atom!("ltr"),
199
_ => Atom::from(ident.as_ref()),
200
}))
201
}
202
203
/// Convert this Direction into a HorizontalDirection, if applicable
204
pub fn as_horizontal_direction(&self) -> Option<HorizontalDirection> {
205
if self.0 == atom!("ltr") {
206
Some(HorizontalDirection::Ltr)
207
} else if self.0 == atom!("rtl") {
208
Some(HorizontalDirection::Rtl)
209
} else {
210
None
211
}
212
}
213
214
/// Gets the element state relevant to this :dir() selector.
215
pub fn element_state(&self) -> ElementState {
216
match self.as_horizontal_direction() {
217
Some(HorizontalDirection::Ltr) => ElementState::IN_LTR_STATE,
218
Some(HorizontalDirection::Rtl) => ElementState::IN_RTL_STATE,
219
None => ElementState::empty(),
220
}
221
}
222
}
223
224
impl ToCss for Direction {
225
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
226
where
227
W: Write,
228
{
229
serialize_atom_identifier(&self.0, dest)
230
}
231
}