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
7use crate::properties::Importance;
8use crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards};
9use crate::stylesheets::Origin;
10
11/// The cascade level these rules are relevant at, as per[1][2][3].
12///
13/// Presentational hints for SVG and HTML are in the "author-level
14/// zero-specificity" level, that is, right after user rules, and before author
15/// rules.
16///
17/// The order of variants declared here is significant, and must be in
18/// _ascending_ order of precedence.
19///
20/// See also [4] for the Shadow DOM bits. We rely on the invariant that rules
21/// from outside the tree the element is in can't affect the element.
22///
23/// The opposite is not true (i.e., :host and ::slotted) from an "inner" shadow
24/// tree may affect an element connected to the document or an "outer" shadow
25/// tree.
26///
27/// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin
28/// [2]: https://drafts.csswg.org/css-cascade/#preshint
29/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
30/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
31#[repr(u8)]
32#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
33pub enum CascadeLevel {
34    /// Normal User-Agent rules.
35    UANormal,
36    /// User normal rules.
37    UserNormal,
38    /// Presentational hints.
39    PresHints,
40    /// Shadow DOM styles from author styles.
41    AuthorNormal {
42        /// The order in the shadow tree hierarchy. This number is relative to
43        /// the tree of the element, and thus the only invariants that need to
44        /// be preserved is:
45        ///
46        ///  * Zero is the same tree as the element that matched the rule. This
47        ///    is important so that we can optimize style attribute insertions.
48        ///
49        ///  * The levels are ordered in accordance with
50        ///    https://drafts.csswg.org/css-scoping/#shadow-cascading
51        shadow_cascade_order: ShadowCascadeOrder,
52    },
53    /// https://drafts.csswg.org/css-anchor-position-1/#position-fallback-origin
54    PositionFallback,
55    /// SVG SMIL animations.
56    SMILOverride,
57    /// CSS animations and script-generated animations.
58    Animations,
59    /// Author-supplied important rules.
60    AuthorImportant {
61        /// The order in the shadow tree hierarchy, inverted, so that PartialOrd
62        /// does the right thing.
63        shadow_cascade_order: ShadowCascadeOrder,
64    },
65    /// User important rules.
66    UserImportant,
67    /// User-agent important rules.
68    UAImportant,
69    /// Transitions
70    Transitions,
71}
72
73impl CascadeLevel {
74    /// Convert this level from "unimportant" to "important".
75    pub fn important(&self) -> Self {
76        match *self {
77            Self::UANormal => Self::UAImportant,
78            Self::UserNormal => Self::UserImportant,
79            Self::AuthorNormal {
80                shadow_cascade_order,
81            } => Self::AuthorImportant {
82                shadow_cascade_order: -shadow_cascade_order,
83            },
84            Self::PresHints
85            | Self::PositionFallback
86            | Self::SMILOverride
87            | Self::Animations
88            | Self::AuthorImportant { .. }
89            | Self::UserImportant
90            | Self::UAImportant
91            | Self::Transitions => *self,
92        }
93    }
94
95    /// Convert this level from "important" to "non-important".
96    pub fn unimportant(&self) -> Self {
97        match *self {
98            Self::UAImportant => Self::UANormal,
99            Self::UserImportant => Self::UserNormal,
100            Self::AuthorImportant {
101                shadow_cascade_order,
102            } => Self::AuthorNormal {
103                shadow_cascade_order: -shadow_cascade_order,
104            },
105            Self::PresHints
106            | Self::PositionFallback
107            | Self::SMILOverride
108            | Self::Animations
109            | Self::AuthorNormal { .. }
110            | Self::UserNormal
111            | Self::UANormal
112            | Self::Transitions => *self,
113        }
114    }
115
116    /// Select a lock guard for this level
117    pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {
118        match *self {
119            Self::UANormal | Self::UserNormal | Self::UserImportant | Self::UAImportant => {
120                guards.ua_or_user
121            },
122            _ => guards.author,
123        }
124    }
125
126    /// Returns the cascade level for author important declarations from the
127    /// same tree as the element.
128    #[inline]
129    pub fn same_tree_author_important() -> Self {
130        Self::AuthorImportant {
131            shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
132        }
133    }
134
135    /// Returns the cascade level for author normal declarations from the same
136    /// tree as the element.
137    #[inline]
138    pub fn same_tree_author_normal() -> Self {
139        Self::AuthorNormal {
140            shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
141        }
142    }
143
144    /// Returns whether this cascade level represents important rules of some
145    /// sort.
146    #[inline]
147    pub fn is_important(&self) -> bool {
148        match *self {
149            Self::AuthorImportant { .. } | Self::UserImportant | Self::UAImportant => true,
150            _ => false,
151        }
152    }
153
154    /// Returns the importance relevant for this rule. Pretty similar to
155    /// `is_important`.
156    #[inline]
157    pub fn importance(&self) -> Importance {
158        if self.is_important() {
159            Importance::Important
160        } else {
161            Importance::Normal
162        }
163    }
164
165    /// Returns the cascade origin of the rule.
166    #[inline]
167    pub fn origin(&self) -> Origin {
168        match *self {
169            Self::UAImportant | Self::UANormal => Origin::UserAgent,
170            Self::UserImportant | Self::UserNormal => Origin::User,
171            Self::PresHints
172            | Self::PositionFallback { .. }
173            | Self::AuthorNormal { .. }
174            | Self::AuthorImportant { .. }
175            | Self::SMILOverride
176            | Self::Animations
177            | Self::Transitions => Origin::Author,
178        }
179    }
180
181    /// Returns whether this cascade level represents an animation rules.
182    #[inline]
183    pub fn is_animation(&self) -> bool {
184        match *self {
185            Self::SMILOverride | Self::Animations | Self::Transitions => true,
186            _ => false,
187        }
188    }
189}
190
191/// A counter to track how many shadow root rules deep we are. This is used to
192/// handle:
193///
194/// https://drafts.csswg.org/css-scoping/#shadow-cascading
195///
196/// See the static functions for the meaning of different values.
197#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
198pub struct ShadowCascadeOrder(i8);
199
200impl ShadowCascadeOrder {
201    /// We keep a maximum of 3 bits of order as a limit so that we can pack
202    /// CascadeLevel in one byte by using half of it for the order, if that ends
203    /// up being necessary.
204    const MAX: i8 = 0b111;
205    const MIN: i8 = -Self::MAX;
206
207    /// A level for the outermost shadow tree (the shadow tree we own, and the
208    /// ones from the slots we're slotted in).
209    #[inline]
210    pub fn for_outermost_shadow_tree() -> Self {
211        Self(-1)
212    }
213
214    /// A level for the element's tree.
215    #[inline]
216    fn for_same_tree() -> Self {
217        Self(0)
218    }
219
220    /// A level for the innermost containing tree (the one closest to the
221    /// element).
222    #[inline]
223    pub fn for_innermost_containing_tree() -> Self {
224        Self(1)
225    }
226
227    /// Decrement the level, moving inwards. We should only move inwards if
228    /// we're traversing slots.
229    #[inline]
230    pub fn dec(&mut self) {
231        debug_assert!(self.0 < 0);
232        if self.0 != Self::MIN {
233            self.0 -= 1;
234        }
235    }
236
237    /// The level, moving inwards. We should only move inwards if we're
238    /// traversing slots.
239    #[inline]
240    pub fn inc(&mut self) {
241        debug_assert_ne!(self.0, -1);
242        if self.0 != Self::MAX {
243            self.0 += 1;
244        }
245    }
246}
247
248impl std::ops::Neg for ShadowCascadeOrder {
249    type Output = Self;
250    #[inline]
251    fn neg(self) -> Self {
252        Self(self.0.neg())
253    }
254}