mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +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 rule_tree::StrongRuleNode;
|
||||
use selector_impl::{PseudoElement, RestyleDamage, TheSelectorImpl};
|
||||
use selector_matching::{ApplicableDeclarationBlock, Stylist};
|
||||
use selector_matching::ApplicableDeclarationBlock;
|
||||
use selectors::MatchAttr;
|
||||
use selectors::bloom::BloomFilter;
|
||||
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
|
||||
}
|
||||
|
||||
pub struct ApplicableDeclarations {
|
||||
pub normal: Vec<ApplicableDeclarationBlock>,
|
||||
pub per_pseudo: HashMap<PseudoElement,
|
||||
Vec<ApplicableDeclarationBlock>,
|
||||
BuildHasherDefault<::fnv::FnvHasher>>,
|
||||
|
||||
/// Whether the `normal` declarations are shareable with other nodes.
|
||||
pub normal_shareable: bool,
|
||||
type PseudoRuleNodes = HashMap<PseudoElement, StrongRuleNode,
|
||||
BuildHasherDefault<::fnv::FnvHasher>>;
|
||||
pub struct MatchResults {
|
||||
pub primary: StrongRuleNode,
|
||||
pub relations: StyleRelations,
|
||||
pub per_pseudo: PseudoRuleNodes,
|
||||
}
|
||||
|
||||
impl ApplicableDeclarations {
|
||||
pub fn new() -> Self {
|
||||
let mut applicable_declarations = ApplicableDeclarations {
|
||||
normal: Vec::with_capacity(16),
|
||||
per_pseudo: HashMap::with_hasher(Default::default()),
|
||||
normal_shareable: false,
|
||||
};
|
||||
|
||||
TheSelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||
applicable_declarations.per_pseudo.insert(pseudo, vec![]);
|
||||
});
|
||||
|
||||
applicable_declarations
|
||||
impl MatchResults {
|
||||
/// Returns true if the primary rule node is shareable with other nodes.
|
||||
pub fn primary_is_shareable(&self) -> bool {
|
||||
use traversal::relations_are_shareable;
|
||||
relations_are_shareable(&self.relations)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,17 +391,12 @@ trait PrivateMatchMethods: TElement {
|
|||
context: &Ctx,
|
||||
parent_style: Option<&Arc<ComputedValues>>,
|
||||
old_style: Option<&Arc<ComputedValues>>,
|
||||
applicable_declarations: &mut Vec<ApplicableDeclarationBlock>,
|
||||
rule_node: &StrongRuleNode,
|
||||
booleans: CascadeBooleans)
|
||||
-> (Arc<ComputedValues>, StrongRuleNode)
|
||||
-> Arc<ComputedValues>
|
||||
where Ctx: StyleContext<'a>
|
||||
{
|
||||
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_flags = CascadeFlags::empty();
|
||||
if booleans.shareable {
|
||||
|
@ -421,7 +406,7 @@ trait PrivateMatchMethods: TElement {
|
|||
let this_style = match parent_style {
|
||||
Some(ref parent_style) => {
|
||||
cascade(shared_context.viewport_size,
|
||||
&rule_node,
|
||||
rule_node,
|
||||
Some(&***parent_style),
|
||||
Some(&mut cascade_info),
|
||||
shared_context.error_reporter.clone(),
|
||||
|
@ -429,7 +414,7 @@ trait PrivateMatchMethods: TElement {
|
|||
}
|
||||
None => {
|
||||
cascade(shared_context.viewport_size,
|
||||
&rule_node,
|
||||
rule_node,
|
||||
None,
|
||||
Some(&mut cascade_info),
|
||||
shared_context.error_reporter.clone(),
|
||||
|
@ -461,7 +446,7 @@ trait PrivateMatchMethods: TElement {
|
|||
}
|
||||
}
|
||||
|
||||
(this_style, rule_node)
|
||||
this_style
|
||||
}
|
||||
|
||||
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 {}
|
||||
|
||||
pub trait MatchMethods : TElement {
|
||||
fn match_element(&self,
|
||||
stylist: &Stylist,
|
||||
parent_bf: Option<&BloomFilter>,
|
||||
mut applicable_declarations: &mut ApplicableDeclarations)
|
||||
-> StyleRelations {
|
||||
use traversal::relations_are_shareable;
|
||||
|
||||
fn match_element<'a, Ctx>(&self, context: &Ctx, parent_bf: Option<&BloomFilter>)
|
||||
-> MatchResults
|
||||
where Ctx: StyleContext<'a>
|
||||
{
|
||||
let mut applicable_declarations: Vec<ApplicableDeclarationBlock> = Vec::with_capacity(16);
|
||||
let stylist = &context.shared_context().stylist;
|
||||
let style_attribute = self.style_attribute();
|
||||
|
||||
let mut relations =
|
||||
// Compute the primary rule node.
|
||||
let mut primary_relations =
|
||||
stylist.push_applicable_declarations(self,
|
||||
parent_bf,
|
||||
style_attribute,
|
||||
None,
|
||||
&mut applicable_declarations.normal,
|
||||
&mut applicable_declarations,
|
||||
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| {
|
||||
stylist.push_applicable_declarations(self,
|
||||
parent_bf,
|
||||
None,
|
||||
debug_assert!(applicable_declarations.is_empty());
|
||||
stylist.push_applicable_declarations(self, parent_bf, None,
|
||||
Some(&pseudo.clone()),
|
||||
applicable_declarations.per_pseudo.entry(pseudo).or_insert(vec![]),
|
||||
&mut applicable_declarations,
|
||||
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 =
|
||||
applicable_declarations.per_pseudo.values().any(|v| !v.is_empty());
|
||||
|
||||
if has_pseudos {
|
||||
relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
|
||||
// If we have any pseudo elements, indicate so in the primary StyleRelations.
|
||||
if !per_pseudo.is_empty() {
|
||||
primary_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
|
||||
|
@ -712,7 +715,9 @@ pub trait MatchMethods : TElement {
|
|||
context: &Ctx,
|
||||
mut data: AtomicRefMut<ElementData>,
|
||||
parent: Option<Self>,
|
||||
mut applicable_declarations: ApplicableDeclarations)
|
||||
primary_rule_node: StrongRuleNode,
|
||||
pseudo_rule_nodes: PseudoRuleNodes,
|
||||
primary_is_shareable: bool)
|
||||
where Ctx: StyleContext<'a>
|
||||
{
|
||||
// Get our parent's style.
|
||||
|
@ -722,8 +727,6 @@ pub trait MatchMethods : TElement {
|
|||
let mut new_styles;
|
||||
|
||||
let damage = {
|
||||
let shareable = applicable_declarations.normal_shareable;
|
||||
|
||||
let (old_primary, old_pseudos) = match data.previous_styles_mut() {
|
||||
None => (None, None),
|
||||
Some(previous) => {
|
||||
|
@ -735,17 +738,17 @@ pub trait MatchMethods : TElement {
|
|||
}
|
||||
};
|
||||
|
||||
let (new_style, rule_node) =
|
||||
let new_style =
|
||||
self.cascade_node_pseudo_element(context,
|
||||
parent_style,
|
||||
old_primary,
|
||||
&mut applicable_declarations.normal,
|
||||
&primary_rule_node,
|
||||
CascadeBooleans {
|
||||
shareable: shareable,
|
||||
shareable: primary_is_shareable,
|
||||
animate: true,
|
||||
});
|
||||
|
||||
new_styles = ElementStyles::new(new_style, rule_node);
|
||||
new_styles = ElementStyles::new(new_style, primary_rule_node);
|
||||
|
||||
let damage =
|
||||
self.compute_damage_and_cascade_pseudos(old_primary,
|
||||
|
@ -753,7 +756,7 @@ pub trait MatchMethods : TElement {
|
|||
&new_styles.primary,
|
||||
&mut new_styles.pseudos,
|
||||
context,
|
||||
&mut applicable_declarations);
|
||||
pseudo_rule_nodes);
|
||||
|
||||
self.as_node().set_can_be_fragmented(parent.map_or(false, |p| {
|
||||
p.as_node().can_be_fragmented() ||
|
||||
|
@ -775,7 +778,7 @@ pub trait MatchMethods : TElement {
|
|||
new_primary: &Arc<ComputedValues>,
|
||||
new_pseudos: &mut PseudoStyles,
|
||||
context: &Ctx,
|
||||
applicable_declarations: &mut ApplicableDeclarations)
|
||||
mut pseudo_rule_nodes: PseudoRuleNodes)
|
||||
-> RestyleDamage
|
||||
where Ctx: StyleContext<'a>
|
||||
{
|
||||
|
@ -817,17 +820,15 @@ pub trait MatchMethods : TElement {
|
|||
|
||||
debug_assert!(new_pseudos.is_empty());
|
||||
<Self as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||
let mut applicable_declarations_for_this_pseudo =
|
||||
applicable_declarations.per_pseudo.get_mut(&pseudo).unwrap();
|
||||
|
||||
let has_declarations =
|
||||
!applicable_declarations_for_this_pseudo.is_empty();
|
||||
let maybe_rule_node = pseudo_rule_nodes.remove(&pseudo);
|
||||
|
||||
// Grab the old pseudo style for analysis.
|
||||
let mut maybe_old_pseudo_style_and_rule_node =
|
||||
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.
|
||||
let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
|
||||
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),
|
||||
maybe_old_pseudo_style_and_rule_node.as_ref().map(|s| &s.0),
|
||||
&mut applicable_declarations_for_this_pseudo,
|
||||
&new_rule_node,
|
||||
CascadeBooleans {
|
||||
shareable: false,
|
||||
animate: animate,
|
||||
|
|
|
@ -8,7 +8,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
|||
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||
use data::ElementData;
|
||||
use dom::{OpaqueNode, StylingMode, TElement, TNode, UnsafeNode};
|
||||
use matching::{ApplicableDeclarations, MatchMethods, StyleSharingResult};
|
||||
use matching::{MatchMethods, StyleSharingResult};
|
||||
use selectors::bloom::BloomFilter;
|
||||
use selectors::matching::StyleRelations;
|
||||
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
|
||||
// probably not necessary since we're likely to be matching only a few
|
||||
// nodes, at best.
|
||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
||||
let data = prepare_for_styling(element, element.get_data().unwrap());
|
||||
let stylist = &context.shared_context().stylist;
|
||||
|
||||
element.match_element(&**stylist,
|
||||
None,
|
||||
&mut applicable_declarations);
|
||||
|
||||
let match_results = element.match_element(context, None);
|
||||
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.
|
||||
match sharing_result {
|
||||
StyleSharingResult::CannotShare => {
|
||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
||||
|
||||
let relations;
|
||||
let match_results;
|
||||
let shareable_element = {
|
||||
if opts::get().style_sharing_stats {
|
||||
STYLE_SHARING_CACHE_MISSES.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// Perform the CSS selector matching.
|
||||
let stylist = &context.shared_context().stylist;
|
||||
|
||||
relations = element.match_element(&**stylist,
|
||||
Some(&*bf),
|
||||
&mut applicable_declarations);
|
||||
|
||||
debug!("Result of selector matching: {:?}", relations);
|
||||
|
||||
if relations_are_shareable(&relations) {
|
||||
match_results = element.match_element(context, Some(&*bf));
|
||||
if match_results.primary_is_shareable() {
|
||||
Some(element)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let relations = match_results.relations;
|
||||
|
||||
// Perform the CSS cascade.
|
||||
unsafe {
|
||||
let shareable = match_results.primary_is_shareable();
|
||||
element.cascade_node(context, data, element.parent_element(),
|
||||
applicable_declarations);
|
||||
match_results.primary,
|
||||
match_results.per_pseudo,
|
||||
shareable);
|
||||
}
|
||||
|
||||
// Add ourselves to the LRU cache.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue