Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move CascadeLevel to its own rule_tree submodule
- Loading branch information
Showing
2 changed files
with
281 additions
and
274 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()) | ||
} | ||
} |
Oops, something went wrong.