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}