summaryrefslogtreecommitdiffstats
path: root/servo/components/style/rule_tree/level.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /servo/components/style/rule_tree/level.rs
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--servo/components/style/rule_tree/level.rs277
1 files changed, 277 insertions, 0 deletions
diff --git a/servo/components/style/rule_tree/level.rs b/servo/components/style/rule_tree/level.rs
new file mode 100644
index 0000000000..c46e63796a
--- /dev/null
+++ b/servo/components/style/rule_tree/level.rs
@@ -0,0 +1,277 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#![forbid(unsafe_code)]
+
+use crate::properties::Importance;
+use crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards};
+use crate::stylesheets::Origin;
+
+/// The cascade level these rules are relevant at, as per[1][2][3].
+///
+/// Presentational hints for SVG and HTML are in the "author-level
+/// zero-specificity" level, that is, right after user rules, and before author
+/// rules.
+///
+/// The order of variants declared here is significant, and must be in
+/// _ascending_ order of precedence.
+///
+/// See also [4] for the Shadow DOM bits. We rely on the invariant that rules
+/// from outside the tree the element is in can't affect the element.
+///
+/// The opposite is not true (i.e., :host and ::slotted) from an "inner" shadow
+/// tree may affect an element connected to the document or an "outer" shadow
+/// tree.
+///
+/// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin
+/// [2]: https://drafts.csswg.org/css-cascade/#preshint
+/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
+/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
+#[repr(u8)]
+#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd)]
+pub enum CascadeLevel {
+ /// Normal User-Agent rules.
+ UANormal,
+ /// User normal rules.
+ UserNormal,
+ /// Presentational hints.
+ PresHints,
+ /// Shadow DOM styles from author styles.
+ AuthorNormal {
+ /// The order in the shadow tree hierarchy. This number is relative to
+ /// the tree of the element, and thus the only invariants that need to
+ /// be preserved is:
+ ///
+ /// * Zero is the same tree as the element that matched the rule. This
+ /// is important so that we can optimize style attribute insertions.
+ ///
+ /// * The levels are ordered in accordance with
+ /// https://drafts.csswg.org/css-scoping/#shadow-cascading
+ shadow_cascade_order: ShadowCascadeOrder,
+ },
+ /// SVG SMIL animations.
+ SMILOverride,
+ /// CSS animations and script-generated animations.
+ Animations,
+ /// Author-supplied important rules.
+ AuthorImportant {
+ /// The order in the shadow tree hierarchy, inverted, so that PartialOrd
+ /// does the right thing.
+ shadow_cascade_order: ShadowCascadeOrder,
+ },
+ /// User important rules.
+ UserImportant,
+ /// User-agent important rules.
+ UAImportant,
+ /// Transitions
+ Transitions,
+}
+
+impl CascadeLevel {
+ /// Pack this cascade level in a single byte.
+ ///
+ /// We have 10 levels, which we can represent with 4 bits, and then a
+ /// cascade order optionally, which we can clamp to three bits max, and
+ /// represent with a fourth bit for the sign.
+ ///
+ /// So this creates: SOOODDDD
+ ///
+ /// Where `S` is the sign of the order (one if negative, 0 otherwise), `O`
+ /// is the absolute value of the order, and `D`s are the discriminant.
+ #[inline]
+ pub fn to_byte_lossy(&self) -> u8 {
+ let (discriminant, order) = match *self {
+ Self::UANormal => (0, 0),
+ Self::UserNormal => (1, 0),
+ Self::PresHints => (2, 0),
+ Self::AuthorNormal {
+ shadow_cascade_order,
+ } => (3, shadow_cascade_order.0),
+ Self::SMILOverride => (4, 0),
+ Self::Animations => (5, 0),
+ Self::AuthorImportant {
+ shadow_cascade_order,
+ } => (6, shadow_cascade_order.0),
+ Self::UserImportant => (7, 0),
+ Self::UAImportant => (8, 0),
+ Self::Transitions => (9, 0),
+ };
+
+ debug_assert_eq!(discriminant & 0xf, discriminant);
+ if order == 0 {
+ return discriminant;
+ }
+
+ let negative = order < 0;
+ let value = std::cmp::min(order.abs() as u8, 0b111);
+ (negative as u8) << 7 | value << 4 | discriminant
+ }
+
+ /// Convert back from the single-byte representation of the cascade level
+ /// explained above.
+ #[inline]
+ pub fn from_byte(b: u8) -> Self {
+ let order = {
+ let abs = ((b & 0b01110000) >> 4) as i8;
+ let negative = b & 0b10000000 != 0;
+ if negative {
+ -abs
+ } else {
+ abs
+ }
+ };
+ let discriminant = b & 0xf;
+ let level = match discriminant {
+ 0 => Self::UANormal,
+ 1 => Self::UserNormal,
+ 2 => Self::PresHints,
+ 3 => {
+ return Self::AuthorNormal {
+ shadow_cascade_order: ShadowCascadeOrder(order),
+ }
+ },
+ 4 => Self::SMILOverride,
+ 5 => Self::Animations,
+ 6 => {
+ return Self::AuthorImportant {
+ shadow_cascade_order: ShadowCascadeOrder(order),
+ }
+ },
+ 7 => Self::UserImportant,
+ 8 => Self::UAImportant,
+ 9 => Self::Transitions,
+ _ => unreachable!("Didn't expect {} as a discriminant", discriminant),
+ };
+ debug_assert_eq!(order, 0, "Didn't expect an order value for {:?}", level);
+ level
+ }
+
+ /// Select a lock guard for this level
+ pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {
+ match *self {
+ Self::UANormal | Self::UserNormal | Self::UserImportant | Self::UAImportant => {
+ guards.ua_or_user
+ },
+ _ => guards.author,
+ }
+ }
+
+ /// Returns the cascade level for author important declarations from the
+ /// same tree as the element.
+ #[inline]
+ pub fn same_tree_author_important() -> Self {
+ Self::AuthorImportant {
+ shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
+ }
+ }
+
+ /// Returns the cascade level for author normal declarations from the same
+ /// tree as the element.
+ #[inline]
+ pub fn same_tree_author_normal() -> Self {
+ Self::AuthorNormal {
+ shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
+ }
+ }
+
+ /// Returns whether this cascade level represents important rules of some
+ /// sort.
+ #[inline]
+ pub fn is_important(&self) -> bool {
+ match *self {
+ Self::AuthorImportant { .. } | Self::UserImportant | Self::UAImportant => true,
+ _ => false,
+ }
+ }
+
+ /// Returns the importance relevant for this rule. Pretty similar to
+ /// `is_important`.
+ #[inline]
+ pub fn importance(&self) -> Importance {
+ if self.is_important() {
+ Importance::Important
+ } else {
+ Importance::Normal
+ }
+ }
+
+ /// Returns the cascade origin of the rule.
+ #[inline]
+ pub fn origin(&self) -> Origin {
+ match *self {
+ Self::UAImportant | Self::UANormal => Origin::UserAgent,
+ Self::UserImportant | Self::UserNormal => Origin::User,
+ Self::PresHints |
+ Self::AuthorNormal { .. } |
+ Self::AuthorImportant { .. } |
+ Self::SMILOverride |
+ Self::Animations |
+ Self::Transitions => Origin::Author,
+ }
+ }
+
+ /// Returns whether this cascade level represents an animation rules.
+ #[inline]
+ pub fn is_animation(&self) -> bool {
+ match *self {
+ Self::SMILOverride | Self::Animations | Self::Transitions => true,
+ _ => false,
+ }
+ }
+}
+
+/// A counter to track how many shadow root rules deep we are. This is used to
+/// handle:
+///
+/// https://drafts.csswg.org/css-scoping/#shadow-cascading
+///
+/// See the static functions for the meaning of different values.
+#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
+pub struct ShadowCascadeOrder(i8);
+
+impl ShadowCascadeOrder {
+ /// A level for the outermost shadow tree (the shadow tree we own, and the
+ /// ones from the slots we're slotted in).
+ #[inline]
+ pub fn for_outermost_shadow_tree() -> Self {
+ Self(-1)
+ }
+
+ /// A level for the element's tree.
+ #[inline]
+ fn for_same_tree() -> Self {
+ Self(0)
+ }
+
+ /// A level for the innermost containing tree (the one closest to the
+ /// element).
+ #[inline]
+ pub fn for_innermost_containing_tree() -> Self {
+ Self(1)
+ }
+
+ /// Decrement the level, moving inwards. We should only move inwards if
+ /// we're traversing slots.
+ #[inline]
+ pub fn dec(&mut self) {
+ debug_assert!(self.0 < 0);
+ self.0 = self.0.saturating_sub(1);
+ }
+
+ /// The level, moving inwards. We should only move inwards if we're
+ /// traversing slots.
+ #[inline]
+ pub fn inc(&mut self) {
+ debug_assert_ne!(self.0, -1);
+ self.0 = self.0.saturating_add(1);
+ }
+}
+
+impl std::ops::Neg for ShadowCascadeOrder {
+ type Output = Self;
+ #[inline]
+ fn neg(self) -> Self {
+ Self(self.0.neg())
+ }
+}