mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Auto merge of #18499 - 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. -->
This commit is contained in:
commit
874cb0d9df
21 changed files with 471 additions and 35 deletions
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue