servo: Merge #18499 - style: add a TLS-based cache of reset style structs (from heycam:rule-cache); r=emilio

Rule cache

<!-- Please describe your changes on the following line: -->
This adds a TLS-based cache reset styles structs keyed off rule nodes.  Reviewed in https://bugzilla.mozilla.org/show_bug.cgi?id=1367635 by me and Emilio.

---
<!-- 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. -->

Source-Repo: https://github.com/servo/servo
Source-Revision: 874cb0d9df44e62a78d427f22f234a13227d07f8

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : 94d8df7014ae82e639bb93f209eea4605f7d8964
This commit is contained in:
Cameron McCormack 2017-09-14 04:28:50 -05:00
Родитель e595bb6c11
Коммит e1fb3d1ec0
21 изменённых файлов: 471 добавлений и 35 удалений

Просмотреть файл

@ -122,6 +122,12 @@ impl<T: ?Sized + 'static> PartialEq for NonZeroPtrMut<T> {
impl<T: ?Sized + 'static> Eq for NonZeroPtrMut<T> {}
impl<T: Sized + 'static> Hash for NonZeroPtrMut<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.ptr().hash(state)
}
}
pub struct Arc<T: ?Sized + 'static> {
p: NonZeroPtrMut<ArcInner<T>>,
}

Просмотреть файл

@ -502,7 +502,9 @@ fn compute_style_for_animation_step(context: &SharedStyleContext,
/* visited_style = */ None,
font_metrics_provider,
CascadeFlags::empty(),
context.quirks_mode());
context.quirks_mode(),
/* rule_cache = */ None,
&mut Default::default());
computed
}
}

Просмотреть файл

@ -20,6 +20,7 @@ use parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};
#[cfg(feature = "servo")] use parking_lot::RwLock;
use properties::ComputedValues;
#[cfg(feature = "servo")] use properties::PropertyId;
use rule_cache::RuleCache;
use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, SnapshotMap};
use selectors::matching::ElementSelectorFlags;
@ -685,6 +686,8 @@ impl StackLimitChecker {
pub struct ThreadLocalStyleContext<E: TElement> {
/// A cache to share style among siblings.
pub sharing_cache: StyleSharingCache<E>,
/// A cache from matched properties to elements that match those.
pub rule_cache: RuleCache,
/// The bloom filter used to fast-reject selector-matching.
pub bloom_filter: StyleBloom<E>,
/// A channel on which new animations that have been triggered by style
@ -722,6 +725,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
pub fn new(shared: &SharedStyleContext) -> Self {
ThreadLocalStyleContext {
sharing_cache: StyleSharingCache::new(),
rule_cache: RuleCache::new(),
bloom_filter: StyleBloom::new(),
new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(),
tasks: SequentialTaskList(Vec::new()),
@ -739,6 +743,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
pub fn new(shared: &SharedStyleContext) -> Self {
ThreadLocalStyleContext {
sharing_cache: StyleSharingCache::new(),
rule_cache: RuleCache::new(),
bloom_filter: StyleBloom::new(),
tasks: SequentialTaskList(Vec::new()),
selector_flags: SelectorFlagsMap::new(),

Просмотреть файл

@ -23,7 +23,9 @@ use media_queries::MediaType;
use parser::ParserContext;
use properties::{ComputedValues, StyleBuilder};
use properties::longhands::font_size;
use rule_cache::RuleCacheConditions;
use servo_arc::Arc;
use std::cell::RefCell;
use std::fmt::{self, Write};
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
use str::starts_with_ignore_ascii_case;
@ -694,6 +696,7 @@ impl Expression {
// http://dev.w3.org/csswg/mediaqueries3/#units
// em units are relative to the initial font-size.
let mut conditions = RuleCacheConditions::default();
let context = computed::Context {
is_root_element: false,
builder: StyleBuilder::for_derived_style(device, default_values, None, None),
@ -703,6 +706,8 @@ impl Expression {
// TODO: pass the correct value here.
quirks_mode: quirks_mode,
for_smil_animation: false,
for_non_inherited_property: None,
rule_cache_conditions: RefCell::new(&mut conditions),
};
let required_value = match self.value {

Просмотреть файл

@ -124,6 +124,7 @@ pub mod matching;
pub mod media_queries;
pub mod parallel;
pub mod parser;
pub mod rule_cache;
pub mod rule_tree;
pub mod scoped_tls;
pub mod selector_map;

Просмотреть файл

@ -59,5 +59,8 @@ bitflags! {
/// Whether the child explicitly inherits any reset property.
const INHERITS_RESET_STYLE = 1 << 8,
/// A flag to mark a style which is a visited style.
const IS_STYLE_IF_VISITED = 1 << 9,
}
}

Просмотреть файл

@ -52,7 +52,7 @@ use gecko::values::round_border_to_device_pixels;
use logical_geometry::WritingMode;
use media_queries::Device;
use properties::animated_properties::TransitionProperty;
use properties::computed_value_flags::ComputedValueFlags;
use properties::computed_value_flags::*;
use properties::{default_font_size_keyword, longhands, FontComputationData, Importance, LonghandId};
use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
use rule_tree::StrongRuleNode;
@ -259,6 +259,11 @@ impl ops::DerefMut for ComputedValues {
}
impl ComputedValuesInner {
/// Whether we're a visited style.
pub fn is_style_if_visited(&self) -> bool {
self.flags.contains(IS_STYLE_IF_VISITED)
}
#[inline]
pub fn is_display_contents(&self) -> bool {
self.get_box().clone_display() == longhands::display::computed_value::T::contents

Просмотреть файл

@ -316,6 +316,13 @@
_ => panic!("entered the wrong cascade_property() implementation"),
};
context.for_non_inherited_property =
% if property.style_struct.inherited:
None;
% else:
Some(LonghandId::${property.camel_case});
% endif
% if not property.derived_from:
match value {
DeclaredValue::Value(specified_value) => {
@ -324,6 +331,10 @@
longhands::system_font::resolve_system_font(sf, context);
}
% endif
% if not property.style_struct.inherited and property.logical:
context.rule_cache_conditions.borrow_mut()
.set_writing_mode_dependency(context.builder.writing_mode);
% endif
% if property.is_vector:
// In the case of a vector property we want to pass
// down an iterator so that this can be computed
@ -375,6 +386,9 @@
CSSWideKeyword::Unset |
% endif
CSSWideKeyword::Inherit => {
% if not property.style_struct.inherited:
context.rule_cache_conditions.borrow_mut().set_uncacheable();
% endif
% if property.ident == "font_size":
longhands::font_size::cascade_inherit_font_size(context);
% else:

Просмотреть файл

@ -226,10 +226,24 @@ ${helpers.single_keyword("position", "static absolute relative fixed sticky",
let ltr = context.style().writing_mode.is_bidi_ltr();
// https://drafts.csswg.org/css-logical-props/#float-clear
match *self {
SpecifiedValue::inline_start if ltr => computed_value::T::left,
SpecifiedValue::inline_start => computed_value::T::right,
SpecifiedValue::inline_end if ltr => computed_value::T::right,
SpecifiedValue::inline_end => computed_value::T::left,
SpecifiedValue::inline_start => {
context.rule_cache_conditions.borrow_mut()
.set_writing_mode_dependency(context.builder.writing_mode);
if ltr {
computed_value::T::left
} else {
computed_value::T::right
}
}
SpecifiedValue::inline_end => {
context.rule_cache_conditions.borrow_mut()
.set_writing_mode_dependency(context.builder.writing_mode);
if ltr {
computed_value::T::right
} else {
computed_value::T::left
}
}
% for value in "none left right".split():
SpecifiedValue::${value} => computed_value::T::${value},
% endfor
@ -264,10 +278,24 @@ ${helpers.single_keyword("position", "static absolute relative fixed sticky",
let ltr = context.style().writing_mode.is_bidi_ltr();
// https://drafts.csswg.org/css-logical-props/#float-clear
match *self {
SpecifiedValue::inline_start if ltr => computed_value::T::left,
SpecifiedValue::inline_start => computed_value::T::right,
SpecifiedValue::inline_end if ltr => computed_value::T::right,
SpecifiedValue::inline_end => computed_value::T::left,
SpecifiedValue::inline_start => {
context.rule_cache_conditions.borrow_mut()
.set_writing_mode_dependency(context.builder.writing_mode);
if ltr {
computed_value::T::left
} else {
computed_value::T::right
}
}
SpecifiedValue::inline_end => {
context.rule_cache_conditions.borrow_mut()
.set_writing_mode_dependency(context.builder.writing_mode);
if ltr {
computed_value::T::right
} else {
computed_value::T::left
}
}
% for value in "none left right both".split():
SpecifiedValue::${value} => computed_value::T::${value},
% endfor

Просмотреть файл

@ -16,6 +16,7 @@ use smallbitvec::SmallBitVec;
use std::borrow::Cow;
use hash::HashSet;
use std::{fmt, mem, ops};
use std::cell::RefCell;
#[cfg(feature = "gecko")] use std::ptr;
#[cfg(feature = "servo")] use cssparser::RGBA;
@ -33,6 +34,7 @@ use media_queries::Device;
use parser::ParserContext;
use properties::animated_properties::AnimatableLonghand;
#[cfg(feature = "gecko")] use properties::longhands::system_font::SystemFont;
use rule_cache::{RuleCache, RuleCacheConditions};
use selector_parser::PseudoElement;
use selectors::parser::SelectorParseError;
#[cfg(feature = "servo")] use servo_config::prefs::PREFS;
@ -45,7 +47,7 @@ use values::generics::text::LineHeight;
use values::computed;
use values::computed::NonNegativeLength;
use rule_tree::{CascadeLevel, StrongRuleNode};
use self::computed_value_flags::ComputedValueFlags;
use self::computed_value_flags::*;
use style_adjuster::StyleAdjuster;
#[cfg(feature = "servo")] use values::specified::BorderStyle;
@ -625,6 +627,36 @@ impl LonghandId {
LonghandId::Direction
)
}
/// Whether computed values of this property lossily convert any complex
/// colors into RGBA colors.
///
/// In Gecko, there are some properties still that compute currentcolor
/// down to an RGBA color at computed value time, instead of as
/// `StyleComplexColor`s. For these properties, we must return `false`,
/// so that we correctly avoid caching style data in the rule tree.
pub fn stores_complex_colors_lossily(&self) -> bool {
% if product == "gecko":
matches!(*self,
% for property in data.longhands:
% if property.predefined_type == "RGBAColor":
LonghandId::${property.camel_case} |
% endif
% endfor
LonghandId::BackgroundImage |
LonghandId::BorderImageSource |
LonghandId::BoxShadow |
LonghandId::MaskImage |
LonghandId::MozBorderBottomColors |
LonghandId::MozBorderLeftColors |
LonghandId::MozBorderRightColors |
LonghandId::MozBorderTopColors |
LonghandId::TextShadow
)
% else:
false
% endif
}
}
/// An identifier for a given shorthand property.
@ -2142,6 +2174,11 @@ impl ComputedValuesInner {
/// Servo for obvious reasons.
pub fn has_moz_binding(&self) -> bool { false }
/// Whether we're a visited style.
pub fn is_style_if_visited(&self) -> bool {
self.flags.contains(IS_STYLE_IF_VISITED)
}
/// Returns whether this style's display value is equal to contents.
///
/// Since this isn't supported in Servo, this is always false for Servo.
@ -2544,12 +2581,17 @@ pub struct StyleBuilder<'a> {
/// The rule node representing the ordered list of rules matched for this
/// node.
rules: Option<StrongRuleNode>,
pub rules: Option<StrongRuleNode>,
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
/// The pseudo-element this style will represent.
pseudo: Option<<&'a PseudoElement>,
pub pseudo: Option<<&'a PseudoElement>,
/// Whether we have mutated any reset structs since the the last time
/// `clear_modified_reset` was called. This is used to tell whether the
/// `StyleAdjuster` did any work.
modified_reset: bool,
/// The writing mode flags.
///
@ -2580,7 +2622,7 @@ impl<'a> StyleBuilder<'a> {
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
writing_mode: WritingMode,
font_size_keyword: FontComputationData,
flags: ComputedValueFlags,
mut flags: ComputedValueFlags,
visited_style: Option<Arc<ComputedValues>>,
) -> Self {
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
@ -2602,6 +2644,10 @@ impl<'a> StyleBuilder<'a> {
reset_style
};
if cascade_flags.contains(VISITED_DEPENDENT_ONLY) {
flags.insert(IS_STYLE_IF_VISITED);
}
StyleBuilder {
device,
parent_style,
@ -2610,6 +2656,7 @@ impl<'a> StyleBuilder<'a> {
reset_style,
pseudo,
rules,
modified_reset: false,
custom_properties,
writing_mode,
font_size_keyword,
@ -2625,6 +2672,11 @@ impl<'a> StyleBuilder<'a> {
}
}
/// Whether we're a visited style.
pub fn is_style_if_visited(&self) -> bool {
self.flags.contains(IS_STYLE_IF_VISITED)
}
/// Creates a StyleBuilder holding only references to the structs of `s`, in
/// order to create a derived style.
pub fn for_derived_style(
@ -2646,6 +2698,7 @@ impl<'a> StyleBuilder<'a> {
inherited_style_ignoring_first_line: inherited_style,
reset_style,
pseudo,
modified_reset: false,
rules: None, // FIXME(emilio): Dubious...
custom_properties: style_to_derive_from.custom_properties(),
writing_mode: style_to_derive_from.writing_mode,
@ -2660,6 +2713,16 @@ impl<'a> StyleBuilder<'a> {
}
}
/// Copy the reset properties from `style`.
pub fn copy_reset_from(&mut self, style: &'a ComputedValues) {
% for style_struct in data.active_style_structs():
% if not style_struct.inherited:
self.${style_struct.ident} =
StyleStructRef::Borrowed(style.${style_struct.name_lower}_arc());
% endif
% endfor
}
% for property in data.longhands:
% if property.ident != "font_size":
/// Inherit `${property.ident}` from our parent style.
@ -2669,11 +2732,13 @@ impl<'a> StyleBuilder<'a> {
% if property.style_struct.inherited:
self.inherited_style.get_${property.style_struct.name_lower}();
% else:
self.inherited_style_ignoring_first_line.get_${property.style_struct.name_lower}();
self.inherited_style_ignoring_first_line
.get_${property.style_struct.name_lower}();
% endif
% if not property.style_struct.inherited:
self.flags.insert(::properties::computed_value_flags::INHERITS_RESET_STYLE);
self.modified_reset = true;
% endif
% if property.ident == "content":
@ -2699,6 +2764,10 @@ impl<'a> StyleBuilder<'a> {
let reset_struct =
self.reset_style.get_${property.style_struct.name_lower}();
% if not property.style_struct.inherited:
self.modified_reset = true;
% endif
self.${property.style_struct.ident}.mutate()
.reset_${property.ident}(
reset_struct,
@ -2715,6 +2784,10 @@ impl<'a> StyleBuilder<'a> {
&mut self,
value: longhands::${property.ident}::computed_value::T
) {
% if not property.style_struct.inherited:
self.modified_reset = true;
% endif
<% props_need_device = ["content", "list_style_type", "font_variant_alternates"] %>
self.${property.style_struct.ident}.mutate()
.set_${property.ident}(
@ -2778,11 +2851,17 @@ impl<'a> StyleBuilder<'a> {
/// Gets a mutable view of the current `${style_struct.name}` style.
pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
% if not property.style_struct.inherited:
self.modified_reset = true;
% endif
self.${style_struct.ident}.mutate()
}
/// Gets a mutable view of the current `${style_struct.name}` style.
pub fn take_${style_struct.name_lower}(&mut self) -> UniqueArc<style_structs::${style_struct.name}> {
% if not property.style_struct.inherited:
self.modified_reset = true;
% endif
self.${style_struct.ident}.take()
}
@ -2831,6 +2910,16 @@ impl<'a> StyleBuilder<'a> {
longhands::_moz_top_layer::computed_value::T::top)
}
/// Clears the "have any reset structs been modified" flag.
fn clear_modified_reset(&mut self) {
self.modified_reset = false;
}
/// Returns whether we have mutated any reset structs since the the last
/// time `clear_modified_reset` was called.
fn modified_reset(&self) -> bool {
self.modified_reset
}
/// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
pub fn build(self) -> Arc<ComputedValues> {
@ -3015,7 +3104,9 @@ pub fn cascade(
visited_style: Option<Arc<ComputedValues>>,
font_metrics_provider: &FontMetricsProvider,
flags: CascadeFlags,
quirks_mode: QuirksMode
quirks_mode: QuirksMode,
rule_cache: Option<<&RuleCache>,
rule_cache_conditions: &mut RuleCacheConditions,
) -> Arc<ComputedValues> {
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
#[cfg(feature = "gecko")]
@ -3075,6 +3166,8 @@ pub fn cascade(
font_metrics_provider,
flags,
quirks_mode,
rule_cache,
rule_cache_conditions,
)
}
@ -3093,6 +3186,8 @@ pub fn apply_declarations<'a, F, I>(
font_metrics_provider: &FontMetricsProvider,
flags: CascadeFlags,
quirks_mode: QuirksMode,
rule_cache: Option<<&RuleCache>,
rule_cache_conditions: &mut RuleCacheConditions,
) -> Arc<ComputedValues>
where
F: Fn() -> I,
@ -3149,11 +3244,13 @@ where
ComputedValueFlags::empty(),
visited_style,
),
font_metrics_provider: font_metrics_provider,
cached_system_font: None,
in_media_query: false,
quirks_mode: quirks_mode,
for_smil_animation: false,
for_non_inherited_property: None,
font_metrics_provider,
quirks_mode,
rule_cache_conditions: RefCell::new(rule_cache_conditions),
};
let ignore_colors = !device.use_document_colors();
@ -3176,6 +3273,7 @@ where
//
// To improve i-cache behavior, we outline the individual functions and use
// virtual dispatch instead.
let mut apply_reset = true;
% for category_to_cascade_now in ["early", "other"]:
% if category_to_cascade_now == "early":
// Pull these out so that we can compute them in a specific order
@ -3186,6 +3284,10 @@ where
for (declaration, cascade_level) in iter_declarations() {
let mut declaration = match *declaration {
PropertyDeclaration::WithVariables(id, ref unparsed) => {
if !id.inherited() {
context.rule_cache_conditions.borrow_mut()
.set_uncacheable();
}
Cow::Owned(unparsed.substitute_variables(
id,
&context.builder.custom_properties,
@ -3208,6 +3310,10 @@ where
continue
}
if !apply_reset && !longhand_id.inherited() {
continue;
}
// When document colors are disabled, skip properties that are
// marked as ignored in that mode, if they come from a UA or
// user style sheet.
@ -3375,16 +3481,16 @@ where
(CASCADE_PROPERTY[discriminant])(&size, &mut context);
% endif
}
% endif
if let Some(style) = rule_cache.and_then(|c| c.find(&context.builder)) {
context.builder.copy_reset_from(style);
apply_reset = false;
}
% endif // category == "early"
% endfor
let mut builder = context.builder;
{
StyleAdjuster::new(&mut builder)
.adjust(layout_parent_style, flags);
}
% if product == "gecko":
if let Some(ref mut bg) = builder.get_background_if_mutated() {
bg.fill_arrays();
@ -3404,6 +3510,22 @@ where
}
% endif
builder.clear_modified_reset();
StyleAdjuster::new(&mut builder)
.adjust(layout_parent_style, flags);
if builder.modified_reset() || !apply_reset {
// If we adjusted any reset structs, we can't cache this ComputedValues.
//
// Also, if we re-used existing reset structs, don't bother caching it
// back again. (Aside from being wasted effort, it will be wrong, since
// context.rule_cache_conditions won't be set appropriately if we
// didn't compute those reset properties.)
context.rule_cache_conditions.borrow_mut()
.set_uncacheable();
}
builder.build()
}

Просмотреть файл

@ -0,0 +1,159 @@
/* 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 selector_parser::PseudoElement;
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;
}
// 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 = 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>,
pseudo: Option<&PseudoElement>,
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;
}
// 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() {
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
}
}

Просмотреть файл

@ -819,7 +819,7 @@ struct WeakRuleNode {
}
/// A strong reference to a rule node.
#[derive(Debug, PartialEq)]
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct StrongRuleNode {
p: NonZeroPtrMut<RuleNode>,
}

Просмотреть файл

@ -13,7 +13,9 @@ use media_queries::MediaType;
use parser::ParserContext;
use properties::{ComputedValues, StyleBuilder};
use properties::longhands::font_size;
use rule_cache::RuleCacheConditions;
use selectors::parser::SelectorParseError;
use std::cell::RefCell;
use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
use style_traits::{CSSPixel, DevicePixel, ToCss, ParseError};
@ -244,6 +246,7 @@ pub enum Range<T> {
impl Range<specified::Length> {
fn to_computed_range(&self, device: &Device, quirks_mode: QuirksMode) -> Range<Au> {
let default_values = device.default_computed_values();
let mut conditions = RuleCacheConditions::default();
// http://dev.w3.org/csswg/mediaqueries3/#units
// em units are relative to the initial font-size.
let context = computed::Context {
@ -257,6 +260,8 @@ impl Range<specified::Length> {
cached_system_font: None,
quirks_mode: quirks_mode,
for_smil_animation: false,
for_non_inherited_property: None,
rule_cache_conditions: RefCell::new(&mut conditions),
};
match *self {

Просмотреть файл

@ -574,10 +574,13 @@ where
}
let implemented_pseudo = self.element.implemented_pseudo_element();
let pseudo = pseudo.or(implemented_pseudo.as_ref());
let mut conditions = Default::default();
let values =
cascade(
self.context.shared.stylist.device(),
pseudo.or(implemented_pseudo.as_ref()),
pseudo,
rules.unwrap_or(self.context.shared.stylist.rule_tree().root()),
&self.context.shared.guards,
parent_style,
@ -587,8 +590,15 @@ where
&self.context.thread_local.font_metrics_provider,
cascade_flags,
self.context.shared.quirks_mode(),
Some(&self.context.thread_local.rule_cache),
&mut conditions,
);
self.context
.thread_local
.rule_cache
.insert_if_possible(&values, pseudo, &conditions);
values
}
}

Просмотреть файл

@ -17,10 +17,12 @@ use font_metrics::get_metrics_provider_for_product;
use media_queries::Device;
use parser::{ParserContext, ParserErrorContext};
use properties::StyleBuilder;
use rule_cache::RuleCacheConditions;
use selectors::parser::SelectorParseError;
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use std::ascii::AsciiExt;
use std::borrow::Cow;
use std::cell::RefCell;
use std::fmt;
use std::iter::Enumerate;
use std::str::Chars;
@ -707,6 +709,7 @@ impl MaybeNew for ViewportConstraints {
let default_values = device.default_computed_values();
let mut conditions = RuleCacheConditions::default();
let context = Context {
is_root_element: false,
builder: StyleBuilder::for_derived_style(device, default_values, None, None),
@ -715,6 +718,8 @@ impl MaybeNew for ViewportConstraints {
in_media_query: false,
quirks_mode: quirks_mode,
for_smil_animation: false,
for_non_inherited_property: None,
rule_cache_conditions: RefCell::new(&mut conditions),
};
// DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom'

Просмотреть файл

@ -771,6 +771,8 @@ impl Stylist {
font_metrics,
cascade_flags,
self.quirks_mode,
/* rule_cache = */ None,
&mut Default::default(),
)
}
@ -987,6 +989,8 @@ impl Stylist {
font_metrics,
cascade_flags,
self.quirks_mode,
/* rule_cache = */ None,
&mut Default::default(),
))
} else {
None
@ -1012,6 +1016,8 @@ impl Stylist {
font_metrics,
cascade_flags,
self.quirks_mode,
/* rule_cache = */ None,
&mut Default::default(),
)
}
@ -1625,6 +1631,8 @@ impl Stylist {
&metrics,
CascadeFlags::empty(),
self.quirks_mode,
/* rule_cache = */ None,
&mut Default::default(),
)
}

Просмотреть файл

@ -866,6 +866,10 @@ impl ToComputedValue for specified::MozLength {
MozLength::LengthOrPercentageOrAuto(lopoa.to_computed_value(context))
}
specified::MozLength::ExtremumLength(ref ext) => {
debug_assert!(context.for_non_inherited_property.is_some(),
"should check whether we're a non-inherited property");
context.rule_cache_conditions.borrow_mut()
.set_writing_mode_dependency(context.builder.writing_mode);
MozLength::ExtremumLength(ext.clone())
}
}

Просмотреть файл

@ -11,10 +11,12 @@ use font_metrics::FontMetricsProvider;
use media_queries::Device;
#[cfg(feature = "gecko")]
use properties;
use properties::{ComputedValues, StyleBuilder};
use properties::{ComputedValues, LonghandId, StyleBuilder};
use rule_cache::RuleCacheConditions;
#[cfg(feature = "servo")]
use servo_url::ServoUrl;
use std::{f32, fmt};
use std::cell::RefCell;
#[cfg(feature = "servo")]
use std::sync::Arc;
use style_traits::ToCss;
@ -116,6 +118,17 @@ pub struct Context<'a> {
/// This is used to allow certain properties to generate out-of-range
/// values, which SMIL allows.
pub for_smil_animation: bool,
/// The property we are computing a value for, if it is a non-inherited
/// property. None if we are computed a value for an inherited property
/// or not computing for a property at all (e.g. in a media query
/// evaluation).
pub for_non_inherited_property: Option<LonghandId>,
/// The conditions to cache a rule node on the rule cache.
///
/// FIXME(emilio): Drop the refcell.
pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>,
}
impl<'a> Context<'a> {

Просмотреть файл

@ -249,18 +249,36 @@ fn convert_nscolor_to_computedcolor(color: nscolor) -> ComputedColor {
impl ToComputedValue for Color {
type ComputedValue = ComputedColor;
fn to_computed_value(&self, _context: &Context) -> ComputedColor {
fn to_computed_value(&self, context: &Context) -> ComputedColor {
match *self {
Color::CurrentColor => ComputedColor::currentcolor(),
Color::CurrentColor => {
if let Some(longhand) = context.for_non_inherited_property {
if longhand.stores_complex_colors_lossily() {
context.rule_cache_conditions.borrow_mut()
.set_uncacheable();
}
}
ComputedColor::currentcolor()
}
Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
Color::Complex(ref complex) => *complex,
Color::Complex(ref complex) => {
if complex.foreground_ratio != 0 {
if let Some(longhand) = context.for_non_inherited_property {
if longhand.stores_complex_colors_lossily() {
context.rule_cache_conditions.borrow_mut()
.set_uncacheable();
}
}
}
*complex
}
#[cfg(feature = "gecko")]
Color::System(system) =>
convert_nscolor_to_computedcolor(system.to_computed_value(_context)),
convert_nscolor_to_computedcolor(system.to_computed_value(context)),
#[cfg(feature = "gecko")]
Color::Special(special) => {
use self::gecko::SpecialColorKeyword as Keyword;
let pres_context = _context.device().pres_context();
let pres_context = context.device().pres_context();
convert_nscolor_to_computedcolor(match special {
Keyword::MozDefaultColor => pres_context.mDefaultColor,
Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
@ -274,7 +292,7 @@ impl ToComputedValue for Color {
use dom::TElement;
use gecko::wrapper::GeckoElement;
use gecko_bindings::bindings::Gecko_GetBody;
let pres_context = _context.device().pres_context();
let pres_context = context.device().pres_context();
let body = unsafe { Gecko_GetBody(pres_context) }.map(GeckoElement);
let data = body.as_ref().and_then(|wrap| wrap.borrow_data());
if let Some(data) = data {

Просмотреть файл

@ -132,9 +132,18 @@ impl FontRelativeLength {
match *self {
FontRelativeLength::Em(length) => {
if !matches!(base_size, FontBaseSize::InheritedStyle) {
context.rule_cache_conditions.borrow_mut()
.set_font_size_dependency(
reference_font_size.into()
);
}
(reference_font_size, length)
},
FontRelativeLength::Ex(length) => {
if context.for_non_inherited_property.is_some() {
context.rule_cache_conditions.borrow_mut().set_uncacheable();
}
let reference_size = match query_font_metrics(context, reference_font_size) {
FontMetricsQueryResult::Available(metrics) => {
metrics.x_height
@ -152,6 +161,9 @@ impl FontRelativeLength {
(reference_size, length)
},
FontRelativeLength::Ch(length) => {
if context.for_non_inherited_property.is_some() {
context.rule_cache_conditions.borrow_mut().set_uncacheable();
}
let reference_size = match query_font_metrics(context, reference_font_size) {
FontMetricsQueryResult::Available(metrics) => {
metrics.zero_advance_measure

Просмотреть файл

@ -9,6 +9,7 @@ use malloc_size_of::MallocSizeOfOps;
use selectors::Element;
use selectors::matching::{MatchingContext, MatchingMode, matches_selector};
use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
use std::cell::RefCell;
use std::env;
use std::fmt::Write;
use std::iter;
@ -116,6 +117,7 @@ use style::properties::PROHIBIT_DISPLAY_CONTENTS;
use style::properties::animated_properties::{AnimatableLonghand, AnimationValue};
use style::properties::animated_properties::compare_property_priority;
use style::properties::parse_one_declaration_into;
use style::rule_cache::RuleCacheConditions;
use style::rule_tree::{CascadeLevel, StyleSource};
use style::selector_parser::PseudoElementCascadeType;
use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
@ -3186,6 +3188,7 @@ fn create_context<'a>(
parent_style: Option<&'a ComputedValues>,
pseudo: Option<&'a PseudoElement>,
for_smil_animation: bool,
rule_cache_conditions: &'a mut RuleCacheConditions,
) -> Context<'a> {
Context {
is_root_element: false,
@ -3200,6 +3203,8 @@ fn create_context<'a>(
in_media_query: false,
quirks_mode: per_doc_data.stylist.quirks_mode(),
for_smil_animation,
for_non_inherited_property: None,
rule_cache_conditions: RefCell::new(rule_cache_conditions),
}
}
@ -3269,6 +3274,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis
let parent_style = parent_data.as_ref().map(|d| d.styles.primary()).map(|x| &**x);
let pseudo = style.pseudo();
let mut conditions = Default::default();
let mut context = create_context(
&data,
&metrics,
@ -3276,6 +3282,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis
parent_style,
pseudo.as_ref(),
/* for_smil_animation = */ false,
&mut conditions,
);
let global_style_data = &*GLOBAL_STYLE_DATA;
@ -3356,13 +3363,15 @@ pub extern "C" fn Servo_GetAnimationValues(declarations: RawServoDeclarationBloc
let parent_style = parent_data.as_ref().map(|d| d.styles.primary()).map(|x| &**x);
let pseudo = style.pseudo();
let mut conditions = Default::default();
let mut context = create_context(
&data,
&metrics,
&style,
parent_style,
pseudo.as_ref(),
/* for_smil_animation = */ true
/* for_smil_animation = */ true,
&mut conditions,
);
let default_values = data.default_computed_values();
@ -3392,13 +3401,15 @@ pub extern "C" fn Servo_AnimationValue_Compute(element: RawGeckoElementBorrowed,
let parent_style = parent_data.as_ref().map(|d| d.styles.primary()).map(|x| &**x);
let pseudo = style.pseudo();
let mut conditions = Default::default();
let mut context = create_context(
&data,
&metrics,
style,
parent_style,
pseudo.as_ref(),
/* for_smil_animation = */ false
/* for_smil_animation = */ false,
&mut conditions,
);
let default_values = data.default_computed_values();