Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
//! Applicable declarations management.
use crate::properties::PropertyDeclarationBlock;
use crate::rule_tree::{CascadeLevel, StyleSource};
use crate::shared_lock::Locked;
use crate::stylesheets::layer_rule::LayerOrder;
use servo_arc::Arc;
use smallvec::SmallVec;
/// List of applicable declarations. This is a transient structure that shuttles
/// declarations between selector matching and inserting into the rule tree, and
/// therefore we want to avoid heap-allocation where possible.
///
/// In measurements on wikipedia, we pretty much never have more than 8 applicable
/// declarations, so we could consider making this 8 entries instead of 16.
/// 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.
///
/// Note that right now this restriction could be lifted if wanted (because we
/// no longer stash the cascade level in the remaining bits), but we keep it in
/// place in case we come up with a use-case for them, lacking reports of the
/// current limit being too small.
///
/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3
const SOURCE_ORDER_BITS: usize = 24;
const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX;
/// The cascade-level+layer order of this declaration.
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
pub struct CascadePriority {
cascade_level: CascadeLevel,
layer_order: LayerOrder,
}
const_assert_eq!(
std::mem::size_of::<CascadePriority>(),
std::mem::size_of::<u32>()
);
impl PartialOrd for CascadePriority {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for CascadePriority {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.cascade_level.cmp(&other.cascade_level).then_with(|| {
let ordering = self.layer_order.cmp(&other.layer_order);
if ordering == std::cmp::Ordering::Equal {
return ordering;
}
//
// Cascade layers (like declarations) are ordered by order
// of appearance. When comparing declarations that belong to
// different layers, then for normal rules the declaration
// whose cascade layer is last wins, and for important rules
// the declaration whose cascade layer is first wins.
//
// But the style attribute layer for some reason is special.
if self.cascade_level.is_important() &&
!self.layer_order.is_style_attribute_layer() &&
!other.layer_order.is_style_attribute_layer()
{
ordering.reverse()
} else {
ordering
}
})
}
}
impl CascadePriority {
/// Construct a new CascadePriority for a given (level, order) pair.
pub fn new(cascade_level: CascadeLevel, layer_order: LayerOrder) -> Self {
Self {
cascade_level,
layer_order,
}
}
/// Returns the layer order.
#[inline]
pub fn layer_order(&self) -> LayerOrder {
self.layer_order
}
/// Returns the cascade level.
#[inline]
pub fn cascade_level(&self) -> CascadeLevel {
self.cascade_level
}
/// Whether this declaration should be allowed if `revert` or `revert-layer`
/// have been specified on a given origin.
///
/// `self` is the priority at which the `revert` or `revert-layer` keyword
/// have been specified.
pub fn allows_when_reverted(&self, other: &Self, origin_revert: bool) -> bool {
if origin_revert {
other.cascade_level.origin() < self.cascade_level.origin()
} else {
other.unimportant() < self.unimportant()
}
}
/// Convert this priority from "important" to "non-important", if needed.
pub fn unimportant(&self) -> Self {
Self::new(self.cascade_level().unimportant(), self.layer_order())
}
/// Convert this priority from "non-important" to "important", if needed.
pub fn important(&self) -> Self {
Self::new(self.cascade_level().important(), self.layer_order())
}
/// The same tree, in author origin, at the root layer.
pub fn same_tree_author_normal_at_root_layer() -> Self {
Self::new(CascadeLevel::same_tree_author_normal(), LayerOrder::root())
}
}
/// Proximity to the scope root.
///
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
pub struct ScopeProximity(u16);
impl PartialOrd for ScopeProximity {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ScopeProximity {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
// Lower proximity to scope root wins
other.0.cmp(&self.0)
}
}
/// Sacrifice the largest possible value for infinity. This makes the comparison
/// trivial.
const PROXIMITY_INFINITY: u16 = u16::MAX;
impl ScopeProximity {
/// Construct a new scope proximity.
pub fn new(proximity: usize) -> Self {
if cfg!(debug_assertions) && proximity >= PROXIMITY_INFINITY as usize {
warn!("Proximity out of bounds");
}
Self(proximity.clamp(0, (PROXIMITY_INFINITY - 1) as usize) as u16)
}
/// Create a scope proximity for delcarations outside of any scope root.
pub fn infinity() -> Self {
Self(PROXIMITY_INFINITY)
}
}
/// A property declaration together with its precedence among rules of equal
/// specificity so that we can sort them.
///
/// This represents the declarations in a given declaration block for a given
/// importance.
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
pub struct ApplicableDeclarationBlock {
/// The style source, either a style rule, or a property declaration block.
#[ignore_malloc_size_of = "Arc"]
pub source: StyleSource,
/// Order of appearance in which this rule appears - Set to 0 if not relevant
/// (e.g. Declaration from `style="/*...*/"`, presentation hints, animations
/// - See `CascadePriority` instead).
source_order: u32,
/// The specificity of the selector.
pub specificity: u32,
/// The proximity to the scope root.
pub scope_proximity: ScopeProximity,
/// The cascade priority of the rule.
pub cascade_priority: CascadePriority,
}
impl ApplicableDeclarationBlock {
/// Constructs an applicable declaration block from a given property
/// declaration block and importance.
#[inline]
pub fn from_declarations(
declarations: Arc<Locked<PropertyDeclarationBlock>>,
level: CascadeLevel,
layer_order: LayerOrder,
) -> Self {
ApplicableDeclarationBlock {
source: StyleSource::from_declarations(declarations),
source_order: 0,
specificity: 0,
scope_proximity: ScopeProximity::infinity(),
cascade_priority: CascadePriority::new(level, layer_order),
}
}
/// Constructs an applicable declaration block from the given components.
#[inline]
pub fn new(
source: StyleSource,
source_order: u32,
level: CascadeLevel,
specificity: u32,
layer_order: LayerOrder,
scope_proximity: ScopeProximity,
) -> Self {
ApplicableDeclarationBlock {
source,
source_order: source_order & SOURCE_ORDER_MASK,
specificity,
scope_proximity,
cascade_priority: CascadePriority::new(level, layer_order),
}
}
/// Returns the source order of the block.
#[inline]
pub fn source_order(&self) -> u32 {
self.source_order
}
/// Returns the cascade level of the block.
#[inline]
pub fn level(&self) -> CascadeLevel {
self.cascade_priority.cascade_level()
}
/// Returns the cascade level of the block.
#[inline]
pub fn layer_order(&self) -> LayerOrder {
self.cascade_priority.layer_order()
}
/// Returns the scope proximity of the block.
#[inline]
pub fn scope_proximity(&self) -> ScopeProximity {
self.scope_proximity
}
/// Convenience method to consume self and return the right thing for the
/// rule tree to iterate over.
#[inline]
pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) {
(self.source, self.cascade_priority)
}
/// Return the key used to sort applicable declarations.
#[inline]
pub fn sort_key(&self) -> (LayerOrder, u32, ScopeProximity, u32) {
(
self.layer_order(),
self.specificity,
self.scope_proximity(),
self.source_order(),
)
}
}
// Size of this struct determines sorting and selector-matching performance.
size_of_test!(ApplicableDeclarationBlock, 24);