mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Auto merge of #18017 - heycam:split-cascade, r=emilio
style: Move all origin-specific cascade data to PerOriginCascadeData <!-- Please describe your changes on the following line: --> This is a preliminary refactoring in preparation for only rebuilding cascade data for origins that have a change. https://bugzilla.mozilla.org/show_bug.cgi?id=1382925 --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- 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/18017) <!-- Reviewable:end -->
This commit is contained in:
commit
ca14848711
7 changed files with 345 additions and 197 deletions
|
@ -2,9 +2,11 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
//! Simple counting bloom filters.
|
//! Counting and non-counting Bloom filters tuned for use as ancestor filters
|
||||||
|
//! for selector matching.
|
||||||
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
|
use std::fmt::{self, Debug};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
// The top 8 bits of the 32-bit hash value are not used by the bloom filter.
|
// The top 8 bits of the 32-bit hash value are not used by the bloom filter.
|
||||||
|
@ -15,9 +17,17 @@ const KEY_SIZE: usize = 12;
|
||||||
const ARRAY_SIZE: usize = 1 << KEY_SIZE;
|
const ARRAY_SIZE: usize = 1 << KEY_SIZE;
|
||||||
const KEY_MASK: u32 = (1 << KEY_SIZE) - 1;
|
const KEY_MASK: u32 = (1 << KEY_SIZE) - 1;
|
||||||
|
|
||||||
/// A counting Bloom filter with 8-bit counters. For now we assume
|
/// A counting Bloom filter with 8-bit counters.
|
||||||
/// that having two hash functions is enough, but we may revisit that
|
pub type BloomFilter = CountingBloomFilter<BloomStorageU8>;
|
||||||
/// decision later.
|
|
||||||
|
/// A non-counting Bloom filter.
|
||||||
|
///
|
||||||
|
/// Effectively a counting Bloom filter with 1-bit counters.
|
||||||
|
pub type NonCountingBloomFilter = CountingBloomFilter<BloomStorageBool>;
|
||||||
|
|
||||||
|
/// A counting Bloom filter with parameterized storage to handle
|
||||||
|
/// counters of different sizes. For now we assume that having two hash
|
||||||
|
/// functions is enough, but we may revisit that decision later.
|
||||||
///
|
///
|
||||||
/// The filter uses an array with 2**KeySize entries.
|
/// The filter uses an array with 2**KeySize entries.
|
||||||
///
|
///
|
||||||
|
@ -61,58 +71,30 @@ const KEY_MASK: u32 = (1 << KEY_SIZE) - 1;
|
||||||
/// Similarly, using a KeySize of 10 would lead to a 4% false
|
/// Similarly, using a KeySize of 10 would lead to a 4% false
|
||||||
/// positive rate for N == 100 and to quite bad false positive
|
/// positive rate for N == 100 and to quite bad false positive
|
||||||
/// rates for larger N.
|
/// rates for larger N.
|
||||||
pub struct BloomFilter {
|
#[derive(Clone)]
|
||||||
counters: [u8; ARRAY_SIZE],
|
pub struct CountingBloomFilter<S> where S: BloomStorage {
|
||||||
|
storage: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for BloomFilter {
|
impl<S> CountingBloomFilter<S> where S: BloomStorage {
|
||||||
#[inline]
|
|
||||||
fn clone(&self) -> BloomFilter {
|
|
||||||
BloomFilter {
|
|
||||||
counters: self.counters,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BloomFilter {
|
|
||||||
/// Creates a new bloom filter.
|
/// Creates a new bloom filter.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> BloomFilter {
|
pub fn new() -> Self {
|
||||||
BloomFilter {
|
CountingBloomFilter {
|
||||||
counters: [0; ARRAY_SIZE],
|
storage: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn first_slot(&self, hash: u32) -> &u8 {
|
|
||||||
&self.counters[hash1(hash) as usize]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn first_mut_slot(&mut self, hash: u32) -> &mut u8 {
|
|
||||||
&mut self.counters[hash1(hash) as usize]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn second_slot(&self, hash: u32) -> &u8 {
|
|
||||||
&self.counters[hash2(hash) as usize]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn second_mut_slot(&mut self, hash: u32) -> &mut u8 {
|
|
||||||
&mut self.counters[hash2(hash) as usize]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.counters = [0; ARRAY_SIZE]
|
self.storage = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slow linear accessor to make sure the bloom filter is zeroed. This should
|
// Slow linear accessor to make sure the bloom filter is zeroed. This should
|
||||||
// never be used in release builds.
|
// never be used in release builds.
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub fn is_zeroed(&self) -> bool {
|
pub fn is_zeroed(&self) -> bool {
|
||||||
self.counters.iter().all(|x| *x == 0)
|
self.storage.is_zeroed()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
|
@ -122,18 +104,8 @@ impl BloomFilter {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_hash(&mut self, hash: u32) {
|
pub fn insert_hash(&mut self, hash: u32) {
|
||||||
{
|
self.storage.adjust_first_slot(hash, true);
|
||||||
let slot1 = self.first_mut_slot(hash);
|
self.storage.adjust_second_slot(hash, true);
|
||||||
if !full(slot1) {
|
|
||||||
*slot1 += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let slot2 = self.second_mut_slot(hash);
|
|
||||||
if !full(slot2) {
|
|
||||||
*slot2 += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts an item into the bloom filter.
|
/// Inserts an item into the bloom filter.
|
||||||
|
@ -144,18 +116,8 @@ impl BloomFilter {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_hash(&mut self, hash: u32) {
|
pub fn remove_hash(&mut self, hash: u32) {
|
||||||
{
|
self.storage.adjust_first_slot(hash, false);
|
||||||
let slot1 = self.first_mut_slot(hash);
|
self.storage.adjust_second_slot(hash, false);
|
||||||
if !full(slot1) {
|
|
||||||
*slot1 -= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let slot2 = self.second_mut_slot(hash);
|
|
||||||
if !full(slot2) {
|
|
||||||
*slot2 -= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes an item from the bloom filter.
|
/// Removes an item from the bloom filter.
|
||||||
|
@ -166,7 +128,8 @@ impl BloomFilter {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn might_contain_hash(&self, hash: u32) -> bool {
|
pub fn might_contain_hash(&self, hash: u32) -> bool {
|
||||||
*self.first_slot(hash) != 0 && *self.second_slot(hash) != 0
|
!self.storage.first_slot_is_empty(hash) &&
|
||||||
|
!self.storage.second_slot_is_empty(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether the filter might contain an item. This can
|
/// Check whether the filter might contain an item. This can
|
||||||
|
@ -179,9 +142,147 @@ impl BloomFilter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
impl<S> Debug for CountingBloomFilter<S> where S: BloomStorage {
|
||||||
fn full(slot: &u8) -> bool {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
*slot == 0xff
|
let mut slots_used = 0;
|
||||||
|
for i in 0..ARRAY_SIZE {
|
||||||
|
if !self.storage.slot_is_empty(i) {
|
||||||
|
slots_used += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(f, "BloomFilter({}/{})", slots_used, ARRAY_SIZE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BloomStorage : Clone + Default {
|
||||||
|
fn slot_is_empty(&self, index: usize) -> bool;
|
||||||
|
fn adjust_slot(&mut self, index: usize, increment: bool);
|
||||||
|
fn is_zeroed(&self) -> bool;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn first_slot_is_empty(&self, hash: u32) -> bool {
|
||||||
|
self.slot_is_empty(Self::first_slot_index(hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn second_slot_is_empty(&self, hash: u32) -> bool {
|
||||||
|
self.slot_is_empty(Self::second_slot_index(hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn adjust_first_slot(&mut self, hash: u32, increment: bool) {
|
||||||
|
self.adjust_slot(Self::first_slot_index(hash), increment)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn adjust_second_slot(&mut self, hash: u32, increment: bool) {
|
||||||
|
self.adjust_slot(Self::second_slot_index(hash), increment)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn first_slot_index(hash: u32) -> usize {
|
||||||
|
hash1(hash) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn second_slot_index(hash: u32) -> usize {
|
||||||
|
hash2(hash) as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Storage class for a CountingBloomFilter that has 8-bit counters.
|
||||||
|
pub struct BloomStorageU8 {
|
||||||
|
counters: [u8; ARRAY_SIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BloomStorage for BloomStorageU8 {
|
||||||
|
#[inline]
|
||||||
|
fn adjust_slot(&mut self, index: usize, increment: bool) {
|
||||||
|
let slot = &mut self.counters[index];
|
||||||
|
if *slot != 0xff { // full
|
||||||
|
if increment {
|
||||||
|
*slot += 1;
|
||||||
|
} else {
|
||||||
|
*slot -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn slot_is_empty(&self, index: usize) -> bool {
|
||||||
|
self.counters[index] == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_zeroed(&self) -> bool {
|
||||||
|
self.counters.iter().all(|x| *x == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BloomStorageU8 {
|
||||||
|
fn default() -> Self {
|
||||||
|
BloomStorageU8 {
|
||||||
|
counters: [0; ARRAY_SIZE],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for BloomStorageU8 {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
BloomStorageU8 {
|
||||||
|
counters: self.counters,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Storage class for a CountingBloomFilter that has 1-bit counters.
|
||||||
|
pub struct BloomStorageBool {
|
||||||
|
counters: [u8; ARRAY_SIZE / 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BloomStorage for BloomStorageBool {
|
||||||
|
#[inline]
|
||||||
|
fn adjust_slot(&mut self, index: usize, increment: bool) {
|
||||||
|
let bit = 1 << (index % 8);
|
||||||
|
let byte = &mut self.counters[index / 8];
|
||||||
|
|
||||||
|
// Since we have only one bit for storage, decrementing it
|
||||||
|
// should never do anything. Assert against an accidental
|
||||||
|
// decrementing of a bit that was never set.
|
||||||
|
assert!(increment || (*byte & bit) != 0,
|
||||||
|
"should not decrement if slot is already false");
|
||||||
|
|
||||||
|
if increment {
|
||||||
|
*byte |= bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn slot_is_empty(&self, index: usize) -> bool {
|
||||||
|
let bit = 1 << (index % 8);
|
||||||
|
(self.counters[index / 8] & bit) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_zeroed(&self) -> bool {
|
||||||
|
self.counters.iter().all(|x| *x == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BloomStorageBool {
|
||||||
|
fn default() -> Self {
|
||||||
|
BloomStorageBool {
|
||||||
|
counters: [0; ARRAY_SIZE / 8],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for BloomStorageBool {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
BloomStorageBool {
|
||||||
|
counters: self.counters,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash<T: Hash>(elem: &T) -> u32 {
|
fn hash<T: Hash>(elem: &T) -> u32 {
|
||||||
|
@ -203,8 +304,16 @@ fn hash2(hash: u32) -> u32 {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_and_insert_some_stuff() {
|
fn create_and_insert_some_stuff() {
|
||||||
|
use std::mem::transmute;
|
||||||
|
|
||||||
let mut bf = BloomFilter::new();
|
let mut bf = BloomFilter::new();
|
||||||
|
|
||||||
|
// Statically assert that ARRAY_SIZE is a multiple of 8, which
|
||||||
|
// BloomStorageBool relies on.
|
||||||
|
unsafe {
|
||||||
|
transmute::<[u8; ARRAY_SIZE % 8], [u8; 0]>([]);
|
||||||
|
}
|
||||||
|
|
||||||
for i in 0_usize .. 1000 {
|
for i in 0_usize .. 1000 {
|
||||||
bf.insert(&i);
|
bf.insert(&i);
|
||||||
}
|
}
|
||||||
|
|
|
@ -533,7 +533,7 @@ pub fn maybe_start_animations(context: &SharedStyleContext,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref anim) = context.stylist.animations().get(name) {
|
if let Some(ref anim) = context.stylist.get_animation(name) {
|
||||||
debug!("maybe_start_animations: animation {} found", name);
|
debug!("maybe_start_animations: animation {} found", name);
|
||||||
|
|
||||||
// If this animation doesn't have any keyframe, we can just continue
|
// If this animation doesn't have any keyframe, we can just continue
|
||||||
|
@ -637,7 +637,7 @@ pub fn update_style_for_animation(context: &SharedStyleContext,
|
||||||
KeyframesRunningState::Paused(progress) => started_at + duration * progress,
|
KeyframesRunningState::Paused(progress) => started_at + duration * progress,
|
||||||
};
|
};
|
||||||
|
|
||||||
let animation = match context.stylist.animations().get(name) {
|
let animation = match context.stylist.get_animation(name) {
|
||||||
None => {
|
None => {
|
||||||
warn!("update_style_for_animation: Animation {:?} not found", name);
|
warn!("update_style_for_animation: Animation {:?} not found", name);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -388,16 +388,16 @@ impl TraversalStatistics {
|
||||||
D: DomTraversal<E>,
|
D: DomTraversal<E>,
|
||||||
{
|
{
|
||||||
let threshold = traversal.shared_context().options.style_statistics_threshold;
|
let threshold = traversal.shared_context().options.style_statistics_threshold;
|
||||||
|
let stylist = traversal.shared_context().stylist;
|
||||||
|
|
||||||
self.is_parallel = Some(traversal.is_parallel());
|
self.is_parallel = Some(traversal.is_parallel());
|
||||||
self.is_large = Some(self.elements_traversed as usize >= threshold);
|
self.is_large = Some(self.elements_traversed as usize >= threshold);
|
||||||
self.traversal_time_ms = (time::precise_time_s() - start) * 1000.0;
|
self.traversal_time_ms = (time::precise_time_s() - start) * 1000.0;
|
||||||
self.selectors = traversal.shared_context().stylist.num_selectors() as u32;
|
self.selectors = stylist.num_selectors() as u32;
|
||||||
self.revalidation_selectors = traversal.shared_context().stylist.num_revalidation_selectors() as u32;
|
self.revalidation_selectors = stylist.num_revalidation_selectors() as u32;
|
||||||
self.dependency_selectors =
|
self.dependency_selectors = stylist.num_invalidations() as u32;
|
||||||
traversal.shared_context().stylist.invalidation_map().len() as u32;
|
self.declarations = stylist.num_declarations() as u32;
|
||||||
self.declarations = traversal.shared_context().stylist.num_declarations() as u32;
|
self.stylist_rebuilds = stylist.num_rebuilds() as u32;
|
||||||
self.stylist_rebuilds = traversal.shared_context().stylist.num_rebuilds() as u32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether this traversal is 'large' in order to avoid console spam
|
/// Returns whether this traversal is 'large' in order to avoid console spam
|
||||||
|
|
|
@ -109,7 +109,7 @@ impl SelectorMapEntry for Dependency {
|
||||||
|
|
||||||
/// The same, but for state selectors, which can track more exactly what state
|
/// The same, but for state selectors, which can track more exactly what state
|
||||||
/// do they track.
|
/// do they track.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct StateDependency {
|
pub struct StateDependency {
|
||||||
/// The other dependency fields.
|
/// The other dependency fields.
|
||||||
|
@ -132,6 +132,7 @@ impl SelectorMapEntry for StateDependency {
|
||||||
/// In particular, we want to lookup as few things as possible to get the fewer
|
/// In particular, we want to lookup as few things as possible to get the fewer
|
||||||
/// selectors the better, so this looks up by id, class, or looks at the list of
|
/// selectors the better, so this looks up by id, class, or looks at the list of
|
||||||
/// state/other attribute affecting selectors.
|
/// state/other attribute affecting selectors.
|
||||||
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct InvalidationMap {
|
pub struct InvalidationMap {
|
||||||
/// A map from a given class name to all the selectors with that class
|
/// A map from a given class name to all the selectors with that class
|
||||||
|
|
|
@ -213,9 +213,9 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
|
||||||
invalidates_self: false,
|
invalidates_self: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
collector.collect_dependencies_in_invalidation_map(
|
shared_context.stylist.each_invalidation_map(|invalidation_map| {
|
||||||
shared_context.stylist.invalidation_map(),
|
collector.collect_dependencies_in_invalidation_map(invalidation_map);
|
||||||
);
|
});
|
||||||
|
|
||||||
// TODO(emilio): Consider storing dependencies from the UA sheet in
|
// TODO(emilio): Consider storing dependencies from the UA sheet in
|
||||||
// a different map. If we do that, we can skip the stuff on the
|
// a different map. If we do that, we can skip the stuff on the
|
||||||
|
@ -223,9 +223,9 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
|
||||||
// just at that map.
|
// just at that map.
|
||||||
let _cut_off_inheritance =
|
let _cut_off_inheritance =
|
||||||
self.element.each_xbl_stylist(|stylist| {
|
self.element.each_xbl_stylist(|stylist| {
|
||||||
collector.collect_dependencies_in_invalidation_map(
|
stylist.each_invalidation_map(|invalidation_map| {
|
||||||
stylist.invalidation_map(),
|
collector.collect_dependencies_in_invalidation_map(invalidation_map);
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
collector.invalidates_self
|
collector.invalidates_self
|
||||||
|
|
|
@ -25,7 +25,7 @@ use rule_tree::{CascadeLevel, RuleTree, StyleSource};
|
||||||
use selector_map::{PrecomputedHashMap, SelectorMap, SelectorMapEntry};
|
use selector_map::{PrecomputedHashMap, SelectorMap, SelectorMapEntry};
|
||||||
use selector_parser::{SelectorImpl, PerPseudoElementMap, PseudoElement};
|
use selector_parser::{SelectorImpl, PerPseudoElementMap, PseudoElement};
|
||||||
use selectors::attr::NamespaceConstraint;
|
use selectors::attr::NamespaceConstraint;
|
||||||
use selectors::bloom::BloomFilter;
|
use selectors::bloom::{BloomFilter, NonCountingBloomFilter};
|
||||||
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
|
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
|
||||||
use selectors::matching::VisitedHandlingMode;
|
use selectors::matching::VisitedHandlingMode;
|
||||||
use selectors::parser::{AncestorHashes, Combinator, Component, Selector};
|
use selectors::parser::{AncestorHashes, Combinator, Component, Selector};
|
||||||
|
@ -97,9 +97,6 @@ pub struct Stylist {
|
||||||
/// The rule tree, that stores the results of selector matching.
|
/// The rule tree, that stores the results of selector matching.
|
||||||
rule_tree: RuleTree,
|
rule_tree: RuleTree,
|
||||||
|
|
||||||
/// A map with all the animations indexed by name.
|
|
||||||
animations: PrecomputedHashMap<Atom, KeyframesAnimation>,
|
|
||||||
|
|
||||||
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
||||||
/// These are eagerly computed once, and then used to resolve the new
|
/// These are eagerly computed once, and then used to resolve the new
|
||||||
/// computed values on the fly on layout.
|
/// computed values on the fly on layout.
|
||||||
|
@ -111,52 +108,6 @@ pub struct Stylist {
|
||||||
/// style rule appears in a stylesheet, needed to sort them by source order.
|
/// style rule appears in a stylesheet, needed to sort them by source order.
|
||||||
rules_source_order: u32,
|
rules_source_order: u32,
|
||||||
|
|
||||||
/// The invalidation map for this document.
|
|
||||||
invalidation_map: InvalidationMap,
|
|
||||||
|
|
||||||
/// The attribute local names that appear in attribute selectors. Used
|
|
||||||
/// to avoid taking element snapshots when an irrelevant attribute changes.
|
|
||||||
/// (We don't bother storing the namespace, since namespaced attributes
|
|
||||||
/// are rare.)
|
|
||||||
///
|
|
||||||
/// FIXME(heycam): This doesn't really need to be a counting Bloom filter.
|
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "just an array")]
|
|
||||||
attribute_dependencies: BloomFilter,
|
|
||||||
|
|
||||||
/// Whether `"style"` appears in an attribute selector. This is not common,
|
|
||||||
/// and by tracking this explicitly, we can avoid taking an element snapshot
|
|
||||||
/// in the common case of style=""` changing due to modifying
|
|
||||||
/// `element.style`. (We could track this in `attribute_dependencies`, like
|
|
||||||
/// all other attributes, but we should probably not risk incorrectly
|
|
||||||
/// returning `true` for `"style"` just due to a hash collision.)
|
|
||||||
style_attribute_dependency: bool,
|
|
||||||
|
|
||||||
/// The element state bits that are relied on by selectors. Like
|
|
||||||
/// `attribute_dependencies`, this is used to avoid taking element snapshots
|
|
||||||
/// when an irrelevant element state bit changes.
|
|
||||||
state_dependencies: ElementState,
|
|
||||||
|
|
||||||
/// The ids that appear in the rightmost complex selector of selectors (and
|
|
||||||
/// hence in our selector maps). Used to determine when sharing styles is
|
|
||||||
/// safe: we disallow style sharing for elements whose id matches this
|
|
||||||
/// filter, and hence might be in one of our selector maps.
|
|
||||||
///
|
|
||||||
/// FIXME(bz): This doesn't really need to be a counting Blooom filter.
|
|
||||||
#[cfg_attr(feature = "servo", ignore_heap_size_of = "just an array")]
|
|
||||||
mapped_ids: BloomFilter,
|
|
||||||
|
|
||||||
/// 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")]
|
|
||||||
selectors_for_cache_revalidation: SelectorMap<RevalidationSelectorAndHashes>,
|
|
||||||
|
|
||||||
/// The total number of selectors.
|
|
||||||
num_selectors: usize,
|
|
||||||
|
|
||||||
/// The total number of declarations.
|
|
||||||
num_declarations: usize,
|
|
||||||
|
|
||||||
/// The total number of times the stylist has been rebuilt.
|
/// The total number of times the stylist has been rebuilt.
|
||||||
num_rebuilds: usize,
|
num_rebuilds: usize,
|
||||||
}
|
}
|
||||||
|
@ -239,18 +190,9 @@ impl Stylist {
|
||||||
effective_media_query_results: EffectiveMediaQueryResults::new(),
|
effective_media_query_results: EffectiveMediaQueryResults::new(),
|
||||||
|
|
||||||
cascade_data: CascadeData::new(),
|
cascade_data: CascadeData::new(),
|
||||||
animations: Default::default(),
|
|
||||||
precomputed_pseudo_element_decls: PerPseudoElementMap::default(),
|
precomputed_pseudo_element_decls: PerPseudoElementMap::default(),
|
||||||
rules_source_order: 0,
|
rules_source_order: 0,
|
||||||
rule_tree: RuleTree::new(),
|
rule_tree: RuleTree::new(),
|
||||||
invalidation_map: InvalidationMap::new(),
|
|
||||||
attribute_dependencies: BloomFilter::new(),
|
|
||||||
style_attribute_dependency: false,
|
|
||||||
state_dependencies: ElementState::empty(),
|
|
||||||
mapped_ids: BloomFilter::new(),
|
|
||||||
selectors_for_cache_revalidation: SelectorMap::new(),
|
|
||||||
num_selectors: 0,
|
|
||||||
num_declarations: 0,
|
|
||||||
num_rebuilds: 0,
|
num_rebuilds: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,12 +201,12 @@ impl Stylist {
|
||||||
|
|
||||||
/// Returns the number of selectors.
|
/// Returns the number of selectors.
|
||||||
pub fn num_selectors(&self) -> usize {
|
pub fn num_selectors(&self) -> usize {
|
||||||
self.num_selectors
|
self.cascade_data.iter_origins().map(|d| d.num_selectors).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of declarations.
|
/// Returns the number of declarations.
|
||||||
pub fn num_declarations(&self) -> usize {
|
pub fn num_declarations(&self) -> usize {
|
||||||
self.num_declarations
|
self.cascade_data.iter_origins().map(|d| d.num_declarations).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of times the stylist has been rebuilt.
|
/// Returns the number of times the stylist has been rebuilt.
|
||||||
|
@ -274,12 +216,27 @@ impl Stylist {
|
||||||
|
|
||||||
/// Returns the number of revalidation_selectors.
|
/// Returns the number of revalidation_selectors.
|
||||||
pub fn num_revalidation_selectors(&self) -> usize {
|
pub fn num_revalidation_selectors(&self) -> usize {
|
||||||
self.selectors_for_cache_revalidation.len()
|
self.cascade_data.iter_origins()
|
||||||
|
.map(|d| d.selectors_for_cache_revalidation.len()).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a reference to the invalidation map.
|
/// Returns the number of entries in invalidation maps.
|
||||||
pub fn invalidation_map(&self) -> &InvalidationMap {
|
pub fn num_invalidations(&self) -> usize {
|
||||||
&self.invalidation_map
|
self.cascade_data.iter_origins()
|
||||||
|
.map(|d| d.invalidation_map.len()).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invokes `f` with the `InvalidationMap` for each origin.
|
||||||
|
///
|
||||||
|
/// NOTE(heycam) This might be better as an `iter_invalidation_maps`, once
|
||||||
|
/// we have `impl trait` and can return that easily without bothering to
|
||||||
|
/// create a whole new iterator type.
|
||||||
|
pub fn each_invalidation_map<F>(&self, mut f: F)
|
||||||
|
where F: FnMut(&InvalidationMap)
|
||||||
|
{
|
||||||
|
for origin_cascade_data in self.cascade_data.iter_origins() {
|
||||||
|
f(&origin_cascade_data.invalidation_map)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the stylist's state, effectively resetting it to more or less
|
/// Clear the stylist's state, effectively resetting it to more or less
|
||||||
|
@ -307,18 +264,9 @@ impl Stylist {
|
||||||
self.is_device_dirty = true;
|
self.is_device_dirty = true;
|
||||||
// preserve current quirks_mode value
|
// preserve current quirks_mode value
|
||||||
self.cascade_data.clear();
|
self.cascade_data.clear();
|
||||||
self.animations.clear(); // Or set to Default::default()?
|
|
||||||
self.precomputed_pseudo_element_decls.clear();
|
self.precomputed_pseudo_element_decls.clear();
|
||||||
self.rules_source_order = 0;
|
self.rules_source_order = 0;
|
||||||
// We want to keep rule_tree around across stylist rebuilds.
|
// We want to keep rule_tree around across stylist rebuilds.
|
||||||
self.invalidation_map.clear();
|
|
||||||
self.attribute_dependencies.clear();
|
|
||||||
self.style_attribute_dependency = false;
|
|
||||||
self.state_dependencies = ElementState::empty();
|
|
||||||
self.mapped_ids.clear();
|
|
||||||
self.selectors_for_cache_revalidation = SelectorMap::new();
|
|
||||||
self.num_selectors = 0;
|
|
||||||
self.num_declarations = 0;
|
|
||||||
// preserve num_rebuilds value, since it should stay across
|
// preserve num_rebuilds value, since it should stay across
|
||||||
// clear()/rebuild() cycles.
|
// clear()/rebuild() cycles.
|
||||||
}
|
}
|
||||||
|
@ -459,9 +407,10 @@ impl Stylist {
|
||||||
match *rule {
|
match *rule {
|
||||||
CssRule::Style(ref locked) => {
|
CssRule::Style(ref locked) => {
|
||||||
let style_rule = locked.read_with(&guard);
|
let style_rule = locked.read_with(&guard);
|
||||||
self.num_declarations += style_rule.block.read_with(&guard).len();
|
origin_cascade_data.num_declarations +=
|
||||||
|
style_rule.block.read_with(&guard).len();
|
||||||
for selector in &style_rule.selectors.0 {
|
for selector in &style_rule.selectors.0 {
|
||||||
self.num_selectors += 1;
|
origin_cascade_data.num_selectors += 1;
|
||||||
|
|
||||||
let map = match selector.pseudo_element() {
|
let map = match selector.pseudo_element() {
|
||||||
Some(pseudo) if pseudo.is_precomputed() => {
|
Some(pseudo) if pseudo.is_precomputed() => {
|
||||||
|
@ -506,20 +455,22 @@ impl Stylist {
|
||||||
|
|
||||||
map.insert(rule, self.quirks_mode);
|
map.insert(rule, self.quirks_mode);
|
||||||
|
|
||||||
self.invalidation_map.note_selector(selector, self.quirks_mode);
|
origin_cascade_data
|
||||||
|
.invalidation_map
|
||||||
|
.note_selector(selector, self.quirks_mode);
|
||||||
let mut visitor = StylistSelectorVisitor {
|
let mut visitor = StylistSelectorVisitor {
|
||||||
needs_revalidation: false,
|
needs_revalidation: false,
|
||||||
passed_rightmost_selector: false,
|
passed_rightmost_selector: false,
|
||||||
attribute_dependencies: &mut self.attribute_dependencies,
|
attribute_dependencies: &mut origin_cascade_data.attribute_dependencies,
|
||||||
style_attribute_dependency: &mut self.style_attribute_dependency,
|
style_attribute_dependency: &mut origin_cascade_data.style_attribute_dependency,
|
||||||
state_dependencies: &mut self.state_dependencies,
|
state_dependencies: &mut origin_cascade_data.state_dependencies,
|
||||||
mapped_ids: &mut self.mapped_ids,
|
mapped_ids: &mut origin_cascade_data.mapped_ids,
|
||||||
};
|
};
|
||||||
|
|
||||||
selector.visit(&mut visitor);
|
selector.visit(&mut visitor);
|
||||||
|
|
||||||
if visitor.needs_revalidation {
|
if visitor.needs_revalidation {
|
||||||
self.selectors_for_cache_revalidation.insert(
|
origin_cascade_data.selectors_for_cache_revalidation.insert(
|
||||||
RevalidationSelectorAndHashes::new(selector.clone(), hashes),
|
RevalidationSelectorAndHashes::new(selector.clone(), hashes),
|
||||||
self.quirks_mode);
|
self.quirks_mode);
|
||||||
}
|
}
|
||||||
|
@ -542,14 +493,15 @@ impl Stylist {
|
||||||
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
|
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
|
||||||
|
|
||||||
// Don't let a prefixed keyframes animation override a non-prefixed one.
|
// Don't let a prefixed keyframes animation override a non-prefixed one.
|
||||||
let needs_insertion = keyframes_rule.vendor_prefix.is_none() ||
|
let needs_insertion =
|
||||||
self.animations.get(keyframes_rule.name.as_atom()).map_or(true, |rule|
|
keyframes_rule.vendor_prefix.is_none() ||
|
||||||
rule.vendor_prefix.is_some());
|
origin_cascade_data.animations.get(keyframes_rule.name.as_atom())
|
||||||
|
.map_or(true, |rule| rule.vendor_prefix.is_some());
|
||||||
if needs_insertion {
|
if needs_insertion {
|
||||||
let animation = KeyframesAnimation::from_keyframes(
|
let animation = KeyframesAnimation::from_keyframes(
|
||||||
&keyframes_rule.keyframes, keyframes_rule.vendor_prefix.clone(), guard);
|
&keyframes_rule.keyframes, keyframes_rule.vendor_prefix.clone(), guard);
|
||||||
debug!("Found valid keyframe animation: {:?}", animation);
|
debug!("Found valid keyframe animation: {:?}", animation);
|
||||||
self.animations.insert(keyframes_rule.name.as_atom().clone(), animation);
|
origin_cascade_data.animations.insert(keyframes_rule.name.as_atom().clone(), animation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -576,9 +528,16 @@ impl Stylist {
|
||||||
// we rebuild.
|
// we rebuild.
|
||||||
true
|
true
|
||||||
} else if *local_name == local_name!("style") {
|
} else if *local_name == local_name!("style") {
|
||||||
self.style_attribute_dependency
|
self.cascade_data
|
||||||
|
.iter_origins()
|
||||||
|
.any(|d| d.style_attribute_dependency)
|
||||||
} else {
|
} else {
|
||||||
self.attribute_dependencies.might_contain_hash(local_name.get_hash())
|
self.cascade_data
|
||||||
|
.iter_origins()
|
||||||
|
.any(|d| {
|
||||||
|
d.attribute_dependencies
|
||||||
|
.might_contain_hash(local_name.get_hash())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -590,14 +549,16 @@ impl Stylist {
|
||||||
// rules rely on until we rebuild.
|
// rules rely on until we rebuild.
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
self.state_dependencies.intersects(state)
|
self.has_state_dependency(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the given ElementState bit is relied upon by a selector
|
/// Returns whether the given ElementState bit is relied upon by a selector
|
||||||
/// of some rule in the stylist.
|
/// of some rule in the stylist.
|
||||||
pub fn has_state_dependency(&self, state: ElementState) -> bool {
|
pub fn has_state_dependency(&self, state: ElementState) -> bool {
|
||||||
self.state_dependencies.intersects(state)
|
self.cascade_data
|
||||||
|
.iter_origins()
|
||||||
|
.any(|d| d.state_dependencies.intersects(state))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the style for a given "precomputed" pseudo-element, taking the
|
/// Computes the style for a given "precomputed" pseudo-element, taking the
|
||||||
|
@ -1317,7 +1278,9 @@ impl Stylist {
|
||||||
/// of our rule maps.
|
/// of our rule maps.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn may_have_rules_for_id(&self, id: &Atom) -> bool {
|
pub fn may_have_rules_for_id(&self, id: &Atom) -> bool {
|
||||||
self.mapped_ids.might_contain_hash(id.get_hash())
|
self.cascade_data
|
||||||
|
.iter_origins()
|
||||||
|
.any(|d| d.mapped_ids.might_contain_hash(id.get_hash()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return whether the device is dirty, that is, whether the screen size or
|
/// Return whether the device is dirty, that is, whether the screen size or
|
||||||
|
@ -1327,10 +1290,13 @@ impl Stylist {
|
||||||
self.is_device_dirty
|
self.is_device_dirty
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the map of registered `@keyframes` animations.
|
/// Returns the registered `@keyframes` animation for the specified name.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn animations(&self) -> &PrecomputedHashMap<Atom, KeyframesAnimation> {
|
pub fn get_animation(&self, name: &Atom) -> Option<&KeyframesAnimation> {
|
||||||
&self.animations
|
self.cascade_data
|
||||||
|
.iter_origins()
|
||||||
|
.filter_map(|d| d.animations.get(name))
|
||||||
|
.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the match results of a given element against the set of
|
/// Computes the match results of a given element against the set of
|
||||||
|
@ -1354,17 +1320,19 @@ impl Stylist {
|
||||||
// the lookups, which means that the bitvecs are comparable. We verify
|
// the lookups, which means that the bitvecs are comparable. We verify
|
||||||
// this in the caller by asserting that the bitvecs are same-length.
|
// this in the caller by asserting that the bitvecs are same-length.
|
||||||
let mut results = BitVec::new();
|
let mut results = BitVec::new();
|
||||||
self.selectors_for_cache_revalidation.lookup(
|
for origin_cascade_data in self.cascade_data.iter_origins() {
|
||||||
*element, self.quirks_mode, &mut |selector_and_hashes| {
|
origin_cascade_data.selectors_for_cache_revalidation.lookup(
|
||||||
results.push(matches_selector(&selector_and_hashes.selector,
|
*element, self.quirks_mode, &mut |selector_and_hashes| {
|
||||||
selector_and_hashes.selector_offset,
|
results.push(matches_selector(&selector_and_hashes.selector,
|
||||||
Some(&selector_and_hashes.hashes),
|
selector_and_hashes.selector_offset,
|
||||||
element,
|
Some(&selector_and_hashes.hashes),
|
||||||
&mut matching_context,
|
element,
|
||||||
flags_setter));
|
&mut matching_context,
|
||||||
true
|
flags_setter));
|
||||||
}
|
true
|
||||||
);
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
@ -1470,9 +1438,9 @@ struct StylistSelectorVisitor<'a> {
|
||||||
passed_rightmost_selector: bool,
|
passed_rightmost_selector: bool,
|
||||||
/// The filter with all the id's getting referenced from rightmost
|
/// The filter with all the id's getting referenced from rightmost
|
||||||
/// selectors.
|
/// selectors.
|
||||||
mapped_ids: &'a mut BloomFilter,
|
mapped_ids: &'a mut NonCountingBloomFilter,
|
||||||
/// The filter with the local names of attributes there are selectors for.
|
/// The filter with the local names of attributes there are selectors for.
|
||||||
attribute_dependencies: &'a mut BloomFilter,
|
attribute_dependencies: &'a mut NonCountingBloomFilter,
|
||||||
/// Whether there's any attribute selector for the [style] attribute.
|
/// Whether there's any attribute selector for the [style] attribute.
|
||||||
style_attribute_dependency: &'a mut bool,
|
style_attribute_dependency: &'a mut bool,
|
||||||
/// All the states selectors in the page reference.
|
/// All the states selectors in the page reference.
|
||||||
|
@ -1635,6 +1603,11 @@ impl CascadeData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterator over `PerOriginCascadeData`, from highest level (user) to lowest
|
||||||
|
/// (user agent).
|
||||||
|
///
|
||||||
|
/// We rely on this specific order for correctly looking up animations
|
||||||
|
/// (prioritizing rules at higher cascade levels), among other things.
|
||||||
struct CascadeDataIter<'a> {
|
struct CascadeDataIter<'a> {
|
||||||
cascade_data: &'a CascadeData,
|
cascade_data: &'a CascadeData,
|
||||||
cur: usize,
|
cur: usize,
|
||||||
|
@ -1645,9 +1618,9 @@ impl<'a> Iterator for CascadeDataIter<'a> {
|
||||||
|
|
||||||
fn next(&mut self) -> Option<&'a PerOriginCascadeData> {
|
fn next(&mut self) -> Option<&'a PerOriginCascadeData> {
|
||||||
let result = match self.cur {
|
let result = match self.cur {
|
||||||
0 => &self.cascade_data.user_agent,
|
0 => &self.cascade_data.user,
|
||||||
1 => &self.cascade_data.author,
|
1 => &self.cascade_data.author,
|
||||||
2 => &self.cascade_data.user,
|
2 => &self.cascade_data.user_agent,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
self.cur += 1;
|
self.cur += 1;
|
||||||
|
@ -1666,6 +1639,52 @@ struct PerOriginCascadeData {
|
||||||
/// Rules from stylesheets at this `CascadeData`'s origin that correspond
|
/// Rules from stylesheets at this `CascadeData`'s origin that correspond
|
||||||
/// to a given pseudo-element.
|
/// to a given pseudo-element.
|
||||||
pseudos_map: PerPseudoElementMap<SelectorMap<Rule>>,
|
pseudos_map: PerPseudoElementMap<SelectorMap<Rule>>,
|
||||||
|
|
||||||
|
/// A map with all the animations at this `CascadeData`'s origin, indexed
|
||||||
|
/// by name.
|
||||||
|
animations: PrecomputedHashMap<Atom, KeyframesAnimation>,
|
||||||
|
|
||||||
|
/// The invalidation map for the rules at this origin.
|
||||||
|
invalidation_map: InvalidationMap,
|
||||||
|
|
||||||
|
/// The attribute local names that appear in attribute selectors. Used
|
||||||
|
/// to avoid taking element snapshots when an irrelevant attribute changes.
|
||||||
|
/// (We don't bother storing the namespace, since namespaced attributes
|
||||||
|
/// are rare.)
|
||||||
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "just an array")]
|
||||||
|
attribute_dependencies: NonCountingBloomFilter,
|
||||||
|
|
||||||
|
/// Whether `"style"` appears in an attribute selector. This is not common,
|
||||||
|
/// and by tracking this explicitly, we can avoid taking an element snapshot
|
||||||
|
/// in the common case of style=""` changing due to modifying
|
||||||
|
/// `element.style`. (We could track this in `attribute_dependencies`, like
|
||||||
|
/// all other attributes, but we should probably not risk incorrectly
|
||||||
|
/// returning `true` for `"style"` just due to a hash collision.)
|
||||||
|
style_attribute_dependency: bool,
|
||||||
|
|
||||||
|
/// The element state bits that are relied on by selectors. Like
|
||||||
|
/// `attribute_dependencies`, this is used to avoid taking element snapshots
|
||||||
|
/// when an irrelevant element state bit changes.
|
||||||
|
state_dependencies: ElementState,
|
||||||
|
|
||||||
|
/// The ids that appear in the rightmost complex selector of selectors (and
|
||||||
|
/// hence in our selector maps). Used to determine when sharing styles is
|
||||||
|
/// safe: we disallow style sharing for elements whose id matches this
|
||||||
|
/// filter, and hence might be in one of our selector maps.
|
||||||
|
#[cfg_attr(feature = "servo", ignore_heap_size_of = "just an array")]
|
||||||
|
mapped_ids: NonCountingBloomFilter,
|
||||||
|
|
||||||
|
/// 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")]
|
||||||
|
selectors_for_cache_revalidation: SelectorMap<RevalidationSelectorAndHashes>,
|
||||||
|
|
||||||
|
/// The total number of selectors.
|
||||||
|
num_selectors: usize,
|
||||||
|
|
||||||
|
/// The total number of declarations.
|
||||||
|
num_declarations: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerOriginCascadeData {
|
impl PerOriginCascadeData {
|
||||||
|
@ -1673,6 +1692,15 @@ impl PerOriginCascadeData {
|
||||||
Self {
|
Self {
|
||||||
element_map: SelectorMap::new(),
|
element_map: SelectorMap::new(),
|
||||||
pseudos_map: PerPseudoElementMap::default(),
|
pseudos_map: PerPseudoElementMap::default(),
|
||||||
|
animations: Default::default(),
|
||||||
|
invalidation_map: InvalidationMap::new(),
|
||||||
|
attribute_dependencies: NonCountingBloomFilter::new(),
|
||||||
|
style_attribute_dependency: false,
|
||||||
|
state_dependencies: ElementState::empty(),
|
||||||
|
mapped_ids: NonCountingBloomFilter::new(),
|
||||||
|
selectors_for_cache_revalidation: SelectorMap::new(),
|
||||||
|
num_selectors: 0,
|
||||||
|
num_declarations: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1685,7 +1713,17 @@ impl PerOriginCascadeData {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
*self = Self::new();
|
self.element_map = SelectorMap::new();
|
||||||
|
self.pseudos_map = Default::default();
|
||||||
|
self.animations = Default::default();
|
||||||
|
self.invalidation_map.clear();
|
||||||
|
self.attribute_dependencies.clear();
|
||||||
|
self.style_attribute_dependency = false;
|
||||||
|
self.state_dependencies = ElementState::empty();
|
||||||
|
self.mapped_ids.clear();
|
||||||
|
self.selectors_for_cache_revalidation = SelectorMap::new();
|
||||||
|
self.num_selectors = 0;
|
||||||
|
self.num_declarations = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
|
fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
|
||||||
|
@ -1761,8 +1799,8 @@ impl Rule {
|
||||||
|
|
||||||
/// A function to be able to test the revalidation stuff.
|
/// A function to be able to test the revalidation stuff.
|
||||||
pub fn needs_revalidation_for_testing(s: &Selector<SelectorImpl>) -> bool {
|
pub fn needs_revalidation_for_testing(s: &Selector<SelectorImpl>) -> bool {
|
||||||
let mut attribute_dependencies = BloomFilter::new();
|
let mut attribute_dependencies = NonCountingBloomFilter::new();
|
||||||
let mut mapped_ids = BloomFilter::new();
|
let mut mapped_ids = NonCountingBloomFilter::new();
|
||||||
let mut style_attribute_dependency = false;
|
let mut style_attribute_dependency = false;
|
||||||
let mut state_dependencies = ElementState::empty();
|
let mut state_dependencies = ElementState::empty();
|
||||||
let mut visitor = StylistSelectorVisitor {
|
let mut visitor = StylistSelectorVisitor {
|
||||||
|
|
|
@ -3297,7 +3297,7 @@ pub extern "C" fn Servo_StyleSet_GetKeyframesForName(raw_data: RawServoStyleSetB
|
||||||
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
|
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
|
||||||
let name = unsafe { Atom::from(name.as_ref().unwrap().as_str_unchecked()) };
|
let name = unsafe { Atom::from(name.as_ref().unwrap().as_str_unchecked()) };
|
||||||
|
|
||||||
let animation = match data.stylist.animations().get(&name) {
|
let animation = match data.stylist.get_animation(&name) {
|
||||||
Some(animation) => animation,
|
Some(animation) => animation,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue