Skip to main content

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