mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
style: Cache substituted values from shorthand properties during the cascade.
This brings the time down to 1.6ms from 4.8ms on the test-case in the bug. This should be improvable too, but I think this is a nice improvement for regular styling as well. Differential Revision: https://phabricator.services.mozilla.com/D105187
This commit is contained in:
parent
1b18b06186
commit
b0d05d1a5d
4 changed files with 96 additions and 54 deletions
|
@ -10,11 +10,10 @@ use crate::dom::TElement;
|
||||||
use crate::font_metrics::FontMetricsProvider;
|
use crate::font_metrics::FontMetricsProvider;
|
||||||
use crate::logical_geometry::WritingMode;
|
use crate::logical_geometry::WritingMode;
|
||||||
use crate::media_queries::Device;
|
use crate::media_queries::Device;
|
||||||
use crate::properties::{CSSWideKeyword, LonghandId, LonghandIdSet, PropertyFlags};
|
|
||||||
use crate::properties::{ComputedValueFlags, CASCADE_PROPERTY};
|
|
||||||
use crate::properties::{ComputedValues, Importance, StyleBuilder};
|
|
||||||
use crate::properties::{
|
use crate::properties::{
|
||||||
DeclarationImportanceIterator, PropertyDeclaration, PropertyDeclarationId,
|
CSSWideKeyword, ComputedValueFlags, ComputedValues, DeclarationImportanceIterator, Importance,
|
||||||
|
LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationId, PropertyFlags,
|
||||||
|
ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
|
||||||
};
|
};
|
||||||
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
||||||
use crate::rule_tree::StrongRuleNode;
|
use crate::rule_tree::StrongRuleNode;
|
||||||
|
@ -325,10 +324,12 @@ where
|
||||||
|
|
||||||
let using_cached_reset_properties = {
|
let using_cached_reset_properties = {
|
||||||
let mut cascade = Cascade::new(&mut context, cascade_mode);
|
let mut cascade = Cascade::new(&mut context, cascade_mode);
|
||||||
|
let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
|
||||||
|
|
||||||
cascade.apply_properties::<EarlyProperties, _>(
|
cascade.apply_properties::<EarlyProperties, _>(
|
||||||
ApplyResetProperties::Yes,
|
ApplyResetProperties::Yes,
|
||||||
declarations.iter().cloned(),
|
declarations.iter().cloned(),
|
||||||
|
&mut shorthand_cache,
|
||||||
);
|
);
|
||||||
|
|
||||||
cascade.compute_visited_style_if_needed(
|
cascade.compute_visited_style_if_needed(
|
||||||
|
@ -348,7 +349,11 @@ where
|
||||||
ApplyResetProperties::Yes
|
ApplyResetProperties::Yes
|
||||||
};
|
};
|
||||||
|
|
||||||
cascade.apply_properties::<LateProperties, _>(apply_reset, declarations.iter().cloned());
|
cascade.apply_properties::<LateProperties, _>(
|
||||||
|
apply_reset,
|
||||||
|
declarations.iter().cloned(),
|
||||||
|
&mut shorthand_cache,
|
||||||
|
);
|
||||||
|
|
||||||
using_cached_reset_properties
|
using_cached_reset_properties
|
||||||
};
|
};
|
||||||
|
@ -501,10 +506,14 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn substitute_variables_if_needed<'decl>(
|
fn substitute_variables_if_needed<'decl, 'cache>(
|
||||||
&mut self,
|
&mut self,
|
||||||
declaration: &'decl PropertyDeclaration,
|
declaration: &'decl PropertyDeclaration,
|
||||||
) -> Cow<'decl, PropertyDeclaration> {
|
cache: &'cache mut ShorthandsWithPropertyReferencesCache,
|
||||||
|
) -> Cow<'decl, PropertyDeclaration>
|
||||||
|
where
|
||||||
|
'cache: 'decl,
|
||||||
|
{
|
||||||
let declaration = match *declaration {
|
let declaration = match *declaration {
|
||||||
PropertyDeclaration::WithVariables(ref declaration) => declaration,
|
PropertyDeclaration::WithVariables(ref declaration) => declaration,
|
||||||
ref d => return Cow::Borrowed(d),
|
ref d => return Cow::Borrowed(d),
|
||||||
|
@ -536,12 +545,13 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Cow::Owned(declaration.value.substitute_variables(
|
declaration.value.substitute_variables(
|
||||||
declaration.id,
|
declaration.id,
|
||||||
self.context.builder.custom_properties.as_ref(),
|
self.context.builder.custom_properties.as_ref(),
|
||||||
self.context.quirks_mode,
|
self.context.quirks_mode,
|
||||||
self.context.device(),
|
self.context.device(),
|
||||||
))
|
cache,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -559,6 +569,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
&mut self,
|
&mut self,
|
||||||
apply_reset: ApplyResetProperties,
|
apply_reset: ApplyResetProperties,
|
||||||
declarations: I,
|
declarations: I,
|
||||||
|
mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
|
||||||
) where
|
) where
|
||||||
Phase: CascadePhase,
|
Phase: CascadePhase,
|
||||||
I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>,
|
I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>,
|
||||||
|
@ -619,7 +630,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut declaration = self.substitute_variables_if_needed(declaration);
|
let mut declaration =
|
||||||
|
self.substitute_variables_if_needed(declaration, &mut shorthand_cache);
|
||||||
|
|
||||||
// When document colors are disabled, do special handling of
|
// When document colors are disabled, do special handling of
|
||||||
// properties that are marked as ignored in that mode.
|
// properties that are marked as ignored in that mode.
|
||||||
|
|
|
@ -837,6 +837,7 @@ impl PropertyDeclarationBlock {
|
||||||
custom_properties.as_ref(),
|
custom_properties.as_ref(),
|
||||||
QuirksMode::NoQuirks,
|
QuirksMode::NoQuirks,
|
||||||
device,
|
device,
|
||||||
|
&mut Default::default()
|
||||||
)
|
)
|
||||||
.to_css(dest)
|
.to_css(dest)
|
||||||
},
|
},
|
||||||
|
|
|
@ -367,6 +367,7 @@ impl AnimationValue {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PropertyDeclaration::WithVariables(ref declaration) => {
|
PropertyDeclaration::WithVariables(ref declaration) => {
|
||||||
|
let mut cache = Default::default();
|
||||||
let substituted = {
|
let substituted = {
|
||||||
let custom_properties =
|
let custom_properties =
|
||||||
extra_custom_properties.or_else(|| context.style().custom_properties());
|
extra_custom_properties.or_else(|| context.style().custom_properties());
|
||||||
|
@ -376,6 +377,7 @@ impl AnimationValue {
|
||||||
custom_properties,
|
custom_properties,
|
||||||
context.quirks_mode,
|
context.quirks_mode,
|
||||||
context.device(),
|
context.device(),
|
||||||
|
&mut cache,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
return AnimationValue::from_declaration(
|
return AnimationValue::from_declaration(
|
||||||
|
|
|
@ -29,11 +29,11 @@ use crate::context::QuirksMode;
|
||||||
use crate::logical_geometry::WritingMode;
|
use crate::logical_geometry::WritingMode;
|
||||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||||
use crate::computed_value_flags::*;
|
use crate::computed_value_flags::*;
|
||||||
|
use crate::hash::FxHashMap;
|
||||||
use crate::media_queries::Device;
|
use crate::media_queries::Device;
|
||||||
use crate::parser::ParserContext;
|
use crate::parser::ParserContext;
|
||||||
use crate::properties::longhands::system_font::SystemFont;
|
use crate::properties::longhands::system_font::SystemFont;
|
||||||
use crate::selector_parser::PseudoElement;
|
use crate::selector_parser::PseudoElement;
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
|
||||||
#[cfg(feature = "servo")] use servo_config::prefs;
|
#[cfg(feature = "servo")] use servo_config::prefs;
|
||||||
use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
|
use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
|
||||||
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
|
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
|
||||||
|
@ -1634,23 +1634,44 @@ impl ToCss for UnparsedValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A simple cache for properties that come from a shorthand and have variable
|
||||||
|
/// references.
|
||||||
|
///
|
||||||
|
/// This cache works because of the fact that you can't have competing values
|
||||||
|
/// for a given longhand coming from the same shorthand (but note that this is
|
||||||
|
/// why the shorthand needs to be part of the cache key).
|
||||||
|
pub type ShorthandsWithPropertyReferencesCache =
|
||||||
|
FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
|
||||||
|
|
||||||
impl UnparsedValue {
|
impl UnparsedValue {
|
||||||
fn substitute_variables(
|
fn substitute_variables<'cache>(
|
||||||
&self,
|
&self,
|
||||||
longhand_id: LonghandId,
|
longhand_id: LonghandId,
|
||||||
custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
|
custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
) -> PropertyDeclaration {
|
shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
|
||||||
|
) -> Cow<'cache, PropertyDeclaration> {
|
||||||
let invalid_at_computed_value_time = || {
|
let invalid_at_computed_value_time = || {
|
||||||
let keyword = if longhand_id.inherited() {
|
let keyword = if longhand_id.inherited() {
|
||||||
CSSWideKeyword::Inherit
|
CSSWideKeyword::Inherit
|
||||||
} else {
|
} else {
|
||||||
CSSWideKeyword::Initial
|
CSSWideKeyword::Initial
|
||||||
};
|
};
|
||||||
PropertyDeclaration::css_wide_keyword(longhand_id, keyword)
|
Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(shorthand_id) = self.from_shorthand {
|
||||||
|
let key = (shorthand_id, longhand_id);
|
||||||
|
if shorthand_cache.contains_key(&key) {
|
||||||
|
// FIXME: This double lookup should be avoidable, but rustc
|
||||||
|
// doesn't like that, see:
|
||||||
|
//
|
||||||
|
// https://github.com/rust-lang/rust/issues/82146
|
||||||
|
return Cow::Borrowed(&shorthand_cache[&key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let css = match crate::custom_properties::substitute(
|
let css = match crate::custom_properties::substitute(
|
||||||
&self.css,
|
&self.css,
|
||||||
self.first_token_type,
|
self.first_token_type,
|
||||||
|
@ -1685,53 +1706,59 @@ impl UnparsedValue {
|
||||||
let mut input = Parser::new(&mut input);
|
let mut input = Parser::new(&mut input);
|
||||||
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
||||||
if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
|
if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
|
||||||
return PropertyDeclaration::css_wide_keyword(longhand_id, keyword);
|
return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
|
||||||
}
|
}
|
||||||
|
|
||||||
let declaration = input.parse_entirely(|input| {
|
let shorthand = match self.from_shorthand {
|
||||||
match self.from_shorthand {
|
None => {
|
||||||
None => longhand_id.parse_value(&context, input),
|
return match input.parse_entirely(|input| longhand_id.parse_value(&context, input)) {
|
||||||
Some(ShorthandId::All) => {
|
Ok(decl) => Cow::Owned(decl),
|
||||||
// No need to parse the 'all' shorthand as anything other
|
Err(..) => invalid_at_computed_value_time(),
|
||||||
// than a CSS-wide keyword, after variable substitution.
|
|
||||||
Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("all".into())))
|
|
||||||
}
|
}
|
||||||
% for shorthand in data.shorthands_except_all():
|
},
|
||||||
Some(ShorthandId::${shorthand.camel_case}) => {
|
Some(shorthand) => shorthand,
|
||||||
shorthands::${shorthand.ident}::parse_value(&context, input)
|
};
|
||||||
.map(|longhands| {
|
|
||||||
match longhand_id {
|
match shorthand {
|
||||||
<% seen = set() %>
|
ShorthandId::All => {
|
||||||
% for property in shorthand.sub_properties:
|
// No need to parse the 'all' shorthand as anything other
|
||||||
// When animating logical properties, we end up
|
// than a CSS-wide keyword, after variable substitution.
|
||||||
// physicalizing the value during the animation, but
|
return invalid_at_computed_value_time();
|
||||||
// the value still comes from the logical shorthand.
|
}
|
||||||
//
|
% for shorthand in data.shorthands_except_all():
|
||||||
// So we need to handle the physical properties too.
|
ShorthandId::${shorthand.camel_case} => {
|
||||||
% for prop in [property] + property.all_physical_mapped_properties(data):
|
let longhands = match input.parse_entirely(|input| shorthands::${shorthand.ident}::parse_value(&context, input)) {
|
||||||
% if prop.camel_case not in seen:
|
Ok(l) => l,
|
||||||
LonghandId::${prop.camel_case} => {
|
_ => return invalid_at_computed_value_time(),
|
||||||
PropertyDeclaration::${prop.camel_case}(
|
};
|
||||||
longhands.${property.ident}
|
|
||||||
)
|
<% seen = set() %>
|
||||||
}
|
% for property in shorthand.sub_properties:
|
||||||
<% seen.add(prop.camel_case) %>
|
// When animating logical properties, we end up
|
||||||
|
// physicalizing the value during the animation, but the
|
||||||
|
// value still comes from the logical shorthand.
|
||||||
|
//
|
||||||
|
// So we need to handle the physical properties too.
|
||||||
|
% for prop in property.all_physical_mapped_properties(data) + [property]:
|
||||||
|
% if prop.ident not in seen:
|
||||||
|
shorthand_cache.insert(
|
||||||
|
(shorthand, LonghandId::${prop.camel_case}),
|
||||||
|
PropertyDeclaration::${prop.camel_case}(
|
||||||
|
longhands.${property.ident}
|
||||||
|
% if prop.ident != property.ident:
|
||||||
|
.clone()
|
||||||
% endif
|
% endif
|
||||||
% endfor
|
)
|
||||||
% endfor
|
);
|
||||||
<% del seen %>
|
% endif
|
||||||
_ => unreachable!()
|
<% seen.add(prop.ident) %>
|
||||||
}
|
% endfor
|
||||||
})
|
|
||||||
}
|
|
||||||
% endfor
|
% endfor
|
||||||
}
|
}
|
||||||
});
|
% endfor
|
||||||
|
|
||||||
match declaration {
|
|
||||||
Ok(decl) => decl,
|
|
||||||
Err(..) => invalid_at_computed_value_time(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cow::Borrowed(&shorthand_cache[&(shorthand, longhand_id)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue