mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +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
|
@ -122,6 +122,12 @@ impl<T: ?Sized + 'static> PartialEq for NonZeroPtrMut<T> {
|
||||||
|
|
||||||
impl<T: ?Sized + 'static> Eq 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> {
|
pub struct Arc<T: ?Sized + 'static> {
|
||||||
p: NonZeroPtrMut<ArcInner<T>>,
|
p: NonZeroPtrMut<ArcInner<T>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -502,7 +502,9 @@ fn compute_style_for_animation_step(context: &SharedStyleContext,
|
||||||
/* visited_style = */ None,
|
/* visited_style = */ None,
|
||||||
font_metrics_provider,
|
font_metrics_provider,
|
||||||
CascadeFlags::empty(),
|
CascadeFlags::empty(),
|
||||||
context.quirks_mode());
|
context.quirks_mode(),
|
||||||
|
/* rule_cache = */ None,
|
||||||
|
&mut Default::default());
|
||||||
computed
|
computed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ use parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};
|
||||||
#[cfg(feature = "servo")] use parking_lot::RwLock;
|
#[cfg(feature = "servo")] use parking_lot::RwLock;
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
#[cfg(feature = "servo")] use properties::PropertyId;
|
#[cfg(feature = "servo")] use properties::PropertyId;
|
||||||
|
use rule_cache::RuleCache;
|
||||||
use rule_tree::StrongRuleNode;
|
use rule_tree::StrongRuleNode;
|
||||||
use selector_parser::{EAGER_PSEUDO_COUNT, SnapshotMap};
|
use selector_parser::{EAGER_PSEUDO_COUNT, SnapshotMap};
|
||||||
use selectors::matching::ElementSelectorFlags;
|
use selectors::matching::ElementSelectorFlags;
|
||||||
|
@ -685,6 +686,8 @@ impl StackLimitChecker {
|
||||||
pub struct ThreadLocalStyleContext<E: TElement> {
|
pub struct ThreadLocalStyleContext<E: TElement> {
|
||||||
/// A cache to share style among siblings.
|
/// A cache to share style among siblings.
|
||||||
pub sharing_cache: StyleSharingCache<E>,
|
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.
|
/// The bloom filter used to fast-reject selector-matching.
|
||||||
pub bloom_filter: StyleBloom<E>,
|
pub bloom_filter: StyleBloom<E>,
|
||||||
/// A channel on which new animations that have been triggered by style
|
/// 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 {
|
pub fn new(shared: &SharedStyleContext) -> Self {
|
||||||
ThreadLocalStyleContext {
|
ThreadLocalStyleContext {
|
||||||
sharing_cache: StyleSharingCache::new(),
|
sharing_cache: StyleSharingCache::new(),
|
||||||
|
rule_cache: RuleCache::new(),
|
||||||
bloom_filter: StyleBloom::new(),
|
bloom_filter: StyleBloom::new(),
|
||||||
new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(),
|
new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(),
|
||||||
tasks: SequentialTaskList(Vec::new()),
|
tasks: SequentialTaskList(Vec::new()),
|
||||||
|
@ -739,6 +743,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
|
||||||
pub fn new(shared: &SharedStyleContext) -> Self {
|
pub fn new(shared: &SharedStyleContext) -> Self {
|
||||||
ThreadLocalStyleContext {
|
ThreadLocalStyleContext {
|
||||||
sharing_cache: StyleSharingCache::new(),
|
sharing_cache: StyleSharingCache::new(),
|
||||||
|
rule_cache: RuleCache::new(),
|
||||||
bloom_filter: StyleBloom::new(),
|
bloom_filter: StyleBloom::new(),
|
||||||
tasks: SequentialTaskList(Vec::new()),
|
tasks: SequentialTaskList(Vec::new()),
|
||||||
selector_flags: SelectorFlagsMap::new(),
|
selector_flags: SelectorFlagsMap::new(),
|
||||||
|
|
|
@ -23,7 +23,9 @@ use media_queries::MediaType;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
use properties::{ComputedValues, StyleBuilder};
|
use properties::{ComputedValues, StyleBuilder};
|
||||||
use properties::longhands::font_size;
|
use properties::longhands::font_size;
|
||||||
|
use rule_cache::RuleCacheConditions;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
|
||||||
use str::starts_with_ignore_ascii_case;
|
use str::starts_with_ignore_ascii_case;
|
||||||
|
@ -694,6 +696,7 @@ impl Expression {
|
||||||
|
|
||||||
// http://dev.w3.org/csswg/mediaqueries3/#units
|
// http://dev.w3.org/csswg/mediaqueries3/#units
|
||||||
// em units are relative to the initial font-size.
|
// em units are relative to the initial font-size.
|
||||||
|
let mut conditions = RuleCacheConditions::default();
|
||||||
let context = computed::Context {
|
let context = computed::Context {
|
||||||
is_root_element: false,
|
is_root_element: false,
|
||||||
builder: StyleBuilder::for_derived_style(device, default_values, None, None),
|
builder: StyleBuilder::for_derived_style(device, default_values, None, None),
|
||||||
|
@ -703,6 +706,8 @@ impl Expression {
|
||||||
// TODO: pass the correct value here.
|
// TODO: pass the correct value here.
|
||||||
quirks_mode: quirks_mode,
|
quirks_mode: quirks_mode,
|
||||||
for_smil_animation: false,
|
for_smil_animation: false,
|
||||||
|
for_non_inherited_property: None,
|
||||||
|
rule_cache_conditions: RefCell::new(&mut conditions),
|
||||||
};
|
};
|
||||||
|
|
||||||
let required_value = match self.value {
|
let required_value = match self.value {
|
||||||
|
|
|
@ -124,6 +124,7 @@ pub mod matching;
|
||||||
pub mod media_queries;
|
pub mod media_queries;
|
||||||
pub mod parallel;
|
pub mod parallel;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
pub mod rule_cache;
|
||||||
pub mod rule_tree;
|
pub mod rule_tree;
|
||||||
pub mod scoped_tls;
|
pub mod scoped_tls;
|
||||||
pub mod selector_map;
|
pub mod selector_map;
|
||||||
|
|
|
@ -59,5 +59,8 @@ bitflags! {
|
||||||
|
|
||||||
/// Whether the child explicitly inherits any reset property.
|
/// Whether the child explicitly inherits any reset property.
|
||||||
const INHERITS_RESET_STYLE = 1 << 8,
|
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 logical_geometry::WritingMode;
|
||||||
use media_queries::Device;
|
use media_queries::Device;
|
||||||
use properties::animated_properties::TransitionProperty;
|
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::{default_font_size_keyword, longhands, FontComputationData, Importance, LonghandId};
|
||||||
use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
|
use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
|
||||||
use rule_tree::StrongRuleNode;
|
use rule_tree::StrongRuleNode;
|
||||||
|
@ -259,6 +259,11 @@ impl ops::DerefMut for ComputedValues {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputedValuesInner {
|
impl ComputedValuesInner {
|
||||||
|
/// Whether we're a visited style.
|
||||||
|
pub fn is_style_if_visited(&self) -> bool {
|
||||||
|
self.flags.contains(IS_STYLE_IF_VISITED)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_display_contents(&self) -> bool {
|
pub fn is_display_contents(&self) -> bool {
|
||||||
self.get_box().clone_display() == longhands::display::computed_value::T::contents
|
self.get_box().clone_display() == longhands::display::computed_value::T::contents
|
||||||
|
|
|
@ -316,6 +316,13 @@
|
||||||
_ => panic!("entered the wrong cascade_property() implementation"),
|
_ => 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:
|
% if not property.derived_from:
|
||||||
match value {
|
match value {
|
||||||
DeclaredValue::Value(specified_value) => {
|
DeclaredValue::Value(specified_value) => {
|
||||||
|
@ -324,6 +331,10 @@
|
||||||
longhands::system_font::resolve_system_font(sf, context);
|
longhands::system_font::resolve_system_font(sf, context);
|
||||||
}
|
}
|
||||||
% endif
|
% 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:
|
% if property.is_vector:
|
||||||
// In the case of a vector property we want to pass
|
// In the case of a vector property we want to pass
|
||||||
// down an iterator so that this can be computed
|
// down an iterator so that this can be computed
|
||||||
|
@ -375,6 +386,9 @@
|
||||||
CSSWideKeyword::Unset |
|
CSSWideKeyword::Unset |
|
||||||
% endif
|
% endif
|
||||||
CSSWideKeyword::Inherit => {
|
CSSWideKeyword::Inherit => {
|
||||||
|
% if not property.style_struct.inherited:
|
||||||
|
context.rule_cache_conditions.borrow_mut().set_uncacheable();
|
||||||
|
% endif
|
||||||
% if property.ident == "font_size":
|
% if property.ident == "font_size":
|
||||||
longhands::font_size::cascade_inherit_font_size(context);
|
longhands::font_size::cascade_inherit_font_size(context);
|
||||||
% else:
|
% else:
|
||||||
|
|
|
@ -226,10 +226,24 @@ ${helpers.single_keyword("position", "static absolute relative fixed sticky",
|
||||||
let ltr = context.style().writing_mode.is_bidi_ltr();
|
let ltr = context.style().writing_mode.is_bidi_ltr();
|
||||||
// https://drafts.csswg.org/css-logical-props/#float-clear
|
// https://drafts.csswg.org/css-logical-props/#float-clear
|
||||||
match *self {
|
match *self {
|
||||||
SpecifiedValue::inline_start if ltr => computed_value::T::left,
|
SpecifiedValue::inline_start => {
|
||||||
SpecifiedValue::inline_start => computed_value::T::right,
|
context.rule_cache_conditions.borrow_mut()
|
||||||
SpecifiedValue::inline_end if ltr => computed_value::T::right,
|
.set_writing_mode_dependency(context.builder.writing_mode);
|
||||||
SpecifiedValue::inline_end => computed_value::T::left,
|
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():
|
% for value in "none left right".split():
|
||||||
SpecifiedValue::${value} => computed_value::T::${value},
|
SpecifiedValue::${value} => computed_value::T::${value},
|
||||||
% endfor
|
% endfor
|
||||||
|
@ -264,10 +278,24 @@ ${helpers.single_keyword("position", "static absolute relative fixed sticky",
|
||||||
let ltr = context.style().writing_mode.is_bidi_ltr();
|
let ltr = context.style().writing_mode.is_bidi_ltr();
|
||||||
// https://drafts.csswg.org/css-logical-props/#float-clear
|
// https://drafts.csswg.org/css-logical-props/#float-clear
|
||||||
match *self {
|
match *self {
|
||||||
SpecifiedValue::inline_start if ltr => computed_value::T::left,
|
SpecifiedValue::inline_start => {
|
||||||
SpecifiedValue::inline_start => computed_value::T::right,
|
context.rule_cache_conditions.borrow_mut()
|
||||||
SpecifiedValue::inline_end if ltr => computed_value::T::right,
|
.set_writing_mode_dependency(context.builder.writing_mode);
|
||||||
SpecifiedValue::inline_end => computed_value::T::left,
|
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():
|
% for value in "none left right both".split():
|
||||||
SpecifiedValue::${value} => computed_value::T::${value},
|
SpecifiedValue::${value} => computed_value::T::${value},
|
||||||
% endfor
|
% endfor
|
||||||
|
|
|
@ -16,6 +16,7 @@ use smallbitvec::SmallBitVec;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use hash::HashSet;
|
use hash::HashSet;
|
||||||
use std::{fmt, mem, ops};
|
use std::{fmt, mem, ops};
|
||||||
|
use std::cell::RefCell;
|
||||||
#[cfg(feature = "gecko")] use std::ptr;
|
#[cfg(feature = "gecko")] use std::ptr;
|
||||||
|
|
||||||
#[cfg(feature = "servo")] use cssparser::RGBA;
|
#[cfg(feature = "servo")] use cssparser::RGBA;
|
||||||
|
@ -33,6 +34,7 @@ use media_queries::Device;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
use properties::animated_properties::AnimatableLonghand;
|
use properties::animated_properties::AnimatableLonghand;
|
||||||
#[cfg(feature = "gecko")] use properties::longhands::system_font::SystemFont;
|
#[cfg(feature = "gecko")] use properties::longhands::system_font::SystemFont;
|
||||||
|
use rule_cache::{RuleCache, RuleCacheConditions};
|
||||||
use selector_parser::PseudoElement;
|
use selector_parser::PseudoElement;
|
||||||
use selectors::parser::SelectorParseError;
|
use selectors::parser::SelectorParseError;
|
||||||
#[cfg(feature = "servo")] use servo_config::prefs::PREFS;
|
#[cfg(feature = "servo")] use servo_config::prefs::PREFS;
|
||||||
|
@ -45,7 +47,7 @@ use values::generics::text::LineHeight;
|
||||||
use values::computed;
|
use values::computed;
|
||||||
use values::computed::NonNegativeLength;
|
use values::computed::NonNegativeLength;
|
||||||
use rule_tree::{CascadeLevel, StrongRuleNode};
|
use rule_tree::{CascadeLevel, StrongRuleNode};
|
||||||
use self::computed_value_flags::ComputedValueFlags;
|
use self::computed_value_flags::*;
|
||||||
use style_adjuster::StyleAdjuster;
|
use style_adjuster::StyleAdjuster;
|
||||||
#[cfg(feature = "servo")] use values::specified::BorderStyle;
|
#[cfg(feature = "servo")] use values::specified::BorderStyle;
|
||||||
|
|
||||||
|
@ -625,6 +627,36 @@ impl LonghandId {
|
||||||
LonghandId::Direction
|
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.
|
/// An identifier for a given shorthand property.
|
||||||
|
@ -2142,6 +2174,11 @@ impl ComputedValuesInner {
|
||||||
/// Servo for obvious reasons.
|
/// Servo for obvious reasons.
|
||||||
pub fn has_moz_binding(&self) -> bool { false }
|
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.
|
/// Returns whether this style's display value is equal to contents.
|
||||||
///
|
///
|
||||||
/// Since this isn't supported in Servo, this is always false for Servo.
|
/// 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
|
/// The rule node representing the ordered list of rules matched for this
|
||||||
/// node.
|
/// node.
|
||||||
rules: Option<StrongRuleNode>,
|
pub rules: Option<StrongRuleNode>,
|
||||||
|
|
||||||
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
|
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
|
||||||
|
|
||||||
/// The pseudo-element this style will represent.
|
/// 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.
|
/// The writing mode flags.
|
||||||
///
|
///
|
||||||
|
@ -2580,7 +2622,7 @@ impl<'a> StyleBuilder<'a> {
|
||||||
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
|
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
|
||||||
writing_mode: WritingMode,
|
writing_mode: WritingMode,
|
||||||
font_size_keyword: FontComputationData,
|
font_size_keyword: FontComputationData,
|
||||||
flags: ComputedValueFlags,
|
mut flags: ComputedValueFlags,
|
||||||
visited_style: Option<Arc<ComputedValues>>,
|
visited_style: Option<Arc<ComputedValues>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
||||||
|
@ -2602,6 +2644,10 @@ impl<'a> StyleBuilder<'a> {
|
||||||
reset_style
|
reset_style
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if cascade_flags.contains(VISITED_DEPENDENT_ONLY) {
|
||||||
|
flags.insert(IS_STYLE_IF_VISITED);
|
||||||
|
}
|
||||||
|
|
||||||
StyleBuilder {
|
StyleBuilder {
|
||||||
device,
|
device,
|
||||||
parent_style,
|
parent_style,
|
||||||
|
@ -2610,6 +2656,7 @@ impl<'a> StyleBuilder<'a> {
|
||||||
reset_style,
|
reset_style,
|
||||||
pseudo,
|
pseudo,
|
||||||
rules,
|
rules,
|
||||||
|
modified_reset: false,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
writing_mode,
|
writing_mode,
|
||||||
font_size_keyword,
|
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
|
/// Creates a StyleBuilder holding only references to the structs of `s`, in
|
||||||
/// order to create a derived style.
|
/// order to create a derived style.
|
||||||
pub fn for_derived_style(
|
pub fn for_derived_style(
|
||||||
|
@ -2646,6 +2698,7 @@ impl<'a> StyleBuilder<'a> {
|
||||||
inherited_style_ignoring_first_line: inherited_style,
|
inherited_style_ignoring_first_line: inherited_style,
|
||||||
reset_style,
|
reset_style,
|
||||||
pseudo,
|
pseudo,
|
||||||
|
modified_reset: false,
|
||||||
rules: None, // FIXME(emilio): Dubious...
|
rules: None, // FIXME(emilio): Dubious...
|
||||||
custom_properties: style_to_derive_from.custom_properties(),
|
custom_properties: style_to_derive_from.custom_properties(),
|
||||||
writing_mode: style_to_derive_from.writing_mode,
|
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:
|
% for property in data.longhands:
|
||||||
% if property.ident != "font_size":
|
% if property.ident != "font_size":
|
||||||
/// Inherit `${property.ident}` from our parent style.
|
/// Inherit `${property.ident}` from our parent style.
|
||||||
|
@ -2669,11 +2732,13 @@ impl<'a> StyleBuilder<'a> {
|
||||||
% if property.style_struct.inherited:
|
% if property.style_struct.inherited:
|
||||||
self.inherited_style.get_${property.style_struct.name_lower}();
|
self.inherited_style.get_${property.style_struct.name_lower}();
|
||||||
% else:
|
% 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
|
% endif
|
||||||
|
|
||||||
% if not property.style_struct.inherited:
|
% if not property.style_struct.inherited:
|
||||||
self.flags.insert(::properties::computed_value_flags::INHERITS_RESET_STYLE);
|
self.flags.insert(::properties::computed_value_flags::INHERITS_RESET_STYLE);
|
||||||
|
self.modified_reset = true;
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
% if property.ident == "content":
|
% if property.ident == "content":
|
||||||
|
@ -2699,6 +2764,10 @@ impl<'a> StyleBuilder<'a> {
|
||||||
let reset_struct =
|
let reset_struct =
|
||||||
self.reset_style.get_${property.style_struct.name_lower}();
|
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()
|
self.${property.style_struct.ident}.mutate()
|
||||||
.reset_${property.ident}(
|
.reset_${property.ident}(
|
||||||
reset_struct,
|
reset_struct,
|
||||||
|
@ -2715,6 +2784,10 @@ impl<'a> StyleBuilder<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
value: longhands::${property.ident}::computed_value::T
|
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"] %>
|
<% props_need_device = ["content", "list_style_type", "font_variant_alternates"] %>
|
||||||
self.${property.style_struct.ident}.mutate()
|
self.${property.style_struct.ident}.mutate()
|
||||||
.set_${property.ident}(
|
.set_${property.ident}(
|
||||||
|
@ -2778,11 +2851,17 @@ impl<'a> StyleBuilder<'a> {
|
||||||
|
|
||||||
/// Gets a mutable view of the current `${style_struct.name}` style.
|
/// 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} {
|
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()
|
self.${style_struct.ident}.mutate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable view of the current `${style_struct.name}` style.
|
/// 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}> {
|
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()
|
self.${style_struct.ident}.take()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2831,6 +2910,16 @@ impl<'a> StyleBuilder<'a> {
|
||||||
longhands::_moz_top_layer::computed_value::T::top)
|
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.
|
/// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
|
||||||
pub fn build(self) -> Arc<ComputedValues> {
|
pub fn build(self) -> Arc<ComputedValues> {
|
||||||
|
@ -3015,7 +3104,9 @@ pub fn cascade(
|
||||||
visited_style: Option<Arc<ComputedValues>>,
|
visited_style: Option<Arc<ComputedValues>>,
|
||||||
font_metrics_provider: &FontMetricsProvider,
|
font_metrics_provider: &FontMetricsProvider,
|
||||||
flags: CascadeFlags,
|
flags: CascadeFlags,
|
||||||
quirks_mode: QuirksMode
|
quirks_mode: QuirksMode,
|
||||||
|
rule_cache: Option<<&RuleCache>,
|
||||||
|
rule_cache_conditions: &mut RuleCacheConditions,
|
||||||
) -> Arc<ComputedValues> {
|
) -> Arc<ComputedValues> {
|
||||||
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -3075,6 +3166,8 @@ pub fn cascade(
|
||||||
font_metrics_provider,
|
font_metrics_provider,
|
||||||
flags,
|
flags,
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
|
rule_cache,
|
||||||
|
rule_cache_conditions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3093,6 +3186,8 @@ pub fn apply_declarations<'a, F, I>(
|
||||||
font_metrics_provider: &FontMetricsProvider,
|
font_metrics_provider: &FontMetricsProvider,
|
||||||
flags: CascadeFlags,
|
flags: CascadeFlags,
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
|
rule_cache: Option<<&RuleCache>,
|
||||||
|
rule_cache_conditions: &mut RuleCacheConditions,
|
||||||
) -> Arc<ComputedValues>
|
) -> Arc<ComputedValues>
|
||||||
where
|
where
|
||||||
F: Fn() -> I,
|
F: Fn() -> I,
|
||||||
|
@ -3149,11 +3244,13 @@ where
|
||||||
ComputedValueFlags::empty(),
|
ComputedValueFlags::empty(),
|
||||||
visited_style,
|
visited_style,
|
||||||
),
|
),
|
||||||
font_metrics_provider: font_metrics_provider,
|
|
||||||
cached_system_font: None,
|
cached_system_font: None,
|
||||||
in_media_query: false,
|
in_media_query: false,
|
||||||
quirks_mode: quirks_mode,
|
|
||||||
for_smil_animation: false,
|
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();
|
let ignore_colors = !device.use_document_colors();
|
||||||
|
@ -3176,6 +3273,7 @@ where
|
||||||
//
|
//
|
||||||
// To improve i-cache behavior, we outline the individual functions and use
|
// To improve i-cache behavior, we outline the individual functions and use
|
||||||
// virtual dispatch instead.
|
// virtual dispatch instead.
|
||||||
|
let mut apply_reset = true;
|
||||||
% for category_to_cascade_now in ["early", "other"]:
|
% for category_to_cascade_now in ["early", "other"]:
|
||||||
% if category_to_cascade_now == "early":
|
% if category_to_cascade_now == "early":
|
||||||
// Pull these out so that we can compute them in a specific order
|
// 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() {
|
for (declaration, cascade_level) in iter_declarations() {
|
||||||
let mut declaration = match *declaration {
|
let mut declaration = match *declaration {
|
||||||
PropertyDeclaration::WithVariables(id, ref unparsed) => {
|
PropertyDeclaration::WithVariables(id, ref unparsed) => {
|
||||||
|
if !id.inherited() {
|
||||||
|
context.rule_cache_conditions.borrow_mut()
|
||||||
|
.set_uncacheable();
|
||||||
|
}
|
||||||
Cow::Owned(unparsed.substitute_variables(
|
Cow::Owned(unparsed.substitute_variables(
|
||||||
id,
|
id,
|
||||||
&context.builder.custom_properties,
|
&context.builder.custom_properties,
|
||||||
|
@ -3208,6 +3310,10 @@ where
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !apply_reset && !longhand_id.inherited() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// When document colors are disabled, skip properties that are
|
// When document colors are disabled, skip properties that are
|
||||||
// marked as ignored in that mode, if they come from a UA or
|
// marked as ignored in that mode, if they come from a UA or
|
||||||
// user style sheet.
|
// user style sheet.
|
||||||
|
@ -3375,16 +3481,16 @@ where
|
||||||
(CASCADE_PROPERTY[discriminant])(&size, &mut context);
|
(CASCADE_PROPERTY[discriminant])(&size, &mut context);
|
||||||
% endif
|
% 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
|
% endfor
|
||||||
|
|
||||||
let mut builder = context.builder;
|
let mut builder = context.builder;
|
||||||
|
|
||||||
{
|
|
||||||
StyleAdjuster::new(&mut builder)
|
|
||||||
.adjust(layout_parent_style, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
% if product == "gecko":
|
% if product == "gecko":
|
||||||
if let Some(ref mut bg) = builder.get_background_if_mutated() {
|
if let Some(ref mut bg) = builder.get_background_if_mutated() {
|
||||||
bg.fill_arrays();
|
bg.fill_arrays();
|
||||||
|
@ -3404,6 +3510,22 @@ where
|
||||||
}
|
}
|
||||||
% endif
|
% 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()
|
builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
159
components/style/rule_cache.rs
Normal file
159
components/style/rule_cache.rs
Normal file
|
@ -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.
|
/// A strong reference to a rule node.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||||
pub struct StrongRuleNode {
|
pub struct StrongRuleNode {
|
||||||
p: NonZeroPtrMut<RuleNode>,
|
p: NonZeroPtrMut<RuleNode>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,9 @@ use media_queries::MediaType;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
use properties::{ComputedValues, StyleBuilder};
|
use properties::{ComputedValues, StyleBuilder};
|
||||||
use properties::longhands::font_size;
|
use properties::longhands::font_size;
|
||||||
|
use rule_cache::RuleCacheConditions;
|
||||||
use selectors::parser::SelectorParseError;
|
use selectors::parser::SelectorParseError;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
|
||||||
use style_traits::{CSSPixel, DevicePixel, ToCss, ParseError};
|
use style_traits::{CSSPixel, DevicePixel, ToCss, ParseError};
|
||||||
|
@ -244,6 +246,7 @@ pub enum Range<T> {
|
||||||
impl Range<specified::Length> {
|
impl Range<specified::Length> {
|
||||||
fn to_computed_range(&self, device: &Device, quirks_mode: QuirksMode) -> Range<Au> {
|
fn to_computed_range(&self, device: &Device, quirks_mode: QuirksMode) -> Range<Au> {
|
||||||
let default_values = device.default_computed_values();
|
let default_values = device.default_computed_values();
|
||||||
|
let mut conditions = RuleCacheConditions::default();
|
||||||
// http://dev.w3.org/csswg/mediaqueries3/#units
|
// http://dev.w3.org/csswg/mediaqueries3/#units
|
||||||
// em units are relative to the initial font-size.
|
// em units are relative to the initial font-size.
|
||||||
let context = computed::Context {
|
let context = computed::Context {
|
||||||
|
@ -257,6 +260,8 @@ impl Range<specified::Length> {
|
||||||
cached_system_font: None,
|
cached_system_font: None,
|
||||||
quirks_mode: quirks_mode,
|
quirks_mode: quirks_mode,
|
||||||
for_smil_animation: false,
|
for_smil_animation: false,
|
||||||
|
for_non_inherited_property: None,
|
||||||
|
rule_cache_conditions: RefCell::new(&mut conditions),
|
||||||
};
|
};
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
|
|
|
@ -574,10 +574,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let implemented_pseudo = self.element.implemented_pseudo_element();
|
let implemented_pseudo = self.element.implemented_pseudo_element();
|
||||||
|
let pseudo = pseudo.or(implemented_pseudo.as_ref());
|
||||||
|
|
||||||
|
let mut conditions = Default::default();
|
||||||
let values =
|
let values =
|
||||||
cascade(
|
cascade(
|
||||||
self.context.shared.stylist.device(),
|
self.context.shared.stylist.device(),
|
||||||
pseudo.or(implemented_pseudo.as_ref()),
|
pseudo,
|
||||||
rules.unwrap_or(self.context.shared.stylist.rule_tree().root()),
|
rules.unwrap_or(self.context.shared.stylist.rule_tree().root()),
|
||||||
&self.context.shared.guards,
|
&self.context.shared.guards,
|
||||||
parent_style,
|
parent_style,
|
||||||
|
@ -587,8 +590,15 @@ where
|
||||||
&self.context.thread_local.font_metrics_provider,
|
&self.context.thread_local.font_metrics_provider,
|
||||||
cascade_flags,
|
cascade_flags,
|
||||||
self.context.shared.quirks_mode(),
|
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
|
values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,12 @@ use font_metrics::get_metrics_provider_for_product;
|
||||||
use media_queries::Device;
|
use media_queries::Device;
|
||||||
use parser::{ParserContext, ParserErrorContext};
|
use parser::{ParserContext, ParserErrorContext};
|
||||||
use properties::StyleBuilder;
|
use properties::StyleBuilder;
|
||||||
|
use rule_cache::RuleCacheConditions;
|
||||||
use selectors::parser::SelectorParseError;
|
use selectors::parser::SelectorParseError;
|
||||||
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
|
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter::Enumerate;
|
use std::iter::Enumerate;
|
||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
|
@ -707,6 +709,7 @@ impl MaybeNew for ViewportConstraints {
|
||||||
|
|
||||||
let default_values = device.default_computed_values();
|
let default_values = device.default_computed_values();
|
||||||
|
|
||||||
|
let mut conditions = RuleCacheConditions::default();
|
||||||
let context = Context {
|
let context = Context {
|
||||||
is_root_element: false,
|
is_root_element: false,
|
||||||
builder: StyleBuilder::for_derived_style(device, default_values, None, None),
|
builder: StyleBuilder::for_derived_style(device, default_values, None, None),
|
||||||
|
@ -715,6 +718,8 @@ impl MaybeNew for ViewportConstraints {
|
||||||
in_media_query: false,
|
in_media_query: false,
|
||||||
quirks_mode: quirks_mode,
|
quirks_mode: quirks_mode,
|
||||||
for_smil_animation: false,
|
for_smil_animation: false,
|
||||||
|
for_non_inherited_property: None,
|
||||||
|
rule_cache_conditions: RefCell::new(&mut conditions),
|
||||||
};
|
};
|
||||||
|
|
||||||
// DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom'
|
// DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom'
|
||||||
|
|
|
@ -771,6 +771,8 @@ impl Stylist {
|
||||||
font_metrics,
|
font_metrics,
|
||||||
cascade_flags,
|
cascade_flags,
|
||||||
self.quirks_mode,
|
self.quirks_mode,
|
||||||
|
/* rule_cache = */ None,
|
||||||
|
&mut Default::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,6 +989,8 @@ impl Stylist {
|
||||||
font_metrics,
|
font_metrics,
|
||||||
cascade_flags,
|
cascade_flags,
|
||||||
self.quirks_mode,
|
self.quirks_mode,
|
||||||
|
/* rule_cache = */ None,
|
||||||
|
&mut Default::default(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1012,6 +1016,8 @@ impl Stylist {
|
||||||
font_metrics,
|
font_metrics,
|
||||||
cascade_flags,
|
cascade_flags,
|
||||||
self.quirks_mode,
|
self.quirks_mode,
|
||||||
|
/* rule_cache = */ None,
|
||||||
|
&mut Default::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1625,6 +1631,8 @@ impl Stylist {
|
||||||
&metrics,
|
&metrics,
|
||||||
CascadeFlags::empty(),
|
CascadeFlags::empty(),
|
||||||
self.quirks_mode,
|
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))
|
MozLength::LengthOrPercentageOrAuto(lopoa.to_computed_value(context))
|
||||||
}
|
}
|
||||||
specified::MozLength::ExtremumLength(ref ext) => {
|
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())
|
MozLength::ExtremumLength(ext.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,12 @@ use font_metrics::FontMetricsProvider;
|
||||||
use media_queries::Device;
|
use media_queries::Device;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use properties;
|
use properties;
|
||||||
use properties::{ComputedValues, StyleBuilder};
|
use properties::{ComputedValues, LonghandId, StyleBuilder};
|
||||||
|
use rule_cache::RuleCacheConditions;
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::{f32, fmt};
|
use std::{f32, fmt};
|
||||||
|
use std::cell::RefCell;
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
@ -116,6 +118,17 @@ pub struct Context<'a> {
|
||||||
/// This is used to allow certain properties to generate out-of-range
|
/// This is used to allow certain properties to generate out-of-range
|
||||||
/// values, which SMIL allows.
|
/// values, which SMIL allows.
|
||||||
pub for_smil_animation: bool,
|
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> {
|
impl<'a> Context<'a> {
|
||||||
|
|
|
@ -249,18 +249,36 @@ fn convert_nscolor_to_computedcolor(color: nscolor) -> ComputedColor {
|
||||||
impl ToComputedValue for Color {
|
impl ToComputedValue for Color {
|
||||||
type ComputedValue = ComputedColor;
|
type ComputedValue = ComputedColor;
|
||||||
|
|
||||||
fn to_computed_value(&self, _context: &Context) -> ComputedColor {
|
fn to_computed_value(&self, context: &Context) -> ComputedColor {
|
||||||
match *self {
|
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::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")]
|
#[cfg(feature = "gecko")]
|
||||||
Color::System(system) =>
|
Color::System(system) =>
|
||||||
convert_nscolor_to_computedcolor(system.to_computed_value(_context)),
|
convert_nscolor_to_computedcolor(system.to_computed_value(context)),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Color::Special(special) => {
|
Color::Special(special) => {
|
||||||
use self::gecko::SpecialColorKeyword as Keyword;
|
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 {
|
convert_nscolor_to_computedcolor(match special {
|
||||||
Keyword::MozDefaultColor => pres_context.mDefaultColor,
|
Keyword::MozDefaultColor => pres_context.mDefaultColor,
|
||||||
Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
|
Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
|
||||||
|
@ -274,7 +292,7 @@ impl ToComputedValue for Color {
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use gecko::wrapper::GeckoElement;
|
use gecko::wrapper::GeckoElement;
|
||||||
use gecko_bindings::bindings::Gecko_GetBody;
|
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 body = unsafe { Gecko_GetBody(pres_context) }.map(GeckoElement);
|
||||||
let data = body.as_ref().and_then(|wrap| wrap.borrow_data());
|
let data = body.as_ref().and_then(|wrap| wrap.borrow_data());
|
||||||
if let Some(data) = data {
|
if let Some(data) = data {
|
||||||
|
|
|
@ -132,9 +132,18 @@ impl FontRelativeLength {
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
FontRelativeLength::Em(length) => {
|
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)
|
(reference_font_size, length)
|
||||||
},
|
},
|
||||||
FontRelativeLength::Ex(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) {
|
let reference_size = match query_font_metrics(context, reference_font_size) {
|
||||||
FontMetricsQueryResult::Available(metrics) => {
|
FontMetricsQueryResult::Available(metrics) => {
|
||||||
metrics.x_height
|
metrics.x_height
|
||||||
|
@ -152,6 +161,9 @@ impl FontRelativeLength {
|
||||||
(reference_size, length)
|
(reference_size, length)
|
||||||
},
|
},
|
||||||
FontRelativeLength::Ch(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) {
|
let reference_size = match query_font_metrics(context, reference_font_size) {
|
||||||
FontMetricsQueryResult::Available(metrics) => {
|
FontMetricsQueryResult::Available(metrics) => {
|
||||||
metrics.zero_advance_measure
|
metrics.zero_advance_measure
|
||||||
|
|
|
@ -9,6 +9,7 @@ use malloc_size_of::MallocSizeOfOps;
|
||||||
use selectors::Element;
|
use selectors::Element;
|
||||||
use selectors::matching::{MatchingContext, MatchingMode, matches_selector};
|
use selectors::matching::{MatchingContext, MatchingMode, matches_selector};
|
||||||
use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
|
use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::iter;
|
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::{AnimatableLonghand, AnimationValue};
|
||||||
use style::properties::animated_properties::compare_property_priority;
|
use style::properties::animated_properties::compare_property_priority;
|
||||||
use style::properties::parse_one_declaration_into;
|
use style::properties::parse_one_declaration_into;
|
||||||
|
use style::rule_cache::RuleCacheConditions;
|
||||||
use style::rule_tree::{CascadeLevel, StyleSource};
|
use style::rule_tree::{CascadeLevel, StyleSource};
|
||||||
use style::selector_parser::PseudoElementCascadeType;
|
use style::selector_parser::PseudoElementCascadeType;
|
||||||
use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
|
use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
|
||||||
|
@ -3186,6 +3188,7 @@ fn create_context<'a>(
|
||||||
parent_style: Option<&'a ComputedValues>,
|
parent_style: Option<&'a ComputedValues>,
|
||||||
pseudo: Option<&'a PseudoElement>,
|
pseudo: Option<&'a PseudoElement>,
|
||||||
for_smil_animation: bool,
|
for_smil_animation: bool,
|
||||||
|
rule_cache_conditions: &'a mut RuleCacheConditions,
|
||||||
) -> Context<'a> {
|
) -> Context<'a> {
|
||||||
Context {
|
Context {
|
||||||
is_root_element: false,
|
is_root_element: false,
|
||||||
|
@ -3200,6 +3203,8 @@ fn create_context<'a>(
|
||||||
in_media_query: false,
|
in_media_query: false,
|
||||||
quirks_mode: per_doc_data.stylist.quirks_mode(),
|
quirks_mode: per_doc_data.stylist.quirks_mode(),
|
||||||
for_smil_animation,
|
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 parent_style = parent_data.as_ref().map(|d| d.styles.primary()).map(|x| &**x);
|
||||||
|
|
||||||
let pseudo = style.pseudo();
|
let pseudo = style.pseudo();
|
||||||
|
let mut conditions = Default::default();
|
||||||
let mut context = create_context(
|
let mut context = create_context(
|
||||||
&data,
|
&data,
|
||||||
&metrics,
|
&metrics,
|
||||||
|
@ -3276,6 +3282,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis
|
||||||
parent_style,
|
parent_style,
|
||||||
pseudo.as_ref(),
|
pseudo.as_ref(),
|
||||||
/* for_smil_animation = */ false,
|
/* for_smil_animation = */ false,
|
||||||
|
&mut conditions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
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 parent_style = parent_data.as_ref().map(|d| d.styles.primary()).map(|x| &**x);
|
||||||
|
|
||||||
let pseudo = style.pseudo();
|
let pseudo = style.pseudo();
|
||||||
|
let mut conditions = Default::default();
|
||||||
let mut context = create_context(
|
let mut context = create_context(
|
||||||
&data,
|
&data,
|
||||||
&metrics,
|
&metrics,
|
||||||
&style,
|
&style,
|
||||||
parent_style,
|
parent_style,
|
||||||
pseudo.as_ref(),
|
pseudo.as_ref(),
|
||||||
/* for_smil_animation = */ true
|
/* for_smil_animation = */ true,
|
||||||
|
&mut conditions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let default_values = data.default_computed_values();
|
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 parent_style = parent_data.as_ref().map(|d| d.styles.primary()).map(|x| &**x);
|
||||||
|
|
||||||
let pseudo = style.pseudo();
|
let pseudo = style.pseudo();
|
||||||
|
let mut conditions = Default::default();
|
||||||
let mut context = create_context(
|
let mut context = create_context(
|
||||||
&data,
|
&data,
|
||||||
&metrics,
|
&metrics,
|
||||||
style,
|
style,
|
||||||
parent_style,
|
parent_style,
|
||||||
pseudo.as_ref(),
|
pseudo.as_ref(),
|
||||||
/* for_smil_animation = */ false
|
/* for_smil_animation = */ false,
|
||||||
|
&mut conditions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let default_values = data.default_computed_values();
|
let default_values = data.default_computed_values();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue