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 /// A property declaration together with its precedence among rules of equal
/// specificity so that we can sort them. /// specificity so that we can sort them.
/// ///
@ -148,6 +187,8 @@ pub struct ApplicableDeclarationBlock {
source_order: u32, source_order: u32,
/// The specificity of the selector. /// The specificity of the selector.
pub specificity: u32, pub specificity: u32,
/// The proximity to the scope root.
pub scope_proximity: ScopeProximity,
/// The cascade priority of the rule. /// The cascade priority of the rule.
pub cascade_priority: CascadePriority, pub cascade_priority: CascadePriority,
} }
@ -165,6 +206,7 @@ impl ApplicableDeclarationBlock {
source: StyleSource::from_declarations(declarations), source: StyleSource::from_declarations(declarations),
source_order: 0, source_order: 0,
specificity: 0, specificity: 0,
scope_proximity: ScopeProximity::infinity(),
cascade_priority: CascadePriority::new(level, layer_order), cascade_priority: CascadePriority::new(level, layer_order),
} }
} }
@ -177,11 +219,13 @@ impl ApplicableDeclarationBlock {
level: CascadeLevel, level: CascadeLevel,
specificity: u32, specificity: u32,
layer_order: LayerOrder, layer_order: LayerOrder,
scope_proximity: ScopeProximity,
) -> Self { ) -> Self {
ApplicableDeclarationBlock { ApplicableDeclarationBlock {
source, source,
source_order: source_order & SOURCE_ORDER_MASK, source_order: source_order & SOURCE_ORDER_MASK,
specificity, specificity,
scope_proximity,
cascade_priority: CascadePriority::new(level, layer_order), cascade_priority: CascadePriority::new(level, layer_order),
} }
} }
@ -204,12 +248,29 @@ impl ApplicableDeclarationBlock {
self.cascade_priority.layer_order() 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 /// Convenience method to consume self and return the right thing for the
/// rule tree to iterate over. /// rule tree to iterate over.
#[inline] #[inline]
pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) { pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) {
(self.source, self.cascade_priority) (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 this struct determines sorting and selector-matching performance.

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

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

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

@ -5,7 +5,7 @@
//! A data structure to efficiently index structs containing selectors by local //! A data structure to efficiently index structs containing selectors by local
//! name, ids and hash. //! name, ids and hash.
use crate::applicable_declarations::ApplicableDeclarationList; use crate::applicable_declarations::{ApplicableDeclarationList, ScopeProximity};
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::dom::TElement; use crate::dom::TElement;
use crate::rule_tree::CascadeLevel; 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. //! Selector matching.
use crate::applicable_declarations::{ use crate::applicable_declarations::{
ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority, ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority, ScopeProximity,
}; };
use crate::computed_value_flags::ComputedValueFlags; use crate::computed_value_flags::ComputedValueFlags;
use crate::context::{CascadeInputs, QuirksMode}; use crate::context::{CascadeInputs, QuirksMode};
@ -1763,8 +1763,7 @@ impl PageRuleMap {
// Because page-rules do not have source location information stored, // Because page-rules do not have source location information stored,
// use stable sort to ensure source locations are preserved. // use stable sort to ensure source locations are preserved.
matched_rules[start..] matched_rules[start..].sort_by_key(|block| block.sort_key());
.sort_by_key(|block| (block.layer_order(), block.specificity, block.source_order()));
} }
fn match_and_add_rules( fn match_and_add_rules(
@ -1793,6 +1792,7 @@ impl PageRuleMap {
level, level,
specificity, specificity,
cascade_data.layer_order_for(data.layer), 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!(!has_nested_rules);
debug_assert_eq!(stylesheet.contents().origin, Origin::UserAgent); debug_assert_eq!(stylesheet.contents().origin, Origin::UserAgent);
debug_assert_eq!(containing_rule_state.layer_id, LayerId::root()); 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 precomputed_pseudo_element_decls
.as_mut() .as_mut()
.expect("Expected precomputed declarations for the UA level") .expect("Expected precomputed declarations for the UA level")
@ -2842,6 +2842,7 @@ impl CascadeData {
CascadeLevel::UANormal, CascadeLevel::UANormal,
selector.specificity(), selector.specificity(),
LayerOrder::root(), LayerOrder::root(),
ScopeProximity::infinity(),
)); ));
continue; continue;
} }
@ -3474,6 +3475,7 @@ impl Rule {
&self, &self,
level: CascadeLevel, level: CascadeLevel,
cascade_data: &CascadeData, cascade_data: &CascadeData,
scope_proximity: ScopeProximity,
) -> ApplicableDeclarationBlock { ) -> ApplicableDeclarationBlock {
let source = StyleSource::from_rule(self.style_rule.clone()); let source = StyleSource::from_rule(self.style_rule.clone());
ApplicableDeclarationBlock::new( ApplicableDeclarationBlock::new(
@ -3482,6 +3484,7 @@ impl Rule {
level, level,
self.specificity(), self.specificity(),
cascade_data.layer_order_for(self.layer_id), cascade_data.layer_order_for(self.layer_id),
scope_proximity,
) )
} }