style/rule_tree/level.rs
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#![forbid(unsafe_code)]
6
7//! The cascade level and shadow cascade order for tracking shadow tree rules.
8
9use crate::derives::*;
10use crate::properties::Importance;
11use crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards};
12use crate::stylesheets::Origin;
13
14use std::cmp::{Ord, Ordering, PartialOrd};
15
16/// The cascade level these rules are relevant at, as per[1][2][3].
17///
18/// We store them as a bitfield with:
19///
20/// * Shadow cascade order (4 bits). See ShadowCascadeOrder for details.
21/// * Importance bit. Whether the declaration was !important or not.
22/// * The CascadeOrigin of the declaration as per [1].
23///
24/// Presentational hints for SVG and HTML are in the "author-level zero-specificity" level, that
25/// is, right after user rules, and before author rules.
26///
27/// See also [4] for the Shadow DOM bits. We rely on the invariant that rules
28/// from outside the tree the element is in can't affect the element.
29///
30/// The opposite is not true (i.e., :host and ::slotted) from an "inner" shadow tree may affect an
31/// element connected to the document or an "outer" shadow tree.
32///
33/// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin
34/// [2]: https://drafts.csswg.org/css-cascade/#preshint
35/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
36/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
37#[repr(C)]
38#[derive(
39 Clone,
40 Copy,
41 Debug,
42 Eq,
43 Hash,
44 PartialEq,
45 Serialize,
46 Deserialize,
47 ToAnimatedValue,
48 ToResolvedValue,
49 ToShmem,
50)]
51pub struct CascadeLevel(u8);
52bitflags! {
53 impl CascadeLevel: u8 {
54 /// The three bits that represent the CascadeOrigin.
55 const ORIGIN_BITS = 0b00000111;
56 /// Whether the declarations are `!important` or not.
57 const IMPORTANT = 1 << 3;
58 /// The three bits for cascade order absolute value. If you change this, please change
59 /// CASCADE_ORDER_SHIFT accordingly.
60 const CASCADE_ORDER_BITS = 0b01110000;
61 /// The bit for the sign.
62 const CASCADE_ORDER_SIGN = 1 << 7;
63 }
64}
65
66malloc_size_of::malloc_size_of_is_0!(CascadeLevel);
67
68/// The cascade origin of rules.
69///
70/// Presentational hints for SVG and HTML are in the "author-level zero-specificity" level, that
71/// is, right after user rules, and before author rules.
72///
73/// The order of variants declared here is significant, and must be in _ascending_ order of
74/// precedence.
75///
76/// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin
77/// [2]: https://drafts.csswg.org/css-cascade/#cascade-origin
78#[derive(
79 Clone,
80 Copy,
81 Debug,
82 Deserialize,
83 Eq,
84 FromPrimitive,
85 Hash,
86 MallocSizeOf,
87 Ord,
88 PartialEq,
89 PartialOrd,
90 Serialize,
91 ToAnimatedValue,
92 ToResolvedValue,
93 ToShmem,
94)]
95#[repr(u8)]
96pub enum CascadeOrigin {
97 /// Normal User-Agent rules.
98 UA = 0,
99 /// User normal rules.
100 User,
101 /// Presentational hints.
102 PresHints,
103 /// Styles from author styles.
104 Author,
105 /// https://drafts.csswg.org/css-anchor-position-1/#position-fallback-origin
106 PositionFallback,
107 /// SVG SMIL animations.
108 SMILOverride,
109 /// CSS animations and script-generated animations.
110 Animations,
111 /// CSS Transitions
112 Transitions,
113}
114
115impl CascadeOrigin {
116 /// Returns the "simplified" origin.
117 #[inline]
118 pub fn origin(self) -> Origin {
119 match self {
120 Self::UA => Origin::UserAgent,
121 Self::User => Origin::User,
122 _ => Origin::Author,
123 }
124 }
125
126 /// Returns whether this is an "author" origin (in the "simplified" sense of the word).
127 #[inline]
128 pub fn is_author_origin(self) -> bool {
129 self > Self::User
130 }
131
132 /// Select a lock guard for this origin.
133 #[inline]
134 pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {
135 match *self {
136 Self::UA | Self::User => guards.ua_or_user,
137 _ => guards.author,
138 }
139 }
140}
141
142impl Ord for CascadeLevel {
143 fn cmp(&self, other: &Self) -> Ordering {
144 let self_important = self.is_important();
145 if self_important != other.is_important() {
146 return if self_important {
147 if other.origin() == CascadeOrigin::Transitions {
148 // Transitions override all important rules.
149 return Ordering::Less;
150 }
151 Ordering::Greater
152 } else {
153 if self.origin() == CascadeOrigin::Transitions {
154 return Ordering::Greater;
155 }
156 Ordering::Less
157 };
158 }
159 let origin_cmp = self
160 .origin()
161 .cmp(&other.origin())
162 .then_with(|| self.shadow_order().cmp(&other.shadow_order()));
163 if self_important {
164 origin_cmp.reverse()
165 } else {
166 origin_cmp
167 }
168 }
169}
170
171impl PartialOrd for CascadeLevel {
172 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
173 Some(self.cmp(other))
174 }
175}
176
177impl CascadeLevel {
178 /// The shift that we apply to the cascade order.
179 pub const CASCADE_ORDER_SHIFT: usize = 4;
180 /// The author level at the same tree. Here for convenience to avoid duplicating it from C++.
181 pub const SAME_TREE_AUTHOR_NORMAL: Self = Self(CascadeOrigin::Author as u8);
182
183 /// Returns a cascade level with a given origin.
184 pub fn new(origin: CascadeOrigin) -> Self {
185 let bits = origin as u8;
186 debug_assert_eq!(bits & Self::ORIGIN_BITS.bits(), bits);
187 Self(bits)
188 }
189
190 /// Returns the cascade origin.
191 #[inline]
192 pub fn origin(self) -> CascadeOrigin {
193 use num_traits::FromPrimitive;
194 let origin = (self & Self::ORIGIN_BITS).bits();
195 CascadeOrigin::from_u8(origin).unwrap()
196 }
197
198 /// Convert this level from "unimportant" to "important".
199 pub fn important(self) -> Self {
200 debug_assert!(
201 matches!(
202 self.origin(),
203 CascadeOrigin::UA | CascadeOrigin::User | CascadeOrigin::Author
204 ),
205 "{self:?}"
206 );
207 let mut result = self;
208 result.insert(Self::IMPORTANT);
209 result
210 }
211
212 /// Convert this level from "important" to "non-important".
213 pub fn unimportant(self) -> Self {
214 let mut result = self;
215 result.remove(Self::IMPORTANT);
216 result
217 }
218
219 /// Select a lock guard for this level
220 #[inline]
221 pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {
222 self.origin().guard(guards)
223 }
224
225 /// Returns the cascade level for author important declarations from the
226 /// same tree as the element.
227 #[inline]
228 pub fn same_tree_author_important() -> Self {
229 Self::new(CascadeOrigin::Author).important()
230 }
231
232 /// Returns the cascade level for author normal declarations from the same
233 /// tree as the element.
234 #[inline]
235 pub fn same_tree_author_normal() -> Self {
236 Self::new(CascadeOrigin::Author)
237 }
238
239 /// Returns whether this cascade level represents important rules of some
240 /// sort.
241 #[inline]
242 pub fn is_important(&self) -> bool {
243 self.intersects(Self::IMPORTANT)
244 }
245
246 /// Returns the importance relevant for this rule. Pretty similar to
247 /// `is_important`.
248 #[inline]
249 pub fn importance(&self) -> Importance {
250 if self.is_important() {
251 Importance::Important
252 } else {
253 Importance::Normal
254 }
255 }
256
257 /// Returns whether this cascade level represents an animation rules.
258 #[inline]
259 pub fn is_animation(&self) -> bool {
260 match self.origin() {
261 CascadeOrigin::SMILOverride
262 | CascadeOrigin::Animations
263 | CascadeOrigin::Transitions => true,
264 _ => false,
265 }
266 }
267
268 /// Returns whether this cascade level is tree.
269 #[inline]
270 pub fn is_tree(self) -> bool {
271 self.origin() == CascadeOrigin::Author
272 }
273
274 #[inline]
275 fn shadow_order(self) -> ShadowCascadeOrder {
276 let neg = self.intersects(Self::CASCADE_ORDER_SIGN);
277 let abs = (self & Self::CASCADE_ORDER_BITS).bits() >> Self::CASCADE_ORDER_SHIFT;
278 ShadowCascadeOrder(if neg { -(abs as i8) } else { abs as i8 })
279 }
280
281 /// Returns an author normal cascade level with the given shadow cascade order.
282 #[inline]
283 pub fn author_normal(shadow_cascade_order: ShadowCascadeOrder) -> Self {
284 let abs = (shadow_cascade_order.0.abs() as u8) << Self::CASCADE_ORDER_SHIFT;
285 let mut result = Self::new(CascadeOrigin::Author);
286 result |= Self::from_bits_truncate(abs);
287 result.set(Self::CASCADE_ORDER_SIGN, shadow_cascade_order.0 < 0);
288 result
289 }
290}
291
292/// A counter to track how many shadow root rules deep we are. This is used to
293/// handle:
294///
295/// https://drafts.csswg.org/css-scoping/#shadow-cascading
296///
297/// See the static functions for the meaning of different values.
298#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
299#[repr(transparent)]
300pub struct ShadowCascadeOrder(i8);
301
302impl ShadowCascadeOrder {
303 /// We keep a maximum of 3 bits of order as a limit so that we can pack CascadeLevel in 1 byte.
304 const MAX: i8 = 0b111;
305 const MIN: i8 = -Self::MAX;
306
307 /// A level for the outermost shadow tree (the shadow tree we own, and the
308 /// ones from the slots we're slotted in).
309 #[inline]
310 pub fn for_outermost_shadow_tree() -> Self {
311 Self(-1)
312 }
313
314 /// A level for the element's tree.
315 #[inline]
316 pub fn for_same_tree() -> Self {
317 Self(0)
318 }
319
320 /// A level for the innermost containing tree (the one closest to the
321 /// element).
322 #[inline]
323 pub fn for_innermost_containing_tree() -> Self {
324 Self(1)
325 }
326
327 /// Decrement the level, moving inwards. We should only move inwards if
328 /// we're traversing slots.
329 #[inline]
330 pub fn dec(&mut self) {
331 debug_assert!(self.0 < 0);
332 if self.0 != Self::MIN {
333 self.0 -= 1;
334 }
335 }
336
337 /// The level, moving inwards. We should only move inwards if we're
338 /// traversing slots.
339 #[inline]
340 pub fn inc(&mut self) {
341 debug_assert_ne!(self.0, -1);
342 if self.0 != Self::MAX {
343 self.0 += 1;
344 }
345 }
346}
347
348impl std::ops::Neg for ShadowCascadeOrder {
349 type Output = Self;
350 #[inline]
351 fn neg(self) -> Self {
352 Self(self.0.neg())
353 }
354}