зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #16358 - Fix up the style sharing cache (from bholley:style_sharing_fixes); r=emilio
Reviewed in: https://bugzilla.mozilla.org/show_bug.cgi?id=1354895 https://bugzilla.mozilla.org/show_bug.cgi?id=1332525 Source-Repo: https://github.com/servo/servo Source-Revision: 7ba3f1e4f3064ec3054b2346062758375b8ab117 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : ea1494fe9e1c0720730f3999a0c11bd0b3434617
This commit is contained in:
Родитель
8cedccb89e
Коммит
206fa732a5
|
@ -2733,6 +2733,7 @@ dependencies = [
|
|||
"app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bindgen 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -25,6 +25,7 @@ testing = []
|
|||
app_units = "0.4"
|
||||
atomic_refcell = "0.1"
|
||||
bitflags = "0.7"
|
||||
bit-vec = "0.4.3"
|
||||
byteorder = "1.0"
|
||||
cfg-if = "0.1.0"
|
||||
cssparser = "0.12.1"
|
||||
|
|
|
@ -31,6 +31,11 @@ impl<K: PartialEq> LRUCache<K> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the cache.
|
||||
pub fn num_entries(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Touch a given position, and put it in the last item on the list.
|
||||
pub fn touch(&mut self, pos: usize) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
use animation::{Animation, PropertyAnimation};
|
||||
use app_units::Au;
|
||||
use bit_vec::BitVec;
|
||||
use bloom::StyleBloom;
|
||||
use data::ElementData;
|
||||
use dom::{OpaqueNode, TNode, TElement, SendElement};
|
||||
|
@ -111,6 +112,9 @@ pub struct CurrentElementInfo {
|
|||
element: OpaqueNode,
|
||||
/// Whether the element is being styled for the first time.
|
||||
is_initial_style: bool,
|
||||
/// Lazy cache of the result of matching the current element against the
|
||||
/// revalidation selectors.
|
||||
pub revalidation_match_results: Option<BitVec>,
|
||||
/// A Vec of possibly expired animations. Used only by Servo.
|
||||
#[allow(dead_code)]
|
||||
pub possibly_expired_animations: Vec<PropertyAnimation>,
|
||||
|
@ -317,6 +321,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
|
|||
self.current_element_info = Some(CurrentElementInfo {
|
||||
element: element.as_node().opaque(),
|
||||
is_initial_style: !data.has_styles(),
|
||||
revalidation_match_results: None,
|
||||
possibly_expired_animations: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
extern crate app_units;
|
||||
extern crate atomic_refcell;
|
||||
extern crate bit_vec;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[allow(unused_extern_crates)] extern crate byteorder;
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
use Atom;
|
||||
use animation::{self, Animation, PropertyAnimation};
|
||||
use atomic_refcell::AtomicRefMut;
|
||||
use bit_vec::BitVec;
|
||||
use cache::{LRUCache, LRUCacheMutIterator};
|
||||
use cascade_info::CascadeInfo;
|
||||
use context::{SequentialTask, SharedStyleContext, StyleContext};
|
||||
use context::{CurrentElementInfo, SequentialTask, SharedStyleContext, StyleContext};
|
||||
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
|
||||
use dom::{AnimationRules, SendElement, TElement, TNode};
|
||||
use font_metrics::FontMetricsProvider;
|
||||
|
@ -53,6 +54,8 @@ struct StyleSharingCandidate<E: TElement> {
|
|||
element: SendElement<E>,
|
||||
/// The cached class names.
|
||||
class_attributes: Option<Vec<Atom>>,
|
||||
/// The cached result of matching this entry against the revalidation selectors.
|
||||
revalidation_match_results: Option<BitVec>,
|
||||
}
|
||||
|
||||
impl<E: TElement> PartialEq<StyleSharingCandidate<E>> for StyleSharingCandidate<E> {
|
||||
|
@ -95,18 +98,18 @@ pub enum CacheMiss {
|
|||
Class,
|
||||
/// The presentation hints didn't match.
|
||||
PresHints,
|
||||
/// The element and the candidate didn't match the same set of
|
||||
/// sibling-affecting rules.
|
||||
SiblingRules,
|
||||
/// The element and the candidate didn't match the same set of style
|
||||
/// affecting attribute selectors.
|
||||
AttrRules,
|
||||
/// The element and the candidate didn't match the same set of revalidation
|
||||
/// selectors.
|
||||
Revalidation,
|
||||
}
|
||||
|
||||
fn element_matches_candidate<E: TElement>(element: &E,
|
||||
candidate: &mut StyleSharingCandidate<E>,
|
||||
candidate_element: &E,
|
||||
shared_context: &SharedStyleContext)
|
||||
shared: &SharedStyleContext,
|
||||
bloom: &BloomFilter,
|
||||
info: &mut CurrentElementInfo,
|
||||
tasks: &mut Vec<SequentialTask<E>>)
|
||||
-> Result<ComputedStyle, CacheMiss> {
|
||||
macro_rules! miss {
|
||||
($miss: ident) => {
|
||||
|
@ -154,16 +157,9 @@ fn element_matches_candidate<E: TElement>(element: &E,
|
|||
miss!(PresHints)
|
||||
}
|
||||
|
||||
if !match_same_sibling_affecting_rules(element,
|
||||
candidate_element,
|
||||
shared_context) {
|
||||
miss!(SiblingRules)
|
||||
}
|
||||
|
||||
if !match_same_style_affecting_attributes_rules(element,
|
||||
candidate_element,
|
||||
shared_context) {
|
||||
miss!(AttrRules)
|
||||
if !revalidate(element, candidate, candidate_element,
|
||||
shared, bloom, info, tasks) {
|
||||
miss!(Revalidation)
|
||||
}
|
||||
|
||||
let data = candidate_element.borrow_data().unwrap();
|
||||
|
@ -203,19 +199,60 @@ fn have_same_class<E: TElement>(element: &E,
|
|||
element_class_attributes == *candidate.class_attributes.as_ref().unwrap()
|
||||
}
|
||||
|
||||
// TODO: These re-match the candidate every time, which is suboptimal.
|
||||
#[inline]
|
||||
fn match_same_style_affecting_attributes_rules<E: TElement>(element: &E,
|
||||
candidate: &E,
|
||||
ctx: &SharedStyleContext) -> bool {
|
||||
ctx.stylist.match_same_style_affecting_attributes_rules(element, candidate)
|
||||
}
|
||||
fn revalidate<E: TElement>(element: &E,
|
||||
candidate: &mut StyleSharingCandidate<E>,
|
||||
candidate_element: &E,
|
||||
shared: &SharedStyleContext,
|
||||
bloom: &BloomFilter,
|
||||
info: &mut CurrentElementInfo,
|
||||
tasks: &mut Vec<SequentialTask<E>>)
|
||||
-> bool {
|
||||
// NB: We could avoid matching ancestor selectors entirely (rather than
|
||||
// just depending on the bloom filter), at the expense of some complexity.
|
||||
// Gecko bug 1354965 tracks this.
|
||||
//
|
||||
// We could also be even more careful about only matching the minimal number
|
||||
// of revalidation selectors until we find a mismatch. Gecko bug 1355668
|
||||
// tracks this.
|
||||
//
|
||||
// These potential optimizations may not be worth the complexity.
|
||||
let stylist = &shared.stylist;
|
||||
|
||||
#[inline]
|
||||
fn match_same_sibling_affecting_rules<E: TElement>(element: &E,
|
||||
candidate: &E,
|
||||
ctx: &SharedStyleContext) -> bool {
|
||||
ctx.stylist.match_same_sibling_affecting_rules(element, candidate)
|
||||
if info.revalidation_match_results.is_none() {
|
||||
// It's important to set the selector flags. Otherwise, if we succeed in
|
||||
// sharing the style, we may not set the slow selector flags for the
|
||||
// right elements (which may not necessarily be |element|), causing missed
|
||||
// restyles after future DOM mutations.
|
||||
//
|
||||
// Gecko's test_bug534804.html exercises this. A minimal testcase is:
|
||||
// <style> #e:empty + span { ... } </style>
|
||||
// <span id="e">
|
||||
// <span></span>
|
||||
// </span>
|
||||
// <span></span>
|
||||
//
|
||||
// The style sharing cache will get a hit for the second span. When the
|
||||
// child span is subsequently removed from the DOM, missing selector
|
||||
// flags would cause us to miss the restyle on the second span.
|
||||
let mut set_selector_flags = |el: &E, flags: ElementSelectorFlags| {
|
||||
element.apply_selector_flags(tasks, el, flags);
|
||||
};
|
||||
info.revalidation_match_results =
|
||||
Some(stylist.match_revalidation_selectors(element, bloom,
|
||||
&mut set_selector_flags));
|
||||
}
|
||||
|
||||
if candidate.revalidation_match_results.is_none() {
|
||||
candidate.revalidation_match_results =
|
||||
Some(stylist.match_revalidation_selectors(candidate_element, bloom,
|
||||
&mut |_, _| {}));
|
||||
}
|
||||
|
||||
let for_element = info.revalidation_match_results.as_ref().unwrap();
|
||||
let for_candidate = candidate.revalidation_match_results.as_ref().unwrap();
|
||||
debug_assert!(for_element.len() == for_candidate.len());
|
||||
for_element == for_candidate
|
||||
}
|
||||
|
||||
static STYLE_SHARING_CANDIDATE_CACHE_SIZE: usize = 8;
|
||||
|
@ -228,6 +265,11 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the number of entries in the cache.
|
||||
pub fn num_entries(&self) -> usize {
|
||||
self.cache.num_entries()
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> LRUCacheMutIterator<StyleSharingCandidate<E>> {
|
||||
self.cache.iter_mut()
|
||||
}
|
||||
|
@ -238,7 +280,8 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
pub fn insert_if_possible(&mut self,
|
||||
element: &E,
|
||||
style: &Arc<ComputedValues>,
|
||||
relations: StyleRelations) {
|
||||
relations: StyleRelations,
|
||||
revalidation_match_results: Option<BitVec>) {
|
||||
let parent = match element.parent_element() {
|
||||
Some(element) => element,
|
||||
None => {
|
||||
|
@ -255,7 +298,7 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
}
|
||||
|
||||
let box_style = style.get_box();
|
||||
if box_style.transition_property_count() > 0 {
|
||||
if box_style.specifies_transitions() {
|
||||
debug!("Failing to insert to the cache: transitions");
|
||||
return;
|
||||
}
|
||||
|
@ -271,6 +314,7 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
self.cache.insert(StyleSharingCandidate {
|
||||
element: unsafe { SendElement::new(*element) },
|
||||
class_attributes: None,
|
||||
revalidation_match_results: revalidation_match_results,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -651,11 +695,15 @@ trait PrivateMatchMethods: TElement {
|
|||
}
|
||||
|
||||
fn share_style_with_candidate_if_possible(&self,
|
||||
shared_context: &SharedStyleContext,
|
||||
candidate: &mut StyleSharingCandidate<Self>)
|
||||
candidate: &mut StyleSharingCandidate<Self>,
|
||||
shared: &SharedStyleContext,
|
||||
bloom: &BloomFilter,
|
||||
info: &mut CurrentElementInfo,
|
||||
tasks: &mut Vec<SequentialTask<Self>>)
|
||||
-> Result<ComputedStyle, CacheMiss> {
|
||||
let candidate_element = *candidate.element;
|
||||
element_matches_candidate(self, candidate, &candidate_element, shared_context)
|
||||
element_matches_candidate(self, candidate, &candidate_element,
|
||||
shared, bloom, info, tasks)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,11 +766,23 @@ pub trait MatchMethods : TElement {
|
|||
|
||||
// If the style is shareable, add it to the LRU cache.
|
||||
if sharing == StyleSharingBehavior::Allow && relations_are_shareable(&primary_relations) {
|
||||
// If we previously tried to match this element against the cache,
|
||||
// the revalidation match results will already be cached. Otherwise
|
||||
// we'll have None, and compute them later on-demand.
|
||||
//
|
||||
// If we do have the results, grab them here to satisfy the borrow
|
||||
// checker.
|
||||
let revalidation_match_results = context.thread_local
|
||||
.current_element_info
|
||||
.as_mut().unwrap()
|
||||
.revalidation_match_results
|
||||
.take();
|
||||
context.thread_local
|
||||
.style_sharing_candidate_cache
|
||||
.insert_if_possible(self,
|
||||
data.styles().primary.values(),
|
||||
primary_relations);
|
||||
primary_relations,
|
||||
revalidation_match_results);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -970,32 +1030,42 @@ pub trait MatchMethods : TElement {
|
|||
/// live nodes in it, and we have no way to guarantee that at the type
|
||||
/// system level yet.
|
||||
unsafe fn share_style_if_possible(&self,
|
||||
style_sharing_candidate_cache:
|
||||
&mut StyleSharingCandidateCache<Self>,
|
||||
shared_context: &SharedStyleContext,
|
||||
context: &mut StyleContext<Self>,
|
||||
data: &mut AtomicRefMut<ElementData>)
|
||||
-> StyleSharingResult {
|
||||
if is_share_style_cache_disabled() {
|
||||
debug!("{:?} Cannot share style: style sharing cache disabled", self);
|
||||
return StyleSharingResult::CannotShare
|
||||
}
|
||||
|
||||
if self.parent_element().is_none() {
|
||||
debug!("{:?} Cannot share style: element has style attribute", self);
|
||||
return StyleSharingResult::CannotShare
|
||||
}
|
||||
|
||||
if self.style_attribute().is_some() {
|
||||
debug!("{:?} Cannot share style: element has style attribute", self);
|
||||
return StyleSharingResult::CannotShare
|
||||
}
|
||||
|
||||
if self.has_attr(&ns!(), &local_name!("id")) {
|
||||
debug!("{:?} Cannot share style: element has id", self);
|
||||
return StyleSharingResult::CannotShare
|
||||
}
|
||||
|
||||
let cache = &mut context.thread_local.style_sharing_candidate_cache;
|
||||
let current_element_info =
|
||||
&mut context.thread_local.current_element_info.as_mut().unwrap();
|
||||
let bloom = context.thread_local.bloom_filter.filter();
|
||||
let tasks = &mut context.thread_local.tasks;
|
||||
let mut should_clear_cache = false;
|
||||
for (i, candidate) in style_sharing_candidate_cache.iter_mut().enumerate() {
|
||||
for (i, candidate) in cache.iter_mut().enumerate() {
|
||||
let sharing_result =
|
||||
self.share_style_with_candidate_if_possible(shared_context,
|
||||
candidate);
|
||||
self.share_style_with_candidate_if_possible(candidate,
|
||||
&context.shared,
|
||||
bloom,
|
||||
current_element_info,
|
||||
tasks);
|
||||
match sharing_result {
|
||||
Ok(shared_style) => {
|
||||
// Yay, cache hit. Share the style.
|
||||
|
@ -1005,7 +1075,8 @@ pub trait MatchMethods : TElement {
|
|||
let old_values = data.get_styles_mut()
|
||||
.and_then(|s| s.primary.values.take());
|
||||
if let Some(old) = old_values {
|
||||
self.accumulate_damage(shared_context, data.restyle_mut(), &old,
|
||||
self.accumulate_damage(&context.shared,
|
||||
data.restyle_mut(), &old,
|
||||
shared_style.values(), None);
|
||||
}
|
||||
|
||||
|
@ -1033,15 +1104,17 @@ pub trait MatchMethods : TElement {
|
|||
// Too expensive failure, give up, we don't want another
|
||||
// one of these.
|
||||
CacheMiss::PresHints |
|
||||
CacheMiss::SiblingRules |
|
||||
CacheMiss::AttrRules => break,
|
||||
CacheMiss::Revalidation => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("{:?} Cannot share style: {} cache entries", self, cache.num_entries());
|
||||
|
||||
if should_clear_cache {
|
||||
style_sharing_candidate_cache.clear();
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
StyleSharingResult::CannotShare
|
||||
|
|
|
@ -1890,6 +1890,19 @@ fn static_assert() {
|
|||
self.gecko.mTransitions[0].mProperty = nsCSSPropertyID_eCSSPropertyExtra_no_properties;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether there are any transitions specified.
|
||||
pub fn specifies_transitions(&self) -> bool {
|
||||
use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties;
|
||||
if self.gecko.mTransitionPropertyCount == 1 &&
|
||||
self.gecko.mTransitions[0].mProperty == eCSSPropertyExtra_all_properties &&
|
||||
self.gecko.mTransitions[0].mDuration.max(0.0) + self.gecko.mTransitions[0].mDelay <= 0.0f32 {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.gecko.mTransitionPropertyCount > 0
|
||||
}
|
||||
|
||||
pub fn transition_property_at(&self, index: usize)
|
||||
-> longhands::transition_property::computed_value::SingleComputedValue {
|
||||
self.gecko.mTransitions[index].mProperty.into()
|
||||
|
|
|
@ -1509,6 +1509,12 @@ pub mod style_structs {
|
|||
pub fn specifies_animations(&self) -> bool {
|
||||
self.animation_name_iter().any(|name| name.0 != atom!(""))
|
||||
}
|
||||
|
||||
/// Returns whether there are any transitions specified.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn specifies_transitions(&self) -> bool {
|
||||
self.transition_property_count() > 0
|
||||
}
|
||||
% endif
|
||||
}
|
||||
|
||||
|
|
|
@ -400,8 +400,15 @@ fn is_attr_selector(sel: &SimpleSelector<SelectorImpl>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_sibling_affecting_selector(sel: &SimpleSelector<SelectorImpl>) -> bool {
|
||||
/// Whether a selector containing this simple selector needs to be explicitly
|
||||
/// matched against both the style sharing cache entry and the candidate.
|
||||
///
|
||||
///
|
||||
/// We use this for selectors that can have different matching behavior between
|
||||
/// siblings that are otherwise identical as far as the cache is concerned.
|
||||
fn needs_cache_revalidation(sel: &SimpleSelector<SelectorImpl>) -> bool {
|
||||
match *sel {
|
||||
SimpleSelector::Empty |
|
||||
SimpleSelector::FirstChild |
|
||||
SimpleSelector::LastChild |
|
||||
SimpleSelector::OnlyChild |
|
||||
|
@ -412,6 +419,7 @@ fn is_sibling_affecting_selector(sel: &SimpleSelector<SelectorImpl>) -> bool {
|
|||
SimpleSelector::FirstOfType |
|
||||
SimpleSelector::LastOfType |
|
||||
SimpleSelector::OnlyOfType => true,
|
||||
SimpleSelector::NonTSPseudoClass(ref p) => p.state_flag().is_empty(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -481,8 +489,7 @@ struct Dependency {
|
|||
/// selector.
|
||||
pub struct SelectorDependencyVisitor<'a> {
|
||||
dependency_set: &'a mut DependencySet,
|
||||
affects_siblings: bool,
|
||||
affected_by_attribute: bool,
|
||||
needs_cache_revalidation: bool,
|
||||
}
|
||||
|
||||
impl<'a> SelectorDependencyVisitor<'a> {
|
||||
|
@ -490,20 +497,14 @@ impl<'a> SelectorDependencyVisitor<'a> {
|
|||
pub fn new(dependency_set: &'a mut DependencySet) -> Self {
|
||||
SelectorDependencyVisitor {
|
||||
dependency_set: dependency_set,
|
||||
affects_siblings: false,
|
||||
affected_by_attribute: false,
|
||||
needs_cache_revalidation: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether this visitor has known of a sibling-dependent selector.
|
||||
pub fn affects_siblings(&self) -> bool {
|
||||
self.affects_siblings
|
||||
}
|
||||
|
||||
/// Returns whether this visitor has known of a attribute-dependent
|
||||
/// selector.
|
||||
pub fn affected_by_attribute(&self) -> bool {
|
||||
self.affected_by_attribute
|
||||
/// Returns whether this visitor has encountered a simple selector that needs
|
||||
/// cache revalidation.
|
||||
pub fn needs_cache_revalidation(&self) -> bool {
|
||||
self.needs_cache_revalidation
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -518,8 +519,8 @@ impl<'a> SelectorVisitor for SelectorDependencyVisitor<'a> {
|
|||
let mut sensitivities = Sensitivities::new();
|
||||
for s in &selector.compound_selector {
|
||||
sensitivities.states.insert(selector_to_state(s));
|
||||
if !self.affects_siblings {
|
||||
self.affects_siblings = is_sibling_affecting_selector(s);
|
||||
if !self.needs_cache_revalidation {
|
||||
self.needs_cache_revalidation = needs_cache_revalidation(s);
|
||||
}
|
||||
if !sensitivities.attrs {
|
||||
sensitivities.attrs = is_attr_selector(s);
|
||||
|
@ -528,8 +529,8 @@ impl<'a> SelectorVisitor for SelectorDependencyVisitor<'a> {
|
|||
|
||||
let hint = combinator_to_restyle_hint(combinator);
|
||||
|
||||
self.affected_by_attribute |= sensitivities.attrs;
|
||||
self.affects_siblings |= hint.intersects(RESTYLE_LATER_SIBLINGS);
|
||||
self.needs_cache_revalidation |= sensitivities.attrs;
|
||||
self.needs_cache_revalidation |= hint.intersects(RESTYLE_LATER_SIBLINGS);
|
||||
|
||||
if !sensitivities.is_empty() {
|
||||
self.dependency_set.add_dependency(Dependency {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#![deny(missing_docs)]
|
||||
|
||||
use {Atom, LocalName};
|
||||
use bit_vec::BitVec;
|
||||
use data::ComputedStyle;
|
||||
use dom::{AnimationRules, PresentationalHintsSynthetizer, TElement};
|
||||
use error_reporting::StdoutErrorReporter;
|
||||
|
@ -111,14 +112,11 @@ pub struct Stylist {
|
|||
/// Selector dependencies used to compute restyle hints.
|
||||
state_deps: DependencySet,
|
||||
|
||||
/// Selectors in the page affecting siblings
|
||||
/// Selectors that require explicit cache revalidation (i.e. which depend
|
||||
/// on state that is not otherwise visible to the cache, like attributes or
|
||||
/// tree-structural state like child index and pseudos).
|
||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||
sibling_affecting_selectors: Vec<Selector<SelectorImpl>>,
|
||||
|
||||
/// Selectors in the page matching elements with non-common style-affecting
|
||||
/// attributes.
|
||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
|
||||
style_affecting_attributes_selectors: Vec<Selector<SelectorImpl>>,
|
||||
selectors_for_cache_revalidation: Vec<Selector<SelectorImpl>>,
|
||||
}
|
||||
|
||||
/// This struct holds data which user of Stylist may want to extract
|
||||
|
@ -170,8 +168,7 @@ impl Stylist {
|
|||
rule_tree: RuleTree::new(),
|
||||
state_deps: DependencySet::new(),
|
||||
|
||||
sibling_affecting_selectors: vec![],
|
||||
style_affecting_attributes_selectors: vec![]
|
||||
selectors_for_cache_revalidation: vec![],
|
||||
};
|
||||
|
||||
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||
|
@ -225,8 +222,7 @@ impl Stylist {
|
|||
self.state_deps.clear();
|
||||
self.animations.clear();
|
||||
|
||||
self.sibling_affecting_selectors.clear();
|
||||
self.style_affecting_attributes_selectors.clear();
|
||||
self.selectors_for_cache_revalidation.clear();
|
||||
|
||||
extra_data.clear_font_faces();
|
||||
|
||||
|
@ -246,10 +242,8 @@ impl Stylist {
|
|||
}
|
||||
|
||||
debug!("Stylist stats:");
|
||||
debug!(" - Got {} sibling-affecting selectors",
|
||||
self.sibling_affecting_selectors.len());
|
||||
debug!(" - Got {} non-common-style-attribute-affecting selectors",
|
||||
self.style_affecting_attributes_selectors.len());
|
||||
debug!(" - Got {} selectors for cache revalidation",
|
||||
self.selectors_for_cache_revalidation.len());
|
||||
debug!(" - Got {} deps for style-hint calculation",
|
||||
self.state_deps.len());
|
||||
|
||||
|
@ -304,12 +298,8 @@ impl Stylist {
|
|||
SelectorDependencyVisitor::new(&mut self.state_deps);
|
||||
selector.visit(&mut visitor);
|
||||
|
||||
if visitor.affects_siblings() {
|
||||
self.sibling_affecting_selectors.push(selector.clone());
|
||||
}
|
||||
|
||||
if visitor.affected_by_attribute() {
|
||||
self.style_affecting_attributes_selectors.push(selector.clone());
|
||||
if visitor.needs_cache_revalidation() {
|
||||
self.selectors_for_cache_revalidation.push(selector.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -770,83 +760,38 @@ impl Stylist {
|
|||
&self.animations
|
||||
}
|
||||
|
||||
/// Whether two elements match the same not-common style-affecting attribute
|
||||
/// rules.
|
||||
///
|
||||
/// This is used to test elements and candidates in the style-sharing
|
||||
/// candidate cache.
|
||||
pub fn match_same_style_affecting_attributes_rules<E>(&self,
|
||||
element: &E,
|
||||
candidate: &E) -> bool
|
||||
where E: TElement,
|
||||
{
|
||||
use selectors::matching::StyleRelations;
|
||||
use selectors::matching::matches_complex_selector;
|
||||
// TODO(emilio): we can probably do better, the candidate should already
|
||||
// know what rules it matches. Also, we should only match until we find
|
||||
// a descendant combinator, the rest should be ok, since the parent is
|
||||
// the same.
|
||||
//
|
||||
// TODO(emilio): Use the bloom filter, since they contain the element's
|
||||
// ancestor chain and it's correct for the candidate too.
|
||||
for ref selector in self.style_affecting_attributes_selectors.iter() {
|
||||
let element_matches =
|
||||
matches_complex_selector(&selector.complex_selector, element,
|
||||
None, &mut StyleRelations::empty(),
|
||||
&mut |_, _| {});
|
||||
let candidate_matches =
|
||||
matches_complex_selector(&selector.complex_selector, candidate,
|
||||
None, &mut StyleRelations::empty(),
|
||||
&mut |_, _| {});
|
||||
|
||||
if element_matches != candidate_matches {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns the rule root node.
|
||||
#[inline]
|
||||
pub fn rule_tree_root(&self) -> StrongRuleNode {
|
||||
self.rule_tree.root()
|
||||
}
|
||||
|
||||
/// Returns whether two elements match the same sibling-affecting rules.
|
||||
///
|
||||
/// This is also for the style sharing candidate cache.
|
||||
pub fn match_same_sibling_affecting_rules<E>(&self,
|
||||
element: &E,
|
||||
candidate: &E) -> bool
|
||||
/// Computes the match results of a given element against the set of
|
||||
/// revalidation selectors.
|
||||
pub fn match_revalidation_selectors<E, F>(&self,
|
||||
element: &E,
|
||||
bloom: &BloomFilter,
|
||||
flags_setter: &mut F)
|
||||
-> BitVec
|
||||
where E: TElement,
|
||||
F: FnMut(&E, ElementSelectorFlags)
|
||||
{
|
||||
use selectors::matching::StyleRelations;
|
||||
use selectors::matching::matches_complex_selector;
|
||||
// TODO(emilio): we can probably do better, the candidate should already
|
||||
// know what rules it matches.
|
||||
//
|
||||
// TODO(emilio): Use the bloom filter, since they contain the element's
|
||||
// ancestor chain and it's correct for the candidate too.
|
||||
for ref selector in self.sibling_affecting_selectors.iter() {
|
||||
let element_matches =
|
||||
matches_complex_selector(&selector.complex_selector, element,
|
||||
None, &mut StyleRelations::empty(),
|
||||
&mut |_, _| {});
|
||||
|
||||
let candidate_matches =
|
||||
matches_complex_selector(&selector.complex_selector, candidate,
|
||||
None, &mut StyleRelations::empty(),
|
||||
&mut |_, _| {});
|
||||
let len = self.selectors_for_cache_revalidation.len();
|
||||
let mut results = BitVec::from_elem(len, false);
|
||||
|
||||
if element_matches != candidate_matches {
|
||||
debug!("match_same_sibling_affecting_rules: Failure due to {:?}",
|
||||
selector.complex_selector);
|
||||
return false;
|
||||
}
|
||||
for (i, ref selector) in self.selectors_for_cache_revalidation
|
||||
.iter().enumerate() {
|
||||
results.set(i, matches_complex_selector(&selector.complex_selector,
|
||||
element,
|
||||
Some(bloom),
|
||||
&mut StyleRelations::empty(),
|
||||
flags_setter));
|
||||
}
|
||||
|
||||
true
|
||||
results
|
||||
}
|
||||
|
||||
/// Given an element, and a snapshot that represents a previous state of the
|
||||
|
|
|
@ -680,15 +680,13 @@ fn compute_style<E, D>(_traversal: &D,
|
|||
use matching::StyleSharingResult::*;
|
||||
|
||||
context.thread_local.statistics.elements_styled += 1;
|
||||
let shared_context = context.shared;
|
||||
let kind = data.restyle_kind();
|
||||
|
||||
// First, try the style sharing cache. If we get a match we can skip the rest
|
||||
// of the work.
|
||||
if let MatchAndCascade = kind {
|
||||
let sharing_result = unsafe {
|
||||
let cache = &mut context.thread_local.style_sharing_candidate_cache;
|
||||
element.share_style_if_possible(cache, shared_context, &mut data)
|
||||
element.share_style_if_possible(context, &mut data)
|
||||
};
|
||||
if let StyleWasShared(index) = sharing_result {
|
||||
context.thread_local.statistics.styles_shared += 1;
|
||||
|
|
Загрузка…
Ссылка в новой задаче