Bug 1886441: Part 1 - Introduce scope proximity in cascade ordering. r=firefox-style-system-reviewers,emilio

Uses the u32 hole left in `ApplicableDeclarationBlock`.

Differential Revision: https://phabricator.services.mozilla.com/D207778
This commit is contained in:
David Shin 2024-05-30 15:23:40 +00:00
Родитель 5acfcd795d
Коммит 4e40923fb6
4 изменённых файлов: 76 добавлений и 9 удалений

Просмотреть файл

@ -132,6 +132,45 @@ impl CascadePriority {
}
}
/// Proximity to the scope root.
///
/// https://drafts.csswg.org/css-cascade-6/#cascade-proximity
#[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.
///
@ -148,6 +187,8 @@ pub struct ApplicableDeclarationBlock {
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,
}
@ -165,6 +206,7 @@ impl ApplicableDeclarationBlock {
source: StyleSource::from_declarations(declarations),
source_order: 0,
specificity: 0,
scope_proximity: ScopeProximity::infinity(),
cascade_priority: CascadePriority::new(level, layer_order),
}
}
@ -177,11 +219,13 @@ impl ApplicableDeclarationBlock {
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),
}
}
@ -204,12 +248,29 @@ impl ApplicableDeclarationBlock {
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.

Просмотреть файл

@ -143,9 +143,7 @@ where
self.context.current_host = host.map(|e| e.opaque());
f(self);
if start != self.rules.len() {
self.rules[start..].sort_unstable_by_key(|block| {
(block.layer_order(), block.specificity, block.source_order())
});
self.rules[start..].sort_unstable_by_key(|block| block.sort_key());
}
self.context.current_host = old_host;
self.in_sort_scope = false;

Просмотреть файл

@ -5,7 +5,7 @@
//! A data structure to efficiently index structs containing selectors by local
//! name, ids and hash.
use crate::applicable_declarations::ApplicableDeclarationList;
use crate::applicable_declarations::{ApplicableDeclarationList, ScopeProximity};
use crate::context::QuirksMode;
use crate::dom::TElement;
use crate::rule_tree::CascadeLevel;
@ -367,7 +367,12 @@ impl SelectorMap<Rule> {
}
}
matching_rules.push(rule.to_applicable_declaration_block(cascade_level, cascade_data));
// TODO(dshin, bug 1886441): Scope proximity calculation
matching_rules.push(rule.to_applicable_declaration_block(
cascade_level,
cascade_data,
ScopeProximity::infinity(),
));
}
}
}

Просмотреть файл

@ -5,7 +5,7 @@
//! Selector matching.
use crate::applicable_declarations::{
ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority,
ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority, ScopeProximity,
};
use crate::computed_value_flags::ComputedValueFlags;
use crate::context::{CascadeInputs, QuirksMode};
@ -1763,8 +1763,7 @@ impl PageRuleMap {
// Because page-rules do not have source location information stored,
// use stable sort to ensure source locations are preserved.
matched_rules[start..]
.sort_by_key(|block| (block.layer_order(), block.specificity, block.source_order()));
matched_rules[start..].sort_by_key(|block| block.sort_key());
}
fn match_and_add_rules(
@ -1793,6 +1792,7 @@ impl PageRuleMap {
level,
specificity,
cascade_data.layer_order_for(data.layer),
ScopeProximity::infinity(), // Page rule can't have nested rules anyway.
));
}
}
@ -2831,7 +2831,7 @@ impl CascadeData {
debug_assert!(!has_nested_rules);
debug_assert_eq!(stylesheet.contents().origin, Origin::UserAgent);
debug_assert_eq!(containing_rule_state.layer_id, LayerId::root());
// TODO(dshin, bug 1886441): Because we precompute pseudos, we cannot possibly calculate scope proximity.
precomputed_pseudo_element_decls
.as_mut()
.expect("Expected precomputed declarations for the UA level")
@ -2842,6 +2842,7 @@ impl CascadeData {
CascadeLevel::UANormal,
selector.specificity(),
LayerOrder::root(),
ScopeProximity::infinity(),
));
continue;
}
@ -3474,6 +3475,7 @@ impl Rule {
&self,
level: CascadeLevel,
cascade_data: &CascadeData,
scope_proximity: ScopeProximity,
) -> ApplicableDeclarationBlock {
let source = StyleSource::from_rule(self.style_rule.clone());
ApplicableDeclarationBlock::new(
@ -3482,6 +3484,7 @@ impl Rule {
level,
self.specificity(),
cascade_data.layer_order_for(self.layer_id),
scope_proximity,
)
}