mirror of
https://github.com/servo/servo.git
synced 2025-06-16 20:34:30 +00:00
277 lines
8.9 KiB
Rust
277 lines
8.9 KiB
Rust
/* 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())
|
|
}
|
|
}
|