#![deny(missing_docs)]
use crate::dom::{SendElement, TElement};
use crate::LocalName;
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use selectors::bloom::BloomFilter;
use smallvec::SmallVec;
thread_local! {
static BLOOM_KEY: &'static AtomicRefCell<BloomFilter> = Box::leak(Default::default());
}
pub struct StyleBloom<E: TElement> {
filter: AtomicRefMut<'static, BloomFilter>,
elements: SmallVec<[PushedElement<E>; 16]>,
pushed_hashes: SmallVec<[u32; 64]>,
}
const MEMSET_CLEAR_THRESHOLD: usize = 25;
struct PushedElement<E: TElement> {
element: SendElement<E>,
num_hashes: usize,
}
impl<E: TElement> PushedElement<E> {
fn new(el: E, num_hashes: usize) -> Self {
PushedElement {
element: unsafe { SendElement::new(el) },
num_hashes,
}
}
}
#[inline]
pub fn is_attr_name_excluded_from_filter(name: &LocalName) -> bool {
*name == local_name!("class") || *name == local_name!("id") || *name == local_name!("style")
}
pub fn each_relevant_element_hash<E, F>(element: E, mut f: F)
where
E: TElement,
F: FnMut(u32),
{
f(element.local_name().get_hash());
f(element.namespace().get_hash());
if let Some(id) = element.id() {
f(id.get_hash());
}
element.each_class(|class| f(class.get_hash()));
element.each_attr_name(|name| {
if !is_attr_name_excluded_from_filter(name) {
f(name.get_hash())
}
});
}
impl<E: TElement> Drop for StyleBloom<E> {
fn drop(&mut self) {
self.clear();
}
}
impl<E: TElement> StyleBloom<E> {
#[inline(never)]
pub fn new() -> Self {
let filter = BLOOM_KEY.with(|b| b.borrow_mut());
debug_assert!(
filter.is_zeroed(),
"Forgot to zero the bloom filter last time"
);
StyleBloom {
filter,
elements: Default::default(),
pushed_hashes: Default::default(),
}
}
pub fn filter(&self) -> &BloomFilter {
&*self.filter
}
pub fn push(&mut self, element: E) {
if cfg!(debug_assertions) {
if self.elements.is_empty() {
assert!(element.traversal_parent().is_none());
}
}
self.push_internal(element);
}
fn push_internal(&mut self, element: E) {
let mut count = 0;
each_relevant_element_hash(element, |hash| {
count += 1;
self.filter.insert_hash(hash);
self.pushed_hashes.push(hash);
});
self.elements.push(PushedElement::new(element, count));
}
#[inline]
fn pop(&mut self) -> Option<E> {
let PushedElement {
element,
num_hashes,
} = self.elements.pop()?;
let popped_element = *element;
let mut expected_hashes = vec![];
if cfg!(debug_assertions) {
each_relevant_element_hash(popped_element, |hash| expected_hashes.push(hash));
}
for _ in 0..num_hashes {
let hash = self.pushed_hashes.pop().unwrap();
debug_assert_eq!(expected_hashes.pop().unwrap(), hash);
self.filter.remove_hash(hash);
}
Some(popped_element)
}
pub fn matching_depth(&self) -> usize {
self.elements.len()
}
pub fn clear(&mut self) {
self.elements.clear();
if self.pushed_hashes.len() > MEMSET_CLEAR_THRESHOLD {
self.filter.clear();
self.pushed_hashes.clear();
} else {
for hash in self.pushed_hashes.drain(..) {
self.filter.remove_hash(hash);
}
debug_assert!(self.filter.is_zeroed());
}
}
pub fn rebuild(&mut self, mut element: E) {
self.clear();
let mut parents_to_insert = SmallVec::<[E; 16]>::new();
while let Some(parent) = element.traversal_parent() {
parents_to_insert.push(parent);
element = parent;
}
for parent in parents_to_insert.drain(..).rev() {
self.push(parent);
}
}
pub fn assert_complete(&self, mut element: E) {
if cfg!(debug_assertions) {
let mut checked = 0;
while let Some(parent) = element.traversal_parent() {
assert_eq!(
parent,
*(self.elements[self.elements.len() - 1 - checked].element)
);
element = parent;
checked += 1;
}
assert_eq!(checked, self.elements.len());
}
}
#[inline]
pub fn current_parent(&self) -> Option<E> {
self.elements.last().map(|ref el| *el.element)
}
pub fn insert_parents_recovering(&mut self, element: E, element_depth: usize) {
if self.elements.is_empty() {
self.rebuild(element);
return;
}
let traversal_parent = match element.traversal_parent() {
Some(parent) => parent,
None => {
self.clear();
return;
},
};
if self.current_parent() == Some(traversal_parent) {
return;
}
if element_depth == 0 {
self.clear();
return;
}
debug_assert!(
element_depth != 0,
"We should have already cleared the bloom filter"
);
debug_assert!(!self.elements.is_empty(), "How! We should've just rebuilt!");
let mut current_depth = self.elements.len() - 1;
while current_depth > element_depth - 1 {
self.pop().expect("Emilio is bad at math");
current_depth -= 1;
}
let mut common_parent = traversal_parent;
let mut common_parent_depth = element_depth - 1;
let mut parents_to_insert = SmallVec::<[E; 16]>::new();
while common_parent_depth > current_depth {
parents_to_insert.push(common_parent);
common_parent = common_parent.traversal_parent().expect("We were lied to");
common_parent_depth -= 1;
}
debug_assert_eq!(common_parent_depth, current_depth);
while *(self.elements.last().unwrap().element) != common_parent {
parents_to_insert.push(common_parent);
self.pop().unwrap();
common_parent = match common_parent.traversal_parent() {
Some(parent) => parent,
None => {
debug_assert!(self.elements.is_empty());
if cfg!(feature = "gecko") {
break;
} else {
panic!("should have found a common ancestor");
}
},
}
}
for parent in parents_to_insert.drain(..).rev() {
self.push(parent);
}
debug_assert_eq!(self.elements.len(), element_depth);
}
}