aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/layout_wrapper.rs9
-rw-r--r--components/style/cache.rs32
-rw-r--r--components/style/context.rs82
-rw-r--r--components/style/dom.rs6
-rw-r--r--components/style/gecko/wrapper.rs9
-rw-r--r--components/style/matching.rs48
6 files changed, 129 insertions, 57 deletions
diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs
index 4b118bdf7f1..e4df1b77ec3 100644
--- a/components/script/layout_wrapper.rs
+++ b/components/script/layout_wrapper.rs
@@ -55,6 +55,7 @@ use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::fmt;
use std::fmt::Debug;
+use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::mem::transmute;
use std::sync::Arc;
@@ -475,6 +476,14 @@ impl<'le> PartialEq for ServoLayoutElement<'le> {
}
}
+impl<'le> Hash for ServoLayoutElement<'le> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.element.hash(state);
+ }
+}
+
+impl<'le> Eq for ServoLayoutElement<'le> {}
+
impl<'le> ServoLayoutElement<'le> {
fn from_layout_js(el: LayoutJS<Element>) -> ServoLayoutElement<'le> {
ServoLayoutElement {
diff --git a/components/style/cache.rs b/components/style/cache.rs
index 2b12c8acf8f..fcdd7c79b8f 100644
--- a/components/style/cache.rs
+++ b/components/style/cache.rs
@@ -6,27 +6,28 @@
#![deny(missing_docs)]
-use std::{iter, slice};
+use std::collections::VecDeque;
+use std::collections::vec_deque;
/// A LRU cache used to store a set of at most `n` elements at the same time.
///
-/// Currently used for the style sharing candidate cache.
+/// The most-recently-used entry is at index zero.
pub struct LRUCache<K> {
- entries: Vec<K>,
+ entries: VecDeque<K>,
cache_size: usize,
}
/// A iterator over the items of the LRU cache.
-pub type LRUCacheIterator<'a, K> = iter::Rev<slice::Iter<'a, K>>;
+pub type LRUCacheIterator<'a, K> = vec_deque::Iter<'a, K>;
/// A iterator over the mutable items of the LRU cache.
-pub type LRUCacheMutIterator<'a, K> = iter::Rev<slice::IterMut<'a, K>>;
+pub type LRUCacheMutIterator<'a, K> = vec_deque::IterMut<'a, K>;
impl<K: PartialEq> LRUCache<K> {
/// Create a new LRU cache with `size` elements at most.
pub fn new(size: usize) -> Self {
LRUCache {
- entries: vec![],
+ entries: VecDeque::with_capacity(size),
cache_size: size,
}
}
@@ -37,32 +38,33 @@ impl<K: PartialEq> LRUCache<K> {
}
#[inline]
- /// Touch a given position, and put it in the last item on the list.
+ /// Touch a given entry, putting it first in the list.
pub fn touch(&mut self, pos: usize) {
let last_index = self.entries.len() - 1;
if pos != last_index {
- let entry = self.entries.remove(pos);
- self.entries.push(entry);
+ let entry = self.entries.remove(pos).unwrap();
+ self.entries.push_front(entry);
}
}
/// Iterate over the contents of this cache, from more to less recently
/// used.
- pub fn iter(&self) -> LRUCacheIterator<K> {
- self.entries.iter().rev()
+ pub fn iter(&self) -> vec_deque::Iter<K> {
+ self.entries.iter()
}
/// Iterate mutably over the contents of this cache.
- pub fn iter_mut(&mut self) -> LRUCacheMutIterator<K> {
- self.entries.iter_mut().rev()
+ pub fn iter_mut(&mut self) -> vec_deque::IterMut<K> {
+ self.entries.iter_mut()
}
/// Insert a given key in the cache.
pub fn insert(&mut self, key: K) {
if self.entries.len() == self.cache_size {
- self.entries.remove(0);
+ self.entries.pop_back();
}
- self.entries.push(key);
+ self.entries.push_front(key);
+ debug_assert!(self.entries.len() <= self.cache_size);
}
/// Evict all elements from the cache.
diff --git a/components/style/context.rs b/components/style/context.rs
index f33e22a11f3..f89674e8d04 100644
--- a/components/style/context.rs
+++ b/components/style/context.rs
@@ -9,10 +9,12 @@ use animation::{Animation, PropertyAnimation};
use app_units::Au;
use bit_vec::BitVec;
use bloom::StyleBloom;
+use cache::LRUCache;
use data::ElementData;
use dom::{OpaqueNode, TNode, TElement, SendElement};
use error_reporting::ParseErrorReporter;
use euclid::Size2D;
+use fnv::FnvHashMap;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")] use gecko_bindings::structs;
use matching::StyleSharingCandidateCache;
@@ -281,10 +283,8 @@ bitflags! {
/// is used by the style system to queue up work which is not safe to do during
/// the parallel traversal.
pub enum SequentialTask<E: TElement> {
- /// Sets selector flags. This is used when we need to set flags on an
- /// element that we don't have exclusive access to (i.e. the parent).
- SetSelectorFlags(SendElement<E>, ElementSelectorFlags),
-
+ /// Entry to avoid an unused type parameter error on servo.
+ Unused(SendElement<E>),
#[cfg(feature = "gecko")]
/// Marks that we need to update CSS animations, update effect properties of
/// any type of animations after the normal traversal.
@@ -297,9 +297,7 @@ impl<E: TElement> SequentialTask<E> {
use self::SequentialTask::*;
debug_assert!(thread_state::get() == thread_state::LAYOUT);
match self {
- SetSelectorFlags(el, flags) => {
- unsafe { el.set_selector_flags(flags) };
- }
+ Unused(_) => unreachable!(),
#[cfg(feature = "gecko")]
UpdateAnimations(el, pseudo, tasks) => {
unsafe { el.update_animations(pseudo.as_ref(), tasks) };
@@ -307,12 +305,6 @@ impl<E: TElement> SequentialTask<E> {
}
}
- /// Creates a task to set the selector flags on an element.
- pub fn set_selector_flags(el: E, flags: ElementSelectorFlags) -> Self {
- use self::SequentialTask::*;
- SetSelectorFlags(unsafe { SendElement::new(el) }, flags)
- }
-
#[cfg(feature = "gecko")]
/// Creates a task to update various animation state on a given (pseudo-)element.
pub fn update_animations(el: E, pseudo: Option<PseudoElement>,
@@ -322,6 +314,59 @@ impl<E: TElement> SequentialTask<E> {
}
}
+/// Map from Elements to ElementSelectorFlags. Used to defer applying selector
+/// flags until after the traversal.
+pub struct SelectorFlagsMap<E: TElement> {
+ /// The hashmap storing the flags to apply.
+ map: FnvHashMap<SendElement<E>, ElementSelectorFlags>,
+ /// An LRU cache to avoid hashmap lookups, which can be slow if the map
+ /// gets big.
+ cache: LRUCache<(SendElement<E>, ElementSelectorFlags)>,
+}
+
+#[cfg(debug_assertions)]
+impl<E: TElement> Drop for SelectorFlagsMap<E> {
+ fn drop(&mut self) {
+ debug_assert!(self.map.is_empty());
+ }
+}
+
+impl<E: TElement> SelectorFlagsMap<E> {
+ /// Creates a new empty SelectorFlagsMap.
+ pub fn new() -> Self {
+ SelectorFlagsMap {
+ map: FnvHashMap::default(),
+ cache: LRUCache::new(4),
+ }
+ }
+
+ /// Inserts some flags into the map for a given element.
+ pub fn insert_flags(&mut self, element: E, flags: ElementSelectorFlags) {
+ let el = unsafe { SendElement::new(element) };
+ // Check the cache. If the flags have already been noted, we're done.
+ if self.cache.iter().find(|x| x.0 == el)
+ .map_or(ElementSelectorFlags::empty(), |x| x.1)
+ .contains(flags) {
+ return;
+ }
+
+ let f = self.map.entry(el).or_insert(ElementSelectorFlags::empty());
+ *f |= flags;
+
+ // Insert into the cache. We don't worry about duplicate entries,
+ // which lets us avoid reshuffling.
+ self.cache.insert((unsafe { SendElement::new(element) }, *f))
+ }
+
+ /// Applies the flags. Must be called on the main thread.
+ pub fn apply_flags(&mut self) {
+ debug_assert!(thread_state::get() == thread_state::LAYOUT);
+ for (el, flags) in self.map.drain() {
+ unsafe { el.set_selector_flags(flags); }
+ }
+ }
+}
+
/// A thread-local style context.
///
/// This context contains data that needs to be used during restyling, but is
@@ -339,6 +384,11 @@ pub struct ThreadLocalStyleContext<E: TElement> {
/// the rest of the styling is complete. This is useful for infrequently-needed
/// non-threadsafe operations.
pub tasks: Vec<SequentialTask<E>>,
+ /// ElementSelectorFlags that need to be applied after the traversal is
+ /// complete. This map is used in cases where the matching algorithm needs
+ /// to set flags on elements it doesn't have exclusive access to (i.e. other
+ /// than the current element).
+ pub selector_flags: SelectorFlagsMap<E>,
/// Statistics about the traversal.
pub statistics: TraversalStatistics,
/// Information related to the current element, non-None during processing.
@@ -356,6 +406,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
bloom_filter: StyleBloom::new(),
new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(),
tasks: Vec::new(),
+ selector_flags: SelectorFlagsMap::new(),
statistics: TraversalStatistics::default(),
current_element_info: None,
font_metrics_provider: E::FontMetricsProvider::create_from(shared),
@@ -393,9 +444,12 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
impl<E: TElement> Drop for ThreadLocalStyleContext<E> {
fn drop(&mut self) {
debug_assert!(self.current_element_info.is_none());
+ debug_assert!(thread_state::get() == thread_state::LAYOUT);
+
+ // Apply any slow selector flags that need to be set on parents.
+ self.selector_flags.apply_flags();
// Execute any enqueued sequential tasks.
- debug_assert!(thread_state::get() == thread_state::LAYOUT);
for task in self.tasks.drain(..) {
task.execute();
}
diff --git a/components/style/dom.rs b/components/style/dom.rs
index f64c594bd4e..ea2383f409e 100644
--- a/components/style/dom.rs
+++ b/components/style/dom.rs
@@ -20,6 +20,7 @@ use shared_lock::Locked;
use sink::Push;
use std::fmt;
use std::fmt::Debug;
+use std::hash::Hash;
use std::ops::Deref;
use std::sync::Arc;
use stylist::ApplicableDeclarationBlock;
@@ -275,7 +276,8 @@ pub struct AnimationRules(pub Option<Arc<Locked<PropertyDeclarationBlock>>>,
pub Option<Arc<Locked<PropertyDeclarationBlock>>>);
/// The element trait, the main abstraction the style crate acts over.
-pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer {
+pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
+ ElementExt + PresentationalHintsSynthetizer {
/// The concrete node type.
type ConcreteNode: TNode<ConcreteElement = Self>;
@@ -518,7 +520,7 @@ impl<N: TNode> Deref for SendNode<N> {
/// Same reason as for the existence of SendNode, SendElement does the proper
/// things for a given `TElement`.
-#[derive(Debug, PartialEq)]
+#[derive(Debug, Eq, Hash, PartialEq)]
pub struct SendElement<E: TElement>(E);
unsafe impl<E: TElement> Send for SendElement<E> {}
impl<E: TElement> SendElement<E> {
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index 0531384ae32..610686ab6de 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -67,6 +67,7 @@ use shared_lock::Locked;
use sink::Push;
use std::cell::RefCell;
use std::fmt;
+use std::hash::{Hash, Hasher};
use std::ptr;
use std::sync::Arc;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
@@ -705,6 +706,14 @@ impl<'le> PartialEq for GeckoElement<'le> {
}
}
+impl<'le> Eq for GeckoElement<'le> {}
+
+impl<'le> Hash for GeckoElement<'le> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ (self.0 as *const _).hash(state);
+ }
+}
+
impl<'le> PresentationalHintsSynthetizer for GeckoElement<'le> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: Push<ApplicableDeclarationBlock>,
diff --git a/components/style/matching.rs b/components/style/matching.rs
index 58a5c11c742..595f7a10eb9 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -13,7 +13,7 @@ use atomic_refcell::AtomicRefMut;
use bit_vec::BitVec;
use cache::{LRUCache, LRUCacheMutIterator};
use cascade_info::CascadeInfo;
-use context::{CurrentElementInfo, SequentialTask, SharedStyleContext, StyleContext};
+use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
use dom::{AnimationRules, SendElement, TElement, TNode};
use font_metrics::FontMetricsProvider;
@@ -108,7 +108,7 @@ fn element_matches_candidate<E: TElement>(element: &E,
shared: &SharedStyleContext,
bloom: &BloomFilter,
info: &mut CurrentElementInfo,
- tasks: &mut Vec<SequentialTask<E>>)
+ selector_flags_map: &mut SelectorFlagsMap<E>)
-> Result<ComputedStyle, CacheMiss> {
macro_rules! miss {
($miss: ident) => {
@@ -157,7 +157,7 @@ fn element_matches_candidate<E: TElement>(element: &E,
}
if !revalidate(element, candidate, candidate_element,
- shared, bloom, info, tasks) {
+ shared, bloom, info, selector_flags_map) {
miss!(Revalidation)
}
@@ -205,7 +205,7 @@ fn revalidate<E: TElement>(element: &E,
shared: &SharedStyleContext,
bloom: &BloomFilter,
info: &mut CurrentElementInfo,
- tasks: &mut Vec<SequentialTask<E>>)
+ selector_flags_map: &mut SelectorFlagsMap<E>)
-> bool {
// NB: We could avoid matching ancestor selectors entirely (rather than
// just depending on the bloom filter), at the expense of some complexity.
@@ -235,7 +235,7 @@ fn revalidate<E: TElement>(element: &E,
// 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);
+ element.apply_selector_flags(selector_flags_map, el, flags);
};
info.revalidation_match_results =
Some(stylist.match_revalidation_selectors(element, bloom,
@@ -560,9 +560,9 @@ trait PrivateMatchMethods: TElement {
tasks.insert(EFFECT_PROPERTIES);
}
if !tasks.is_empty() {
- let task = SequentialTask::update_animations(self.as_node().as_element().unwrap(),
- pseudo.cloned(),
- tasks);
+ let task = ::context::SequentialTask::update_animations(*self,
+ pseudo.cloned(),
+ tasks);
context.thread_local.tasks.push(task);
}
}
@@ -698,11 +698,11 @@ trait PrivateMatchMethods: TElement {
shared: &SharedStyleContext,
bloom: &BloomFilter,
info: &mut CurrentElementInfo,
- tasks: &mut Vec<SequentialTask<Self>>)
+ selector_flags_map: &mut SelectorFlagsMap<Self>)
-> Result<ComputedStyle, CacheMiss> {
let candidate_element = *candidate.element;
element_matches_candidate(self, candidate, &candidate_element,
- shared, bloom, info, tasks)
+ shared, bloom, info, selector_flags_map)
}
}
@@ -802,9 +802,9 @@ pub trait MatchMethods : TElement {
let mut rule_nodes_changed = false;
let bloom = context.thread_local.bloom_filter.filter();
- let tasks = &mut context.thread_local.tasks;
+ let map = &mut context.thread_local.selector_flags;
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
- self.apply_selector_flags(tasks, element, flags);
+ self.apply_selector_flags(map, element, flags);
};
// Compute the primary rule node.
@@ -844,9 +844,9 @@ pub trait MatchMethods : TElement {
Vec::<ApplicableDeclarationBlock>::with_capacity(16);
let mut rule_nodes_changed = false;
- let tasks = &mut context.thread_local.tasks;
+ let map = &mut context.thread_local.selector_flags;
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
- self.apply_selector_flags(tasks, element, flags);
+ self.apply_selector_flags(map, element, flags);
};
// Borrow the stuff we need here so the borrow checker doesn't get mad
@@ -923,10 +923,10 @@ pub trait MatchMethods : TElement {
///
/// Anyway, let's do the obvious thing for now.
fn apply_selector_flags(&self,
- tasks: &mut Vec<SequentialTask<Self>>,
+ map: &mut SelectorFlagsMap<Self>,
element: &Self,
flags: ElementSelectorFlags) {
- // Apply the selector flags.
+ // Handle flags that apply to the element.
let self_flags = flags.for_self();
if !self_flags.is_empty() {
if element == self {
@@ -942,21 +942,17 @@ pub trait MatchMethods : TElement {
// Instead, we can read them, and post them if necessary as a
// sequential task in order for them to be processed later.
if !element.has_selector_flags(self_flags) {
- let task =
- SequentialTask::set_selector_flags(element.clone(),
- self_flags);
- tasks.push(task);
+ map.insert_flags(*element, self_flags);
}
}
}
+
+ // Handle flags that apply to the parent.
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = element.parent_element() {
- // Avoid the overhead of the SequentialTask if the flags are
- // already set.
if !p.has_selector_flags(parent_flags) {
- let task = SequentialTask::set_selector_flags(p, parent_flags);
- tasks.push(task);
+ map.insert_flags(p, parent_flags);
}
}
}
@@ -1055,7 +1051,7 @@ pub trait MatchMethods : TElement {
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 selector_flags_map = &mut context.thread_local.selector_flags;
let mut should_clear_cache = false;
for (i, candidate) in cache.iter_mut().enumerate() {
let sharing_result =
@@ -1063,7 +1059,7 @@ pub trait MatchMethods : TElement {
&context.shared,
bloom,
current_element_info,
- tasks);
+ selector_flags_map);
match sharing_result {
Ok(shared_style) => {
// Yay, cache hit. Share the style.