diff --git a/components/style/applicable_declarations.rs b/components/style/applicable_declarations.rs index ab83e5737a99..cc7d9c90db81 100644 --- a/components/style/applicable_declarations.rs +++ b/components/style/applicable_declarations.rs @@ -19,27 +19,37 @@ use smallvec::SmallVec; /// However, it may depend a lot on workload, and stack space is cheap. pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>; +/// Blink uses 18 bits to store source order, and does not check overflow [1]. +/// That's a limit that could be reached in realistic webpages, so we use +/// 24 bits and enforce defined behavior in the overflow case. +/// +/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/ +/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3 +const SOURCE_ORDER_SHIFT: usize = 0; +const SOURCE_ORDER_BITS: usize = 24; +const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1; +const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT; + +/// We pack the cascade level in a single byte, see CascadeLevel::to_byte_lossy +/// for the different trade-offs there. +const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS; + /// Stores the source order of a block, the cascade level it belongs to, and the /// counter needed to handle Shadow DOM cascade order properly. -/// -/// FIXME(emilio): Optimize storage. #[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq, Debug)] -struct ApplicableDeclarationBits { - source_order: u32, - cascade_level: CascadeLevel, -} +struct ApplicableDeclarationBits(u32); impl ApplicableDeclarationBits { fn new(source_order: u32, cascade_level: CascadeLevel) -> Self { - Self { source_order, cascade_level } + Self((source_order & SOURCE_ORDER_MASK) | ((cascade_level.to_byte_lossy() as u32) << CASCADE_LEVEL_SHIFT)) } fn source_order(&self) -> u32 { - self.source_order + self.0 & SOURCE_ORDER_MASK } fn level(&self) -> CascadeLevel { - self.cascade_level + CascadeLevel::from_byte((self.0 >> CASCADE_LEVEL_SHIFT) as u8) } } diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index 304a539744f3..7236c32e30d1 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -681,12 +681,76 @@ pub enum CascadeLevel { /// User-agent important rules. UAImportant, /// Transitions - /// - /// NB: If this changes from being last, change from_byte below. 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 {