/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! A cache from rule node to computed values, in order to cache reset //! properties.
usecrate::logical_geometry::WritingMode; usecrate::properties::{ComputedValues, StyleBuilder}; usecrate::rule_tree::StrongRuleNode; usecrate::selector_parser::PseudoElement; usecrate::shared_lock::StylesheetGuards; usecrate::values::computed::{NonNegativeLength, Zoom}; usecrate::values::specified::color::ColorSchemeFlags; use fxhash::FxHashMap; use servo_arc::Arc; use smallvec::SmallVec;
/// The conditions for caching and matching a style in the rule cache. #[derive(Clone, Debug, Default)] pubstruct RuleCacheConditions {
uncacheable: bool,
font_size: Option<NonNegativeLength>,
line_height: Option<NonNegativeLength>,
writing_mode: Option<WritingMode>,
color_scheme: Option<ColorSchemeFlags>,
}
impl RuleCacheConditions { /// Sets the style as depending in the font-size value. pubfn set_font_size_dependency(&mutself, 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 depending in the line-height value. pubfn set_line_height_dependency(&mutself, line_height: NonNegativeLength) {
debug_assert!(self.line_height.map_or(true, |l| l == line_height)); self.line_height = Some(line_height);
}
/// Sets the style as depending in the color-scheme property value. pubfn set_color_scheme_dependency(&mutself, color_scheme: ColorSchemeFlags) {
debug_assert!(self.color_scheme.map_or(true, |cs| cs == color_scheme)); self.color_scheme = Some(color_scheme);
}
/// Sets the style as uncacheable. pubfn set_uncacheable(&mutself) { self.uncacheable = true;
}
/// Sets the style as depending in the writing-mode value `writing_mode`. pubfn set_writing_mode_dependency(&mutself, 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
}
}
/// A TLS cache from rules matched to computed values. pubstruct RuleCache { // FIXME(emilio): Consider using LRUCache or something like that?
map: FxHashMap<StrongRuleNode, SmallVec<[(CachedConditions, Arc<ComputedValues>); 1]>>,
}
/// Walk the rule tree and return a rule node for using as the key /// for rule cache. /// /// It currently skips animation / style attribute / preshint rules when they don't contain any /// declaration of a reset property. We don't skip other levels because walking the whole /// parent chain can be expensive. /// /// TODO(emilio): Measure this, this was not super-well measured for performance (this was done /// for memory in bug 1427681)... Walking the rule tree might be worth it if we hit the cache /// enough? fn get_rule_node_for_cache<'r>(
guards: &StylesheetGuards, mut rule_node: Option<&'r StrongRuleNode>,
) -> Option<&'r StrongRuleNode> { usecrate::rule_tree::CascadeLevel; whilelet Some(node) = rule_node { let priority = node.cascade_priority(); let cascade_level = priority.cascade_level(); let should_try_to_skip =
cascade_level.is_animation() ||
matches!(cascade_level, CascadeLevel::PresHints) ||
priority.layer_order().is_style_attribute_layer(); if !should_try_to_skip { break;
} iflet Some(source) = node.style_source() { let decls = source.get().read_with(cascade_level.guard(guards)); if decls.contains_any_reset() { break;
}
}
rule_node = node.parent();
}
rule_node
}
/// Finds a node in the properties matched cache. /// /// This needs to receive a `StyleBuilder` with the `early` properties /// already applied. pubfn find(
&self,
guards: &StylesheetGuards,
builder_with_early_props: &StyleBuilder,
) -> Option<&ComputedValues> { // A pseudo-element with property restrictions can result in different // computed values if it's also used for a non-pseudo. if builder_with_early_props
.pseudo
.and_then(|p| p.property_restriction())
.is_some()
{ return None;
}
let rules = builder_with_early_props.rules.as_ref(); let rules = Self::get_rule_node_for_cache(guards, rules)?; let cached_values = self.map.get(rules)?;
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. pubfn insert_if_possible(
&mutself,
guards: &StylesheetGuards,
style: &Arc<ComputedValues>,
pseudo: Option<&PseudoElement>,
conditions: &RuleCacheConditions,
) -> bool { if !conditions.cacheable() { returnfalse;
}
// A pseudo-element with property restrictions can result in different // computed values if it's also used for a non-pseudo. if pseudo.and_then(|p| p.property_restriction()).is_some() { returnfalse;
}
let rules = style.rules.as_ref(); let rules = matchSelf::get_rule_node_for_cache(guards, rules) {
Some(r) => r.clone(),
None => returnfalse,
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.