mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
Separate rule node computation from cascade.
This commit is contained in:
parent
75d35241db
commit
5741b8150a
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