mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
style: Add a TLS-based style struct caching mechanism.
This commit is contained in:
parent
298b1363ff
commit
c34c92e904
18 changed files with 278 additions and 13 deletions
143
components/style/rule_cache.rs
Normal file
143
components/style/rule_cache.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
//! A cache from rule node to computed values, in order to cache reset
|
||||
//! properties.
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use logical_geometry::WritingMode;
|
||||
use properties::{ComputedValues, StyleBuilder};
|
||||
use rule_tree::StrongRuleNode;
|
||||
use servo_arc::Arc;
|
||||
use smallvec::SmallVec;
|
||||
use values::computed::NonNegativeLength;
|
||||
|
||||
/// The conditions for caching and matching a style in the rule cache.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct RuleCacheConditions {
|
||||
uncacheable: bool,
|
||||
font_size: Option<NonNegativeLength>,
|
||||
writing_mode: Option<WritingMode>,
|
||||
}
|
||||
|
||||
impl RuleCacheConditions {
|
||||
/// Sets the style as depending in the font-size value.
|
||||
pub fn set_font_size_dependency(&mut self, font_size: NonNegativeLength) {
|
||||
debug_assert!(self.font_size.map_or(true, |f| f == font_size));
|
||||
self.font_size = Some(font_size);
|
||||
}
|
||||
|
||||
/// Sets the style as uncacheable.
|
||||
pub fn set_uncacheable(&mut self) {
|
||||
self.uncacheable = true;
|
||||
}
|
||||
|
||||
/// Sets the style as depending in the writing-mode value `writing_mode`.
|
||||
pub fn set_writing_mode_dependency(&mut self, writing_mode: WritingMode) {
|
||||
debug_assert!(self.writing_mode.map_or(true, |wm| wm == writing_mode));
|
||||
self.writing_mode = Some(writing_mode);
|
||||
}
|
||||
|
||||
/// Returns whether the current style's reset properties are cacheable.
|
||||
fn cacheable(&self) -> bool {
|
||||
!self.uncacheable
|
||||
}
|
||||
|
||||
/// Returns whether `style` matches the conditions.
|
||||
fn matches(&self, style: &StyleBuilder) -> bool {
|
||||
if self.uncacheable {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(fs) = self.font_size {
|
||||
if style.get_font().clone_font_size() != fs {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(wm) = self.writing_mode {
|
||||
if style.writing_mode != wm {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// A TLS cache from rules matched to computed values.
|
||||
pub struct RuleCache {
|
||||
// FIXME(emilio): Consider using LRUCache or something like that?
|
||||
map: FnvHashMap<StrongRuleNode, SmallVec<[(RuleCacheConditions, Arc<ComputedValues>); 1]>>,
|
||||
}
|
||||
|
||||
impl RuleCache {
|
||||
/// Creates an empty `RuleCache`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: FnvHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds a node in the properties matched cache.
|
||||
///
|
||||
/// This needs to receive a `StyleBuilder` with the `early` properties
|
||||
/// already applied.
|
||||
pub fn find(
|
||||
&self,
|
||||
builder_with_early_props: &StyleBuilder,
|
||||
) -> Option<&ComputedValues> {
|
||||
if builder_with_early_props.is_style_if_visited() {
|
||||
// FIXME(emilio): We can probably do better, does it matter much?
|
||||
return None;
|
||||
}
|
||||
|
||||
let rules = match builder_with_early_props.rules {
|
||||
Some(ref rules) => rules,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
self.map.get(rules).and_then(|cached_values| {
|
||||
for &(ref conditions, ref values) in cached_values.iter() {
|
||||
if conditions.matches(builder_with_early_props) {
|
||||
debug!("Using cached reset style with conditions {:?}", conditions);
|
||||
return Some(&**values)
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
/// Inserts a node into the rules cache if possible.
|
||||
///
|
||||
/// Returns whether the style was inserted into the cache.
|
||||
pub fn insert_if_possible(
|
||||
&mut self,
|
||||
style: &Arc<ComputedValues>,
|
||||
conditions: &RuleCacheConditions,
|
||||
) -> bool {
|
||||
if !conditions.cacheable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if style.is_style_if_visited() {
|
||||
// FIXME(emilio): We can probably do better, does it matter much?
|
||||
return false;
|
||||
}
|
||||
|
||||
let rules = match style.rules {
|
||||
Some(ref r) => r.clone(),
|
||||
None => return false,
|
||||
};
|
||||
|
||||
debug!("Inserting cached reset style with conditions {:?}", conditions);
|
||||
self.map
|
||||
.entry(rules)
|
||||
.or_insert_with(SmallVec::new)
|
||||
.push((conditions.clone(), style.clone()));
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue