diff --git a/components/style/rule_tree/level.rs b/components/style/rule_tree/level.rs new file mode 100644 index 000000000000..c46e63796adb --- /dev/null +++ b/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()) + } +} diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index 9844148bffeb..f9f9500144f8 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -10,7 +10,7 @@ use crate::applicable_declarations::ApplicableDeclarationList; use crate::hash::FxHashMap; use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; -use crate::stylesheets::{Origin, StyleRule}; +use crate::stylesheets::StyleRule; use crate::thread_state; use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps}; use parking_lot::RwLock; @@ -21,7 +21,10 @@ use std::mem; use std::ptr; use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; +mod level; mod map; + +pub use self::level::{CascadeLevel, ShadowCascadeOrder}; use self::map::Map; /// The rule tree, the structure servo uses to preserve the results of selector @@ -172,61 +175,6 @@ const FREE_LIST_SENTINEL: *mut RuleNode = 0x01 as *mut RuleNode; /// another thread is currently adding an entry). We spin if we find this value. const FREE_LIST_LOCKED: *mut RuleNode = 0x02 as *mut RuleNode; -/// 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()) - } -} - impl RuleTree { /// Construct a new rule tree. pub fn new() -> Self { @@ -630,224 +578,6 @@ impl RuleTree { /// where it likely did not result from a rigorous performance analysis.) const RULE_TREE_GC_INTERVAL: usize = 300; -/// 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 { - CascadeLevel::UANormal | - CascadeLevel::UserNormal | - CascadeLevel::UserImportant | - CascadeLevel::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 { - CascadeLevel::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 { - CascadeLevel::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 { - CascadeLevel::AuthorImportant { .. } | - CascadeLevel::UserImportant | - CascadeLevel::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 { - CascadeLevel::UAImportant | CascadeLevel::UANormal => Origin::UserAgent, - CascadeLevel::UserImportant | CascadeLevel::UserNormal => Origin::User, - CascadeLevel::PresHints | - CascadeLevel::AuthorNormal { .. } | - CascadeLevel::AuthorImportant { .. } | - CascadeLevel::SMILOverride | - CascadeLevel::Animations | - CascadeLevel::Transitions => Origin::Author, - } - } - - /// Returns whether this cascade level represents an animation rules. - #[inline] - pub fn is_animation(&self) -> bool { - match *self { - CascadeLevel::SMILOverride | CascadeLevel::Animations | CascadeLevel::Transitions => { - true - }, - _ => false, - } - } -} - /// A node in the rule tree. pub struct RuleNode { /// The root node. Only the root has no root pointer, for obvious reasons.