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    /// Select a lock guard for this origin.
128    #[inline]
129    pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {
130        match *self {
131            Self::UA | Self::User => guards.ua_or_user,
132            _ => guards.author,
133        }
134    }
135}
136
137impl Ord for CascadeLevel {
138    fn cmp(&self, other: &Self) -> Ordering {
139        let self_important = self.is_important();
140        if self_important != other.is_important() {
141            return if self_important {
142                if other.origin() == CascadeOrigin::Transitions {
143                    // Transitions override all important rules.
144                    return Ordering::Less;
145                }
146                Ordering::Greater
147            } else {
148                if self.origin() == CascadeOrigin::Transitions {
149                    return Ordering::Greater;
150                }
151                Ordering::Less
152            };
153        }
154        let origin_cmp = self
155            .origin()
156            .cmp(&other.origin())
157            .then_with(|| self.shadow_order().cmp(&other.shadow_order()));
158        if self_important {
159            origin_cmp.reverse()
160        } else {
161            origin_cmp
162        }
163    }
164}
165
166impl PartialOrd for CascadeLevel {
167    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
168        Some(self.cmp(other))
169    }
170}
171
172impl CascadeLevel {
173    /// The shift that we apply to the cascade order.
174    pub const CASCADE_ORDER_SHIFT: usize = 4;
175    /// The author level at the same tree. Here for convenience to avoid duplicating it from C++.
176    pub const SAME_TREE_AUTHOR_NORMAL: Self = Self(CascadeOrigin::Author as u8);
177
178    /// Returns a cascade level with a given origin.
179    pub fn new(origin: CascadeOrigin) -> Self {
180        let bits = origin as u8;
181        debug_assert_eq!(bits & Self::ORIGIN_BITS.bits(), bits);
182        Self(bits)
183    }
184
185    /// Returns the cascade origin.
186    #[inline]
187    pub fn origin(self) -> CascadeOrigin {
188        use num_traits::FromPrimitive;
189        let origin = (self & Self::ORIGIN_BITS).bits();
190        CascadeOrigin::from_u8(origin).unwrap()
191    }
192
193    /// Convert this level from "unimportant" to "important".
194    pub fn important(self) -> Self {
195        debug_assert!(
196            matches!(
197                self.origin(),
198                CascadeOrigin::UA | CascadeOrigin::User | CascadeOrigin::Author
199            ),
200            "{self:?}"
201        );
202        let mut result = self;
203        result.insert(Self::IMPORTANT);
204        result
205    }
206
207    /// Convert this level from "important" to "non-important".
208    pub fn unimportant(self) -> Self {
209        let mut result = self;
210        result.remove(Self::IMPORTANT);
211        result
212    }
213
214    /// Select a lock guard for this level
215    #[inline]
216    pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {
217        self.origin().guard(guards)
218    }
219
220    /// Returns the cascade level for author important declarations from the
221    /// same tree as the element.
222    #[inline]
223    pub fn same_tree_author_important() -> Self {
224        Self::new(CascadeOrigin::Author).important()
225    }
226
227    /// Returns the cascade level for author normal declarations from the same
228    /// tree as the element.
229    #[inline]
230    pub fn same_tree_author_normal() -> Self {
231        Self::new(CascadeOrigin::Author)
232    }
233
234    /// Returns whether this cascade level represents important rules of some
235    /// sort.
236    #[inline]
237    pub fn is_important(&self) -> bool {
238        self.intersects(Self::IMPORTANT)
239    }
240
241    /// Returns the importance relevant for this rule. Pretty similar to
242    /// `is_important`.
243    #[inline]
244    pub fn importance(&self) -> Importance {
245        if self.is_important() {
246            Importance::Important
247        } else {
248            Importance::Normal
249        }
250    }
251
252    /// Returns whether this cascade level represents an animation rules.
253    #[inline]
254    pub fn is_animation(&self) -> bool {
255        match self.origin() {
256            CascadeOrigin::SMILOverride
257            | CascadeOrigin::Animations
258            | CascadeOrigin::Transitions => true,
259            _ => false,
260        }
261    }
262
263    /// Returns whether this cascade level is tree.
264    #[inline]
265    pub fn is_tree(self) -> bool {
266        self.origin() == CascadeOrigin::Author
267    }
268
269    /// Returns the shadow tree order.
270    #[inline]
271    pub fn shadow_order(self) -> ShadowCascadeOrder {
272        let neg = self.intersects(Self::CASCADE_ORDER_SIGN);
273        let abs = (self & Self::CASCADE_ORDER_BITS).bits() >> Self::CASCADE_ORDER_SHIFT;
274        ShadowCascadeOrder(if neg { -(abs as i8) } else { abs as i8 })
275    }
276
277    /// Returns an author normal cascade level with the given shadow cascade order.
278    #[inline]
279    pub fn author_normal(shadow_cascade_order: ShadowCascadeOrder) -> Self {
280        let abs = shadow_cascade_order.0.unsigned_abs() << Self::CASCADE_ORDER_SHIFT;
281        let mut result = Self::new(CascadeOrigin::Author);
282        result |= Self::from_bits_truncate(abs);
283        result.set(Self::CASCADE_ORDER_SIGN, shadow_cascade_order.0 < 0);
284        result
285    }
286
287    /// Get the shadow root corresponding to the shadow cascade order, from the given element.
288    pub fn get_shadow_root_for_scoped<E: TElement>(
289        &self,
290        element: E,
291    ) -> Option<<<E as TElement>::ConcreteNode as TNode>::ConcreteShadowRoot> {
292        let mut cascade_order = self.shadow_order().0;
293        if cascade_order < 0 {
294            let element = element.ultimate_originating_element();
295            let mut slot = element.assigned_slot();
296            while let Some(s) = slot {
297                cascade_order += 1;
298                if cascade_order == 0 {
299                    return s.containing_shadow();
300                }
301                slot = s.assigned_slot();
302            }
303            if cascade_order != -1 {
304                return None;
305            }
306
307            return element.shadow_root();
308        }
309        debug_assert!(cascade_order >= 0);
310        let mut containing_shadow = element.containing_shadow();
311        while let Some(s) = containing_shadow {
312            if cascade_order == 0 {
313                break;
314            }
315            cascade_order -= 1;
316            containing_shadow = s.host().containing_shadow();
317        }
318        containing_shadow
319    }
320}
321
322/// A counter to track how many shadow root rules deep we are. This is used to
323/// handle:
324///
325/// https://drafts.csswg.org/css-scoping/#shadow-cascading
326///
327/// See the static functions for the meaning of different values.
328#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
329#[repr(transparent)]
330pub struct ShadowCascadeOrder(i8);
331
332impl ShadowCascadeOrder {
333    /// We keep a maximum of 3 bits of order as a limit so that we can pack CascadeLevel in 1 byte.
334    const MAX: i8 = 0b111;
335    const MIN: i8 = -Self::MAX;
336
337    /// A level for the outermost shadow tree (the shadow tree we own, and the
338    /// ones from the slots we're slotted in).
339    #[inline]
340    pub fn for_outermost_shadow_tree() -> Self {
341        Self(-1)
342    }
343
344    /// A level for the element's tree.
345    #[inline]
346    pub fn for_same_tree() -> Self {
347        Self(0)
348    }
349
350    /// A level for the innermost containing tree (the one closest to the
351    /// element).
352    #[inline]
353    pub fn for_innermost_containing_tree() -> Self {
354        Self(1)
355    }
356
357    /// Returns true if the level is in the same or a containing shadow tree.
358    #[inline]
359    pub fn is_in_same_or_containing_tree(&self) -> bool {
360        self.0 > 0
361    }
362
363    /// Decrement the level, moving inwards. We should only move inwards if
364    /// we're traversing slots.
365    #[inline]
366    pub fn dec(&mut self) {
367        debug_assert!(self.0 < 0);
368        if self.0 != Self::MIN {
369            self.0 -= 1;
370        }
371    }
372
373    /// The level, moving inwards. We should only move inwards if we're
374    /// traversing slots.
375    #[inline]
376    pub fn inc(&mut self) {
377        debug_assert_ne!(self.0, -1);
378        if self.0 != Self::MAX {
379            self.0 += 1;
380        }
381    }
382}
383
384impl std::ops::Neg for ShadowCascadeOrder {
385    type Output = Self;
386    #[inline]
387    fn neg(self) -> Self {
388        Self(self.0.neg())
389    }
390}