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