mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Auto merge of #14175 - bholley:separate_cascade, r=emilio
Separate selector matching from property cascading This builds on @emilio's rule tree work. The goal is to raise the boundary between rule node computation and property cascading higher up the callstack, so that the new traversal architecture can decide to do one but not the other. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14175) <!-- Reviewable:end -->
This commit is contained in:
commit
3b2e3dcfb9
2 changed files with 86 additions and 92 deletions
|
@ -18,7 +18,7 @@ use properties::{CascadeFlags, ComputedValues, SHAREABLE, cascade};
|
||||||
use properties::longhands::display::computed_value as display;
|
use properties::longhands::display::computed_value as display;
|
||||||
use rule_tree::StrongRuleNode;
|
use rule_tree::StrongRuleNode;
|
||||||
use selector_impl::{PseudoElement, RestyleDamage, TheSelectorImpl};
|
use selector_impl::{PseudoElement, RestyleDamage, TheSelectorImpl};
|
||||||
use selector_matching::{ApplicableDeclarationBlock, Stylist};
|
use selector_matching::ApplicableDeclarationBlock;
|
||||||
use selectors::MatchAttr;
|
use selectors::MatchAttr;
|
||||||
use selectors::bloom::BloomFilter;
|
use selectors::bloom::BloomFilter;
|
||||||
use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, MatchingReason, StyleRelations};
|
use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, MatchingReason, StyleRelations};
|
||||||
|
@ -50,29 +50,19 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &
|
||||||
flags
|
flags
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ApplicableDeclarations {
|
type PseudoRuleNodes = HashMap<PseudoElement, StrongRuleNode,
|
||||||
pub normal: Vec<ApplicableDeclarationBlock>,
|
BuildHasherDefault<::fnv::FnvHasher>>;
|
||||||
pub per_pseudo: HashMap<PseudoElement,
|
pub struct MatchResults {
|
||||||
Vec<ApplicableDeclarationBlock>,
|
pub primary: StrongRuleNode,
|
||||||
BuildHasherDefault<::fnv::FnvHasher>>,
|
pub relations: StyleRelations,
|
||||||
|
pub per_pseudo: PseudoRuleNodes,
|
||||||
/// Whether the `normal` declarations are shareable with other nodes.
|
|
||||||
pub normal_shareable: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicableDeclarations {
|
impl MatchResults {
|
||||||
pub fn new() -> Self {
|
/// Returns true if the primary rule node is shareable with other nodes.
|
||||||
let mut applicable_declarations = ApplicableDeclarations {
|
pub fn primary_is_shareable(&self) -> bool {
|
||||||
normal: Vec::with_capacity(16),
|
use traversal::relations_are_shareable;
|
||||||
per_pseudo: HashMap::with_hasher(Default::default()),
|
relations_are_shareable(&self.relations)
|
||||||
normal_shareable: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
TheSelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
|
||||||
applicable_declarations.per_pseudo.insert(pseudo, vec![]);
|
|
||||||
});
|
|
||||||
|
|
||||||
applicable_declarations
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,17 +391,12 @@ trait PrivateMatchMethods: TElement {
|
||||||
context: &Ctx,
|
context: &Ctx,
|
||||||
parent_style: Option<&Arc<ComputedValues>>,
|
parent_style: Option<&Arc<ComputedValues>>,
|
||||||
old_style: Option<&Arc<ComputedValues>>,
|
old_style: Option<&Arc<ComputedValues>>,
|
||||||
applicable_declarations: &mut Vec<ApplicableDeclarationBlock>,
|
rule_node: &StrongRuleNode,
|
||||||
booleans: CascadeBooleans)
|
booleans: CascadeBooleans)
|
||||||
-> (Arc<ComputedValues>, StrongRuleNode)
|
-> Arc<ComputedValues>
|
||||||
where Ctx: StyleContext<'a>
|
where Ctx: StyleContext<'a>
|
||||||
{
|
{
|
||||||
let shared_context = context.shared_context();
|
let shared_context = context.shared_context();
|
||||||
let rule_node =
|
|
||||||
shared_context.stylist.rule_tree
|
|
||||||
.insert_ordered_rules(
|
|
||||||
applicable_declarations.drain(..).map(|d| (d.source, d.importance)));
|
|
||||||
|
|
||||||
let mut cascade_info = CascadeInfo::new();
|
let mut cascade_info = CascadeInfo::new();
|
||||||
let mut cascade_flags = CascadeFlags::empty();
|
let mut cascade_flags = CascadeFlags::empty();
|
||||||
if booleans.shareable {
|
if booleans.shareable {
|
||||||
|
@ -421,7 +406,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
let this_style = match parent_style {
|
let this_style = match parent_style {
|
||||||
Some(ref parent_style) => {
|
Some(ref parent_style) => {
|
||||||
cascade(shared_context.viewport_size,
|
cascade(shared_context.viewport_size,
|
||||||
&rule_node,
|
rule_node,
|
||||||
Some(&***parent_style),
|
Some(&***parent_style),
|
||||||
Some(&mut cascade_info),
|
Some(&mut cascade_info),
|
||||||
shared_context.error_reporter.clone(),
|
shared_context.error_reporter.clone(),
|
||||||
|
@ -429,7 +414,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
cascade(shared_context.viewport_size,
|
cascade(shared_context.viewport_size,
|
||||||
&rule_node,
|
rule_node,
|
||||||
None,
|
None,
|
||||||
Some(&mut cascade_info),
|
Some(&mut cascade_info),
|
||||||
shared_context.error_reporter.clone(),
|
shared_context.error_reporter.clone(),
|
||||||
|
@ -461,7 +446,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(this_style, rule_node)
|
this_style
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_animations_for_cascade(&self,
|
fn update_animations_for_cascade(&self,
|
||||||
|
@ -516,45 +501,63 @@ trait PrivateMatchMethods: TElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_rule_node<'a, Ctx>(context: &Ctx,
|
||||||
|
applicable_declarations: &mut Vec<ApplicableDeclarationBlock>)
|
||||||
|
-> StrongRuleNode
|
||||||
|
where Ctx: StyleContext<'a>
|
||||||
|
{
|
||||||
|
let shared_context = context.shared_context();
|
||||||
|
let rules = applicable_declarations.drain(..).map(|d| (d.source, d.importance));
|
||||||
|
let rule_node = shared_context.stylist.rule_tree.insert_ordered_rules(rules);
|
||||||
|
rule_node
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: TElement> PrivateMatchMethods for E {}
|
impl<E: TElement> PrivateMatchMethods for E {}
|
||||||
|
|
||||||
pub trait MatchMethods : TElement {
|
pub trait MatchMethods : TElement {
|
||||||
fn match_element(&self,
|
fn match_element<'a, Ctx>(&self, context: &Ctx, parent_bf: Option<&BloomFilter>)
|
||||||
stylist: &Stylist,
|
-> MatchResults
|
||||||
parent_bf: Option<&BloomFilter>,
|
where Ctx: StyleContext<'a>
|
||||||
mut applicable_declarations: &mut ApplicableDeclarations)
|
{
|
||||||
-> StyleRelations {
|
let mut applicable_declarations: Vec<ApplicableDeclarationBlock> = Vec::with_capacity(16);
|
||||||
use traversal::relations_are_shareable;
|
let stylist = &context.shared_context().stylist;
|
||||||
|
|
||||||
let style_attribute = self.style_attribute();
|
let style_attribute = self.style_attribute();
|
||||||
|
|
||||||
let mut relations =
|
// Compute the primary rule node.
|
||||||
|
let mut primary_relations =
|
||||||
stylist.push_applicable_declarations(self,
|
stylist.push_applicable_declarations(self,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
style_attribute,
|
style_attribute,
|
||||||
None,
|
None,
|
||||||
&mut applicable_declarations.normal,
|
&mut applicable_declarations,
|
||||||
MatchingReason::ForStyling);
|
MatchingReason::ForStyling);
|
||||||
|
let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
|
||||||
|
|
||||||
applicable_declarations.normal_shareable = relations_are_shareable(&relations);
|
// Compute the pseudo rule nodes.
|
||||||
|
let mut per_pseudo: PseudoRuleNodes = HashMap::with_hasher(Default::default());
|
||||||
TheSelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
TheSelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||||
stylist.push_applicable_declarations(self,
|
debug_assert!(applicable_declarations.is_empty());
|
||||||
parent_bf,
|
stylist.push_applicable_declarations(self, parent_bf, None,
|
||||||
None,
|
|
||||||
Some(&pseudo.clone()),
|
Some(&pseudo.clone()),
|
||||||
applicable_declarations.per_pseudo.entry(pseudo).or_insert(vec![]),
|
&mut applicable_declarations,
|
||||||
MatchingReason::ForStyling);
|
MatchingReason::ForStyling);
|
||||||
|
|
||||||
|
if !applicable_declarations.is_empty() {
|
||||||
|
let rule_node = compute_rule_node(context, &mut applicable_declarations);
|
||||||
|
per_pseudo.insert(pseudo, rule_node);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let has_pseudos =
|
// If we have any pseudo elements, indicate so in the primary StyleRelations.
|
||||||
applicable_declarations.per_pseudo.values().any(|v| !v.is_empty());
|
if !per_pseudo.is_empty() {
|
||||||
|
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
|
||||||
if has_pseudos {
|
|
||||||
relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
relations
|
MatchResults {
|
||||||
|
primary: primary_rule_node,
|
||||||
|
relations: primary_relations,
|
||||||
|
per_pseudo: per_pseudo,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to share a style with another node. This method is unsafe because it depends on
|
/// Attempts to share a style with another node. This method is unsafe because it depends on
|
||||||
|
@ -712,7 +715,9 @@ pub trait MatchMethods : TElement {
|
||||||
context: &Ctx,
|
context: &Ctx,
|
||||||
mut data: AtomicRefMut<ElementData>,
|
mut data: AtomicRefMut<ElementData>,
|
||||||
parent: Option<Self>,
|
parent: Option<Self>,
|
||||||
mut applicable_declarations: ApplicableDeclarations)
|
primary_rule_node: StrongRuleNode,
|
||||||
|
pseudo_rule_nodes: PseudoRuleNodes,
|
||||||
|
primary_is_shareable: bool)
|
||||||
where Ctx: StyleContext<'a>
|
where Ctx: StyleContext<'a>
|
||||||
{
|
{
|
||||||
// Get our parent's style.
|
// Get our parent's style.
|
||||||
|
@ -722,8 +727,6 @@ pub trait MatchMethods : TElement {
|
||||||
let mut new_styles;
|
let mut new_styles;
|
||||||
|
|
||||||
let damage = {
|
let damage = {
|
||||||
let shareable = applicable_declarations.normal_shareable;
|
|
||||||
|
|
||||||
let (old_primary, old_pseudos) = match data.previous_styles_mut() {
|
let (old_primary, old_pseudos) = match data.previous_styles_mut() {
|
||||||
None => (None, None),
|
None => (None, None),
|
||||||
Some(previous) => {
|
Some(previous) => {
|
||||||
|
@ -735,17 +738,17 @@ pub trait MatchMethods : TElement {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (new_style, rule_node) =
|
let new_style =
|
||||||
self.cascade_node_pseudo_element(context,
|
self.cascade_node_pseudo_element(context,
|
||||||
parent_style,
|
parent_style,
|
||||||
old_primary,
|
old_primary,
|
||||||
&mut applicable_declarations.normal,
|
&primary_rule_node,
|
||||||
CascadeBooleans {
|
CascadeBooleans {
|
||||||
shareable: shareable,
|
shareable: primary_is_shareable,
|
||||||
animate: true,
|
animate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
new_styles = ElementStyles::new(new_style, rule_node);
|
new_styles = ElementStyles::new(new_style, primary_rule_node);
|
||||||
|
|
||||||
let damage =
|
let damage =
|
||||||
self.compute_damage_and_cascade_pseudos(old_primary,
|
self.compute_damage_and_cascade_pseudos(old_primary,
|
||||||
|
@ -753,7 +756,7 @@ pub trait MatchMethods : TElement {
|
||||||
&new_styles.primary,
|
&new_styles.primary,
|
||||||
&mut new_styles.pseudos,
|
&mut new_styles.pseudos,
|
||||||
context,
|
context,
|
||||||
&mut applicable_declarations);
|
pseudo_rule_nodes);
|
||||||
|
|
||||||
self.as_node().set_can_be_fragmented(parent.map_or(false, |p| {
|
self.as_node().set_can_be_fragmented(parent.map_or(false, |p| {
|
||||||
p.as_node().can_be_fragmented() ||
|
p.as_node().can_be_fragmented() ||
|
||||||
|
@ -775,7 +778,7 @@ pub trait MatchMethods : TElement {
|
||||||
new_primary: &Arc<ComputedValues>,
|
new_primary: &Arc<ComputedValues>,
|
||||||
new_pseudos: &mut PseudoStyles,
|
new_pseudos: &mut PseudoStyles,
|
||||||
context: &Ctx,
|
context: &Ctx,
|
||||||
applicable_declarations: &mut ApplicableDeclarations)
|
mut pseudo_rule_nodes: PseudoRuleNodes)
|
||||||
-> RestyleDamage
|
-> RestyleDamage
|
||||||
where Ctx: StyleContext<'a>
|
where Ctx: StyleContext<'a>
|
||||||
{
|
{
|
||||||
|
@ -817,17 +820,15 @@ pub trait MatchMethods : TElement {
|
||||||
|
|
||||||
debug_assert!(new_pseudos.is_empty());
|
debug_assert!(new_pseudos.is_empty());
|
||||||
<Self as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
<Self as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||||
let mut applicable_declarations_for_this_pseudo =
|
let maybe_rule_node = pseudo_rule_nodes.remove(&pseudo);
|
||||||
applicable_declarations.per_pseudo.get_mut(&pseudo).unwrap();
|
|
||||||
|
|
||||||
let has_declarations =
|
|
||||||
!applicable_declarations_for_this_pseudo.is_empty();
|
|
||||||
|
|
||||||
// Grab the old pseudo style for analysis.
|
// Grab the old pseudo style for analysis.
|
||||||
let mut maybe_old_pseudo_style_and_rule_node =
|
let mut maybe_old_pseudo_style_and_rule_node =
|
||||||
old_pseudos.as_mut().and_then(|x| x.remove(&pseudo));
|
old_pseudos.as_mut().and_then(|x| x.remove(&pseudo));
|
||||||
|
|
||||||
if has_declarations {
|
if maybe_rule_node.is_some() {
|
||||||
|
let new_rule_node = maybe_rule_node.unwrap();
|
||||||
|
|
||||||
// We have declarations, so we need to cascade. Compute parameters.
|
// We have declarations, so we need to cascade. Compute parameters.
|
||||||
let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
|
let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
|
||||||
if animate {
|
if animate {
|
||||||
|
@ -839,10 +840,10 @@ pub trait MatchMethods : TElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (new_pseudo_style, new_rule_node) =
|
let new_pseudo_style =
|
||||||
self.cascade_node_pseudo_element(context, Some(new_primary),
|
self.cascade_node_pseudo_element(context, Some(new_primary),
|
||||||
maybe_old_pseudo_style_and_rule_node.as_ref().map(|s| &s.0),
|
maybe_old_pseudo_style_and_rule_node.as_ref().map(|s| &s.0),
|
||||||
&mut applicable_declarations_for_this_pseudo,
|
&new_rule_node,
|
||||||
CascadeBooleans {
|
CascadeBooleans {
|
||||||
shareable: false,
|
shareable: false,
|
||||||
animate: animate,
|
animate: animate,
|
||||||
|
|
|
@ -8,7 +8,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||||
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||||
use data::ElementData;
|
use data::ElementData;
|
||||||
use dom::{OpaqueNode, StylingMode, TElement, TNode, UnsafeNode};
|
use dom::{OpaqueNode, StylingMode, TElement, TNode, UnsafeNode};
|
||||||
use matching::{ApplicableDeclarations, MatchMethods, StyleSharingResult};
|
use matching::{MatchMethods, StyleSharingResult};
|
||||||
use selectors::bloom::BloomFilter;
|
use selectors::bloom::BloomFilter;
|
||||||
use selectors::matching::StyleRelations;
|
use selectors::matching::StyleRelations;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -285,16 +285,14 @@ fn ensure_element_styled_internal<'a, E, C>(element: E,
|
||||||
// Note that we could add the bloom filter's complexity here, but that's
|
// Note that we could add the bloom filter's complexity here, but that's
|
||||||
// probably not necessary since we're likely to be matching only a few
|
// probably not necessary since we're likely to be matching only a few
|
||||||
// nodes, at best.
|
// nodes, at best.
|
||||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
|
||||||
let data = prepare_for_styling(element, element.get_data().unwrap());
|
let data = prepare_for_styling(element, element.get_data().unwrap());
|
||||||
let stylist = &context.shared_context().stylist;
|
let match_results = element.match_element(context, None);
|
||||||
|
|
||||||
element.match_element(&**stylist,
|
|
||||||
None,
|
|
||||||
&mut applicable_declarations);
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
element.cascade_node(context, data, parent, applicable_declarations);
|
let shareable = match_results.primary_is_shareable();
|
||||||
|
element.cascade_node(context, data, parent,
|
||||||
|
match_results.primary,
|
||||||
|
match_results.per_pseudo,
|
||||||
|
shareable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,34 +328,29 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
|
||||||
// Otherwise, match and cascade selectors.
|
// Otherwise, match and cascade selectors.
|
||||||
match sharing_result {
|
match sharing_result {
|
||||||
StyleSharingResult::CannotShare => {
|
StyleSharingResult::CannotShare => {
|
||||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
let match_results;
|
||||||
|
|
||||||
let relations;
|
|
||||||
let shareable_element = {
|
let shareable_element = {
|
||||||
if opts::get().style_sharing_stats {
|
if opts::get().style_sharing_stats {
|
||||||
STYLE_SHARING_CACHE_MISSES.fetch_add(1, Ordering::Relaxed);
|
STYLE_SHARING_CACHE_MISSES.fetch_add(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the CSS selector matching.
|
// Perform the CSS selector matching.
|
||||||
let stylist = &context.shared_context().stylist;
|
match_results = element.match_element(context, Some(&*bf));
|
||||||
|
if match_results.primary_is_shareable() {
|
||||||
relations = element.match_element(&**stylist,
|
|
||||||
Some(&*bf),
|
|
||||||
&mut applicable_declarations);
|
|
||||||
|
|
||||||
debug!("Result of selector matching: {:?}", relations);
|
|
||||||
|
|
||||||
if relations_are_shareable(&relations) {
|
|
||||||
Some(element)
|
Some(element)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let relations = match_results.relations;
|
||||||
|
|
||||||
// Perform the CSS cascade.
|
// Perform the CSS cascade.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let shareable = match_results.primary_is_shareable();
|
||||||
element.cascade_node(context, data, element.parent_element(),
|
element.cascade_node(context, data, element.parent_element(),
|
||||||
applicable_declarations);
|
match_results.primary,
|
||||||
|
match_results.per_pseudo,
|
||||||
|
shareable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add ourselves to the LRU cache.
|
// Add ourselves to the LRU cache.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue