mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
Auto merge of #18496 - bholley:2ndpass, r=emilio
do a second pass on the style sharing cache after computing the rule node https://bugzilla.mozilla.org/show_bug.cgi?id=1397976
This commit is contained in:
commit
298b1363ff
9 changed files with 346 additions and 126 deletions
|
@ -313,6 +313,9 @@ pub struct TraversalStatistics {
|
||||||
pub elements_matched: u32,
|
pub elements_matched: u32,
|
||||||
/// The number of cache hits from the StyleSharingCache.
|
/// The number of cache hits from the StyleSharingCache.
|
||||||
pub styles_shared: u32,
|
pub styles_shared: u32,
|
||||||
|
/// The number of styles reused via rule node comparison from the
|
||||||
|
/// StyleSharingCache.
|
||||||
|
pub styles_reused: u32,
|
||||||
/// The number of selectors in the stylist.
|
/// The number of selectors in the stylist.
|
||||||
pub selectors: u32,
|
pub selectors: u32,
|
||||||
/// The number of revalidation selectors.
|
/// The number of revalidation selectors.
|
||||||
|
@ -347,6 +350,7 @@ impl<'a> ops::Add for &'a TraversalStatistics {
|
||||||
elements_styled: self.elements_styled + other.elements_styled,
|
elements_styled: self.elements_styled + other.elements_styled,
|
||||||
elements_matched: self.elements_matched + other.elements_matched,
|
elements_matched: self.elements_matched + other.elements_matched,
|
||||||
styles_shared: self.styles_shared + other.styles_shared,
|
styles_shared: self.styles_shared + other.styles_shared,
|
||||||
|
styles_reused: self.styles_reused + other.styles_reused,
|
||||||
selectors: 0,
|
selectors: 0,
|
||||||
revalidation_selectors: 0,
|
revalidation_selectors: 0,
|
||||||
dependency_selectors: 0,
|
dependency_selectors: 0,
|
||||||
|
@ -374,6 +378,7 @@ impl fmt::Display for TraversalStatistics {
|
||||||
writeln!(f, "[PERF],elements_styled,{}", self.elements_styled)?;
|
writeln!(f, "[PERF],elements_styled,{}", self.elements_styled)?;
|
||||||
writeln!(f, "[PERF],elements_matched,{}", self.elements_matched)?;
|
writeln!(f, "[PERF],elements_matched,{}", self.elements_matched)?;
|
||||||
writeln!(f, "[PERF],styles_shared,{}", self.styles_shared)?;
|
writeln!(f, "[PERF],styles_shared,{}", self.styles_shared)?;
|
||||||
|
writeln!(f, "[PERF],styles_reused,{}", self.styles_reused)?;
|
||||||
writeln!(f, "[PERF],selectors,{}", self.selectors)?;
|
writeln!(f, "[PERF],selectors,{}", self.selectors)?;
|
||||||
writeln!(f, "[PERF],revalidation_selectors,{}", self.revalidation_selectors)?;
|
writeln!(f, "[PERF],revalidation_selectors,{}", self.revalidation_selectors)?;
|
||||||
writeln!(f, "[PERF],dependency_selectors,{}", self.dependency_selectors)?;
|
writeln!(f, "[PERF],dependency_selectors,{}", self.dependency_selectors)?;
|
||||||
|
|
|
@ -17,11 +17,14 @@ use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use shared_lock::StylesheetGuards;
|
use shared_lock::StylesheetGuards;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::mem;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use style_resolver::{PrimaryStyle, ResolvedElementStyles, ResolvedStyle};
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
/// Various flags stored on ElementData.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
flags ElementDataFlags: u8 {
|
pub flags ElementDataFlags: u8 {
|
||||||
/// Whether the styles changed for this restyle.
|
/// Whether the styles changed for this restyle.
|
||||||
const WAS_RESTYLED = 1 << 0,
|
const WAS_RESTYLED = 1 << 0,
|
||||||
/// Whether the last traversal of this element did not do
|
/// Whether the last traversal of this element did not do
|
||||||
|
@ -35,6 +38,13 @@ bitflags! {
|
||||||
const TRAVERSED_WITHOUT_STYLING = 1 << 1,
|
const TRAVERSED_WITHOUT_STYLING = 1 << 1,
|
||||||
/// Whether we reframed/reconstructed any ancestor or self.
|
/// Whether we reframed/reconstructed any ancestor or self.
|
||||||
const ANCESTOR_WAS_RECONSTRUCTED = 1 << 2,
|
const ANCESTOR_WAS_RECONSTRUCTED = 1 << 2,
|
||||||
|
/// Whether the primary style of this element data was reused from another
|
||||||
|
/// element via a rule node comparison. This allows us to differentiate
|
||||||
|
/// between elements that shared styles because they met all the criteria
|
||||||
|
/// of the style sharing cache, compared to elements that reused style
|
||||||
|
/// structs via rule node identity. The former gives us stronger transitive
|
||||||
|
/// guarantees that allows us to apply the style sharing cache to cousins.
|
||||||
|
const PRIMARY_STYLE_REUSED_VIA_RULE_NODE = 1 << 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +210,7 @@ pub struct ElementData {
|
||||||
pub hint: RestyleHint,
|
pub hint: RestyleHint,
|
||||||
|
|
||||||
/// Flags.
|
/// Flags.
|
||||||
flags: ElementDataFlags,
|
pub flags: ElementDataFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The kind of restyle that a single element should do.
|
/// The kind of restyle that a single element should do.
|
||||||
|
@ -264,6 +274,30 @@ impl ElementData {
|
||||||
self.styles.primary.is_some()
|
self.styles.primary.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns this element's styles as resolved styles to use for sharing.
|
||||||
|
pub fn share_styles(&self) -> ResolvedElementStyles {
|
||||||
|
ResolvedElementStyles {
|
||||||
|
primary: self.share_primary_style(),
|
||||||
|
pseudos: self.styles.pseudos.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns this element's primary style as a resolved style to use for sharing.
|
||||||
|
pub fn share_primary_style(&self) -> PrimaryStyle {
|
||||||
|
let primary_is_reused = self.flags.contains(PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
|
||||||
|
PrimaryStyle(ResolvedStyle::new(self.styles.primary().clone(), primary_is_reused))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a new set of styles, returning the old ones.
|
||||||
|
pub fn set_styles(&mut self, new_styles: ResolvedElementStyles) -> ElementStyles {
|
||||||
|
if new_styles.primary.0.reused_via_rule_node {
|
||||||
|
self.flags.insert(PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
|
||||||
|
} else {
|
||||||
|
self.flags.remove(PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
|
||||||
|
}
|
||||||
|
mem::replace(&mut self.styles, new_styles.into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the kind of restyling that we're going to need to do on this
|
/// Returns the kind of restyling that we're going to need to do on this
|
||||||
/// element, based of the stored restyle hint.
|
/// element, based of the stored restyle hint.
|
||||||
pub fn restyle_kind(
|
pub fn restyle_kind(
|
||||||
|
@ -422,6 +456,28 @@ impl ElementData {
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
pub fn skip_applying_damage(&self) -> bool { false }
|
pub fn skip_applying_damage(&self) -> bool { false }
|
||||||
|
|
||||||
|
/// Returns whether it is safe to perform cousin sharing based on the ComputedValues
|
||||||
|
/// identity of the primary style in this ElementData. There are a few subtle things
|
||||||
|
/// to check.
|
||||||
|
///
|
||||||
|
/// First, if a parent element was already styled and we traversed past it without
|
||||||
|
/// restyling it, that may be because our clever invalidation logic was able to prove
|
||||||
|
/// that the styles of that element would remain unchanged despite changes to the id
|
||||||
|
/// or class attributes. However, style sharing relies on the strong guarantee that all
|
||||||
|
/// the classes and ids up the respective parent chains are identical. As such, if we
|
||||||
|
/// skipped styling for one (or both) of the parents on this traversal, we can't share
|
||||||
|
/// styles across cousins. Note that this is a somewhat conservative check. We could
|
||||||
|
/// tighten it by having the invalidation logic explicitly flag elements for which it
|
||||||
|
/// ellided styling.
|
||||||
|
///
|
||||||
|
/// Second, we want to only consider elements whose ComputedValues match due to a hit
|
||||||
|
/// in the style sharing cache, rather than due to the rule-node-based reuse that
|
||||||
|
/// happens later in the styling pipeline. The former gives us the stronger guarantees
|
||||||
|
/// we need for style sharing, the latter does not.
|
||||||
|
pub fn safe_for_cousin_sharing(&self) -> bool {
|
||||||
|
!self.flags.intersects(TRAVERSED_WITHOUT_STYLING | PRIMARY_STYLE_REUSED_VIA_RULE_NODE)
|
||||||
|
}
|
||||||
|
|
||||||
/// Measures memory usage.
|
/// Measures memory usage.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub fn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize {
|
pub fn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use context::{ElementCascadeInputs, SelectorFlagsMap, SharedStyleContext, StyleContext};
|
use context::{ElementCascadeInputs, SelectorFlagsMap, SharedStyleContext, StyleContext};
|
||||||
use data::{ElementData, ElementStyles};
|
use data::ElementData;
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use invalidation::element::restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS};
|
use invalidation::element::restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS};
|
||||||
use invalidation::element::restyle_hints::{RESTYLE_SMIL, RESTYLE_STYLE_ATTRIBUTE};
|
use invalidation::element::restyle_hints::{RESTYLE_SMIL, RESTYLE_STYLE_ATTRIBUTE};
|
||||||
|
@ -18,6 +18,7 @@ use rule_tree::{CascadeLevel, StrongRuleNode};
|
||||||
use selector_parser::{PseudoElement, RestyleDamage};
|
use selector_parser::{PseudoElement, RestyleDamage};
|
||||||
use selectors::matching::ElementSelectorFlags;
|
use selectors::matching::ElementSelectorFlags;
|
||||||
use servo_arc::{Arc, ArcBorrow};
|
use servo_arc::{Arc, ArcBorrow};
|
||||||
|
use style_resolver::ResolvedElementStyles;
|
||||||
use traversal_flags;
|
use traversal_flags;
|
||||||
|
|
||||||
/// Represents the result of comparing an element's old and new style.
|
/// Represents the result of comparing an element's old and new style.
|
||||||
|
@ -156,7 +157,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
StyleResolverForElement::new(*self, context, RuleInclusion::All, PseudoElementResolution::IfApplicable)
|
StyleResolverForElement::new(*self, context, RuleInclusion::All, PseudoElementResolution::IfApplicable)
|
||||||
.cascade_style_and_visited_with_default_parents(inputs);
|
.cascade_style_and_visited_with_default_parents(inputs);
|
||||||
|
|
||||||
Some(style)
|
Some(style.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -530,26 +531,23 @@ pub trait MatchMethods : TElement {
|
||||||
&self,
|
&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
data: &mut ElementData,
|
data: &mut ElementData,
|
||||||
mut new_styles: ElementStyles,
|
mut new_styles: ResolvedElementStyles,
|
||||||
important_rules_changed: bool,
|
important_rules_changed: bool,
|
||||||
) -> ChildCascadeRequirement {
|
) -> ChildCascadeRequirement {
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use dom::TNode;
|
use dom::TNode;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
debug_assert!(new_styles.primary.is_some(), "How did that happen?");
|
|
||||||
|
|
||||||
self.process_animations(
|
self.process_animations(
|
||||||
context,
|
context,
|
||||||
&mut data.styles.primary,
|
&mut data.styles.primary,
|
||||||
&mut new_styles.primary.as_mut().unwrap(),
|
&mut new_styles.primary.0.style,
|
||||||
data.hint,
|
data.hint,
|
||||||
important_rules_changed,
|
important_rules_changed,
|
||||||
);
|
);
|
||||||
|
|
||||||
// First of all, update the styles.
|
// First of all, update the styles.
|
||||||
let old_styles = mem::replace(&mut data.styles, new_styles);
|
let old_styles = data.set_styles(new_styles);
|
||||||
|
|
||||||
// Propagate the "can be fragmented" bit. It would be nice to
|
// Propagate the "can be fragmented" bit. It would be nice to
|
||||||
// encapsulate this better.
|
// encapsulate this better.
|
||||||
|
|
|
@ -1997,6 +1997,13 @@ pub struct ComputedValues {
|
||||||
inner: ComputedValuesInner,
|
inner: ComputedValuesInner,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ComputedValues {
|
||||||
|
/// Returns the visited rules, if applicable.
|
||||||
|
pub fn visited_rules(&self) -> Option<<&StrongRuleNode> {
|
||||||
|
self.visited_style.as_ref().and_then(|s| s.rules.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
impl ComputedValues {
|
impl ComputedValues {
|
||||||
/// Create a new refcounted `ComputedValues`
|
/// Create a new refcounted `ComputedValues`
|
||||||
|
|
|
@ -10,26 +10,30 @@ use Atom;
|
||||||
use bloom::StyleBloom;
|
use bloom::StyleBloom;
|
||||||
use context::{SelectorFlagsMap, SharedStyleContext};
|
use context::{SelectorFlagsMap, SharedStyleContext};
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use servo_arc::Arc;
|
|
||||||
use sharing::{StyleSharingCandidate, StyleSharingTarget};
|
use sharing::{StyleSharingCandidate, StyleSharingTarget};
|
||||||
|
|
||||||
/// Whether styles may be shared across the children of the given parent elements.
|
/// Determines whether a target and a candidate have compatible parents for sharing.
|
||||||
/// This is used to share style across cousins.
|
pub fn parents_allow_sharing<E>(
|
||||||
///
|
target: &mut StyleSharingTarget<E>,
|
||||||
/// Both elements need to be styled already.
|
candidate: &mut StyleSharingCandidate<E>
|
||||||
pub fn can_share_style_across_parents<E>(first: Option<E>, second: Option<E>) -> bool
|
) -> bool
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
{
|
{
|
||||||
let (first, second) = match (first, second) {
|
// If the identity of the parent style isn't equal, we can't share. We check
|
||||||
(Some(f), Some(s)) => (f, s),
|
// this first, because the result is cached.
|
||||||
_ => return false,
|
if target.parent_style_identity() != candidate.parent_style_identity() {
|
||||||
};
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
debug_assert_ne!(first, second);
|
// Siblings can always share.
|
||||||
|
let parent = target.inheritance_parent().unwrap();
|
||||||
let first_data = first.borrow_data().unwrap();
|
let candidate_parent = candidate.element.inheritance_parent().unwrap();
|
||||||
let second_data = second.borrow_data().unwrap();
|
if parent == candidate_parent {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cousins are a bit more complicated.
|
||||||
|
//
|
||||||
// If a parent element was already styled and we traversed past it without
|
// If a parent element was already styled and we traversed past it without
|
||||||
// restyling it, that may be because our clever invalidation logic was able
|
// restyling it, that may be because our clever invalidation logic was able
|
||||||
// to prove that the styles of that element would remain unchanged despite
|
// to prove that the styles of that element would remain unchanged despite
|
||||||
|
@ -40,14 +44,13 @@ pub fn can_share_style_across_parents<E>(first: Option<E>, second: Option<E>) ->
|
||||||
//
|
//
|
||||||
// This is a somewhat conservative check. We could tighten it by having the
|
// This is a somewhat conservative check. We could tighten it by having the
|
||||||
// invalidation logic explicitly flag elements for which it ellided styling.
|
// invalidation logic explicitly flag elements for which it ellided styling.
|
||||||
if first_data.traversed_without_styling() || second_data.traversed_without_styling() {
|
let parent_data = parent.borrow_data().unwrap();
|
||||||
|
let candidate_parent_data = candidate_parent.borrow_data().unwrap();
|
||||||
|
if !parent_data.safe_for_cousin_sharing() || !candidate_parent_data.safe_for_cousin_sharing() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let same_computed_values =
|
true
|
||||||
Arc::ptr_eq(first_data.styles.primary(), second_data.styles.primary());
|
|
||||||
|
|
||||||
same_computed_values
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether two elements have the same same style attribute (by pointer identity).
|
/// Whether two elements have the same same style attribute (by pointer identity).
|
||||||
|
|
|
@ -70,13 +70,13 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||||
use bloom::StyleBloom;
|
use bloom::StyleBloom;
|
||||||
use cache::LRUCache;
|
use cache::LRUCache;
|
||||||
use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
|
use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
|
||||||
use data::ElementStyles;
|
|
||||||
use dom::{TElement, SendElement};
|
use dom::{TElement, SendElement};
|
||||||
use matching::MatchMethods;
|
use matching::MatchMethods;
|
||||||
use owning_ref::OwningHandle;
|
use owning_ref::OwningHandle;
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
|
use rule_tree::StrongRuleNode;
|
||||||
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
|
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
|
||||||
use servo_arc::Arc;
|
use servo_arc::{Arc, NonZeroPtrMut};
|
||||||
use smallbitvec::SmallBitVec;
|
use smallbitvec::SmallBitVec;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -108,6 +108,18 @@ pub enum StyleSharingBehavior {
|
||||||
Disallow,
|
Disallow,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opaque pointer type to compare ComputedValues identities.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct OpaqueComputedValues(NonZeroPtrMut<()>);
|
||||||
|
impl OpaqueComputedValues {
|
||||||
|
fn from(cv: &ComputedValues) -> Self {
|
||||||
|
let p = NonZeroPtrMut::new(cv as *const ComputedValues as *const () as *mut ());
|
||||||
|
OpaqueComputedValues(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eq(&self, cv: &ComputedValues) -> bool { Self::from(cv) == *self }
|
||||||
|
}
|
||||||
|
|
||||||
/// Some data we want to avoid recomputing all the time while trying to share
|
/// Some data we want to avoid recomputing all the time while trying to share
|
||||||
/// style.
|
/// style.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -121,6 +133,9 @@ pub struct ValidationData {
|
||||||
/// The list of presentational attributes of the element.
|
/// The list of presentational attributes of the element.
|
||||||
pres_hints: Option<SmallVec<[ApplicableDeclarationBlock; 5]>>,
|
pres_hints: Option<SmallVec<[ApplicableDeclarationBlock; 5]>>,
|
||||||
|
|
||||||
|
/// The pointer identity of the parent ComputedValues.
|
||||||
|
parent_style_identity: Option<OpaqueComputedValues>,
|
||||||
|
|
||||||
/// The cached result of matching this entry against the revalidation
|
/// The cached result of matching this entry against the revalidation
|
||||||
/// selectors.
|
/// selectors.
|
||||||
revalidation_match_results: Option<SmallBitVec>,
|
revalidation_match_results: Option<SmallBitVec>,
|
||||||
|
@ -167,6 +182,18 @@ impl ValidationData {
|
||||||
&*self.class_list.as_ref().unwrap()
|
&*self.class_list.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get or compute the parent style identity.
|
||||||
|
pub fn parent_style_identity<E>(&mut self, el: E) -> OpaqueComputedValues
|
||||||
|
where E: TElement,
|
||||||
|
{
|
||||||
|
if self.parent_style_identity.is_none() {
|
||||||
|
let parent = el.inheritance_parent().unwrap();
|
||||||
|
self.parent_style_identity =
|
||||||
|
Some(OpaqueComputedValues::from(parent.borrow_data().unwrap().styles.primary()));
|
||||||
|
}
|
||||||
|
self.parent_style_identity.as_ref().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes the revalidation results if needed, and returns it.
|
/// Computes the revalidation results if needed, and returns it.
|
||||||
/// Inline so we know at compile time what bloom_known_valid is.
|
/// Inline so we know at compile time what bloom_known_valid is.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -250,6 +277,11 @@ impl<E: TElement> StyleSharingCandidate<E> {
|
||||||
self.validation_data.pres_hints(self.element)
|
self.validation_data.pres_hints(self.element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the parent style identity.
|
||||||
|
fn parent_style_identity(&mut self) -> OpaqueComputedValues {
|
||||||
|
self.validation_data.parent_style_identity(self.element)
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute the bit vector of revalidation selector match results
|
/// Compute the bit vector of revalidation selector match results
|
||||||
/// for this candidate.
|
/// for this candidate.
|
||||||
fn revalidation_match_results(
|
fn revalidation_match_results(
|
||||||
|
@ -304,6 +336,11 @@ impl<E: TElement> StyleSharingTarget<E> {
|
||||||
self.validation_data.pres_hints(self.element)
|
self.validation_data.pres_hints(self.element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the parent style identity.
|
||||||
|
fn parent_style_identity(&mut self) -> OpaqueComputedValues {
|
||||||
|
self.validation_data.parent_style_identity(self.element)
|
||||||
|
}
|
||||||
|
|
||||||
fn revalidation_match_results(
|
fn revalidation_match_results(
|
||||||
&mut self,
|
&mut self,
|
||||||
stylist: &Stylist,
|
stylist: &Stylist,
|
||||||
|
@ -342,7 +379,7 @@ impl<E: TElement> StyleSharingTarget<E> {
|
||||||
pub fn share_style_if_possible(
|
pub fn share_style_if_possible(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut StyleContext<E>,
|
context: &mut StyleContext<E>,
|
||||||
) -> Option<ElementStyles> {
|
) -> Option<E> {
|
||||||
let cache = &mut context.thread_local.sharing_cache;
|
let cache = &mut context.thread_local.sharing_cache;
|
||||||
let shared_context = &context.shared;
|
let shared_context = &context.shared;
|
||||||
let selector_flags_map = &mut context.thread_local.selector_flags;
|
let selector_flags_map = &mut context.thread_local.selector_flags;
|
||||||
|
@ -393,14 +430,19 @@ impl<Candidate> SharingCacheBase<Candidate> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: TElement> SharingCache<E> {
|
impl<E: TElement> SharingCache<E> {
|
||||||
fn insert(&mut self, el: E, validation_data_holder: &mut StyleSharingTarget<E>) {
|
fn insert(
|
||||||
self.entries.insert(StyleSharingCandidate {
|
&mut self,
|
||||||
element: el,
|
element: E,
|
||||||
validation_data: validation_data_holder.take_validation_data(),
|
validation_data_holder: Option<&mut StyleSharingTarget<E>>,
|
||||||
});
|
) {
|
||||||
|
let validation_data = match validation_data_holder {
|
||||||
|
Some(v) => v.take_validation_data(),
|
||||||
|
None => ValidationData::default(),
|
||||||
|
};
|
||||||
|
self.entries.insert(StyleSharingCandidate { element, validation_data });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup<F>(&mut self, mut is_match: F) -> Option<ElementStyles>
|
fn lookup<F>(&mut self, mut is_match: F) -> Option<E>
|
||||||
where
|
where
|
||||||
F: FnMut(&mut StyleSharingCandidate<E>) -> bool
|
F: FnMut(&mut StyleSharingCandidate<E>) -> bool
|
||||||
{
|
{
|
||||||
|
@ -418,7 +460,7 @@ impl<E: TElement> SharingCache<E> {
|
||||||
self.entries.touch(i);
|
self.entries.touch(i);
|
||||||
let front = self.entries.front_mut().unwrap();
|
let front = self.entries.front_mut().unwrap();
|
||||||
debug_assert!(is_match(front));
|
debug_assert!(is_match(front));
|
||||||
Some(front.element.borrow_data().unwrap().styles.clone())
|
Some(front.element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -507,7 +549,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
pub fn insert_if_possible(&mut self,
|
pub fn insert_if_possible(&mut self,
|
||||||
element: &E,
|
element: &E,
|
||||||
style: &ComputedValues,
|
style: &ComputedValues,
|
||||||
validation_data_holder: &mut StyleSharingTarget<E>,
|
validation_data_holder: Option<&mut StyleSharingTarget<E>>,
|
||||||
dom_depth: usize) {
|
dom_depth: usize) {
|
||||||
let parent = match element.traversal_parent() {
|
let parent = match element.traversal_parent() {
|
||||||
Some(element) => element,
|
Some(element) => element,
|
||||||
|
@ -575,14 +617,14 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
selector_flags_map: &mut SelectorFlagsMap<E>,
|
selector_flags_map: &mut SelectorFlagsMap<E>,
|
||||||
bloom_filter: &StyleBloom<E>,
|
bloom_filter: &StyleBloom<E>,
|
||||||
target: &mut StyleSharingTarget<E>,
|
target: &mut StyleSharingTarget<E>,
|
||||||
) -> Option<ElementStyles> {
|
) -> Option<E> {
|
||||||
if shared_context.options.disable_style_sharing_cache {
|
if shared_context.options.disable_style_sharing_cache {
|
||||||
debug!("{:?} Cannot share style: style sharing cache disabled",
|
debug!("{:?} Cannot share style: style sharing cache disabled",
|
||||||
target.element);
|
target.element);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if target.traversal_parent().is_none() {
|
if target.inheritance_parent().is_none() {
|
||||||
debug!("{:?} Cannot share style: element has no parent",
|
debug!("{:?} Cannot share style: element has no parent",
|
||||||
target.element);
|
target.element);
|
||||||
return None;
|
return None;
|
||||||
|
@ -615,10 +657,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
// share styles and permit sharing across their children. The latter
|
// share styles and permit sharing across their children. The latter
|
||||||
// check allows us to share style between cousins if the parents
|
// check allows us to share style between cousins if the parents
|
||||||
// shared style.
|
// shared style.
|
||||||
let parent = target.traversal_parent();
|
if !checks::parents_allow_sharing(target, candidate) {
|
||||||
let candidate_parent = candidate.element.traversal_parent();
|
|
||||||
if parent != candidate_parent &&
|
|
||||||
!checks::can_share_style_across_parents(parent, candidate_parent) {
|
|
||||||
trace!("Miss: Parent");
|
trace!("Miss: Parent");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -699,4 +738,44 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to find an element in the cache with the given primary rule node and parent.
|
||||||
|
pub fn lookup_by_rules(
|
||||||
|
&mut self,
|
||||||
|
inherited: &ComputedValues,
|
||||||
|
rules: &StrongRuleNode,
|
||||||
|
visited_rules: Option<&StrongRuleNode>,
|
||||||
|
target: E,
|
||||||
|
) -> Option<E> {
|
||||||
|
self.cache_mut().lookup(|candidate| {
|
||||||
|
if !candidate.parent_style_identity().eq(inherited) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let data = candidate.element.borrow_data().unwrap();
|
||||||
|
let style = data.styles.primary();
|
||||||
|
if style.rules.as_ref() != Some(&rules) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if style.visited_rules() != visited_rules {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule nodes and styles are computed independent of the element's
|
||||||
|
// actual visitedness, but at the end of the cascade (in
|
||||||
|
// `adjust_for_visited`) we do store the visitedness as a flag in
|
||||||
|
// style. (This is a subtle change from initial visited work that
|
||||||
|
// landed when computed values were fused, see
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1381635.)
|
||||||
|
// So at the moment, we need to additionally compare visitedness,
|
||||||
|
// since that is not accounted for by rule nodes alone.
|
||||||
|
// FIXME(jryans): This seems like it breaks the constant time
|
||||||
|
// requirements of visited, assuming we get a cache hit on only one
|
||||||
|
// of unvisited vs. visited.
|
||||||
|
if target.is_visited_link() != candidate.element.is_visited_link() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,52 @@ struct MatchingResults {
|
||||||
relevant_link_found: bool,
|
relevant_link_found: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The primary style of an element or an element-backed pseudo-element.
|
/// A style returned from the resolver machinery.
|
||||||
pub struct PrimaryStyle {
|
pub struct ResolvedStyle {
|
||||||
/// The style per se.
|
/// The style itself.
|
||||||
pub style: Arc<ComputedValues>,
|
pub style: Arc<ComputedValues>,
|
||||||
|
/// Whether the style was reused from another element via the rule node.
|
||||||
|
pub reused_via_rule_node: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The primary style of an element or an element-backed pseudo-element.
|
||||||
|
pub struct PrimaryStyle(pub ResolvedStyle);
|
||||||
|
|
||||||
|
/// A set of style returned from the resolver machinery.
|
||||||
|
pub struct ResolvedElementStyles {
|
||||||
|
/// Primary style.
|
||||||
|
pub primary: PrimaryStyle,
|
||||||
|
/// Pseudo styles.
|
||||||
|
pub pseudos: EagerPseudoStyles,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResolvedStyle {
|
||||||
|
/// Creates a new ResolvedStyle.
|
||||||
|
pub fn new(style: Arc<ComputedValues>, reused_via_rule_node: bool) -> Self {
|
||||||
|
ResolvedStyle { style, reused_via_rule_node }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryStyle {
|
||||||
|
/// Convenience accessor for the style.
|
||||||
|
pub fn style(&self) -> &ComputedValues {
|
||||||
|
&*self.0.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ResolvedStyle> for Arc<ComputedValues> {
|
||||||
|
fn from(r: ResolvedStyle) -> Arc<ComputedValues> {
|
||||||
|
r.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ResolvedElementStyles> for ElementStyles {
|
||||||
|
fn from(r: ResolvedElementStyles) -> ElementStyles {
|
||||||
|
ElementStyles {
|
||||||
|
primary: Some(r.primary.0.into()),
|
||||||
|
pseudos: r.pseudos,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
|
fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
|
||||||
|
@ -140,41 +182,25 @@ where
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut visited_style = None;
|
PrimaryStyle(
|
||||||
let should_compute_visited_style =
|
self.cascade_style_and_visited(
|
||||||
relevant_link_found ||
|
CascadeInputs {
|
||||||
parent_style.and_then(|s| s.get_visited_style()).is_some();
|
rules: Some(primary_results.rule_node),
|
||||||
|
visited_rules,
|
||||||
let pseudo = self.element.implemented_pseudo_element();
|
},
|
||||||
if should_compute_visited_style {
|
|
||||||
visited_style = Some(self.cascade_style(
|
|
||||||
visited_rules.as_ref().or(Some(&primary_results.rule_node)),
|
|
||||||
/* style_if_visited = */ None,
|
|
||||||
parent_style,
|
parent_style,
|
||||||
layout_parent_style,
|
layout_parent_style,
|
||||||
CascadeVisitedMode::Visited,
|
/* pseudo = */ None,
|
||||||
/* pseudo = */ pseudo.as_ref(),
|
)
|
||||||
));
|
)
|
||||||
}
|
|
||||||
let style = self.cascade_style(
|
|
||||||
Some(&primary_results.rule_node),
|
|
||||||
visited_style,
|
|
||||||
parent_style,
|
|
||||||
layout_parent_style,
|
|
||||||
CascadeVisitedMode::Unvisited,
|
|
||||||
/* pseudo = */ pseudo.as_ref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
PrimaryStyle { style, }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Resolve the style of a given element, and all its eager pseudo-elements.
|
/// Resolve the style of a given element, and all its eager pseudo-elements.
|
||||||
pub fn resolve_style(
|
pub fn resolve_style(
|
||||||
&mut self,
|
&mut self,
|
||||||
parent_style: Option<&ComputedValues>,
|
parent_style: Option<&ComputedValues>,
|
||||||
layout_parent_style: Option<&ComputedValues>,
|
layout_parent_style: Option<&ComputedValues>,
|
||||||
) -> ElementStyles {
|
) -> ResolvedElementStyles {
|
||||||
let primary_style =
|
let primary_style =
|
||||||
self.resolve_primary_style(parent_style, layout_parent_style);
|
self.resolve_primary_style(parent_style, layout_parent_style);
|
||||||
|
|
||||||
|
@ -182,10 +208,10 @@ where
|
||||||
|
|
||||||
if self.element.implemented_pseudo_element().is_none() {
|
if self.element.implemented_pseudo_element().is_none() {
|
||||||
let layout_parent_style_for_pseudo =
|
let layout_parent_style_for_pseudo =
|
||||||
if primary_style.style.is_display_contents() {
|
if primary_style.style().is_display_contents() {
|
||||||
layout_parent_style
|
layout_parent_style
|
||||||
} else {
|
} else {
|
||||||
Some(&*primary_style.style)
|
Some(primary_style.style())
|
||||||
};
|
};
|
||||||
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||||
let pseudo_style = self.resolve_pseudo_style(
|
let pseudo_style = self.resolve_pseudo_style(
|
||||||
|
@ -204,16 +230,15 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementStyles {
|
ResolvedElementStyles {
|
||||||
// FIXME(emilio): Remove the Option<>.
|
primary: primary_style,
|
||||||
primary: Some(primary_style.style),
|
|
||||||
pseudos: pseudo_styles,
|
pseudos: pseudo_styles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve an element's styles with the default inheritance parent/layout
|
/// Resolve an element's styles with the default inheritance parent/layout
|
||||||
/// parents.
|
/// parents.
|
||||||
pub fn resolve_style_with_default_parents(&mut self) -> ElementStyles {
|
pub fn resolve_style_with_default_parents(&mut self) -> ResolvedElementStyles {
|
||||||
with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
|
with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
|
||||||
self.resolve_style(parent_style, layout_parent_style)
|
self.resolve_style(parent_style, layout_parent_style)
|
||||||
})
|
})
|
||||||
|
@ -223,7 +248,7 @@ where
|
||||||
pub fn cascade_style_and_visited_with_default_parents(
|
pub fn cascade_style_and_visited_with_default_parents(
|
||||||
&mut self,
|
&mut self,
|
||||||
inputs: CascadeInputs,
|
inputs: CascadeInputs,
|
||||||
) -> Arc<ComputedValues> {
|
) -> ResolvedStyle {
|
||||||
with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
|
with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
|
||||||
self.cascade_style_and_visited(
|
self.cascade_style_and_visited(
|
||||||
inputs,
|
inputs,
|
||||||
|
@ -240,7 +265,28 @@ where
|
||||||
parent_style: Option<&ComputedValues>,
|
parent_style: Option<&ComputedValues>,
|
||||||
layout_parent_style: Option<&ComputedValues>,
|
layout_parent_style: Option<&ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>,
|
pseudo: Option<&PseudoElement>,
|
||||||
) -> Arc<ComputedValues> {
|
) -> ResolvedStyle {
|
||||||
|
// Before doing the cascade, check the sharing cache and see if we can reuse
|
||||||
|
// the style via rule node identity.
|
||||||
|
let may_reuse = pseudo.is_none() && !self.element.is_native_anonymous() &&
|
||||||
|
parent_style.is_some() && inputs.rules.is_some();
|
||||||
|
if may_reuse {
|
||||||
|
let cached = self.context.thread_local.sharing_cache.lookup_by_rules(
|
||||||
|
parent_style.unwrap(),
|
||||||
|
inputs.rules.as_ref().unwrap(),
|
||||||
|
inputs.visited_rules.as_ref(),
|
||||||
|
self.element,
|
||||||
|
);
|
||||||
|
if let Some(el) = cached {
|
||||||
|
self.context.thread_local.statistics.styles_reused += 1;
|
||||||
|
let mut style = el.borrow_data().unwrap().share_primary_style().0;
|
||||||
|
style.reused_via_rule_node = true;
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No style to reuse. Cascade the style, starting with visited style
|
||||||
|
// if necessary.
|
||||||
let mut style_if_visited = None;
|
let mut style_if_visited = None;
|
||||||
if parent_style.map_or(false, |s| s.get_visited_style().is_some()) ||
|
if parent_style.map_or(false, |s| s.get_visited_style().is_some()) ||
|
||||||
inputs.visited_rules.is_some() {
|
inputs.visited_rules.is_some() {
|
||||||
|
@ -253,38 +299,42 @@ where
|
||||||
pseudo,
|
pseudo,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
self.cascade_style(
|
|
||||||
inputs.rules.as_ref(),
|
ResolvedStyle {
|
||||||
style_if_visited,
|
style: self.cascade_style(
|
||||||
parent_style,
|
inputs.rules.as_ref(),
|
||||||
layout_parent_style,
|
style_if_visited,
|
||||||
CascadeVisitedMode::Unvisited,
|
parent_style,
|
||||||
pseudo,
|
layout_parent_style,
|
||||||
)
|
CascadeVisitedMode::Unvisited,
|
||||||
|
pseudo,
|
||||||
|
),
|
||||||
|
reused_via_rule_node: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cascade the element and pseudo-element styles with the default parents.
|
/// Cascade the element and pseudo-element styles with the default parents.
|
||||||
pub fn cascade_styles_with_default_parents(
|
pub fn cascade_styles_with_default_parents(
|
||||||
&mut self,
|
&mut self,
|
||||||
inputs: ElementCascadeInputs,
|
inputs: ElementCascadeInputs,
|
||||||
) -> ElementStyles {
|
) -> ResolvedElementStyles {
|
||||||
with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
|
with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
|
||||||
let primary_style = PrimaryStyle {
|
let primary_style = PrimaryStyle(
|
||||||
style: self.cascade_style_and_visited(
|
self.cascade_style_and_visited(
|
||||||
inputs.primary,
|
inputs.primary,
|
||||||
parent_style,
|
parent_style,
|
||||||
layout_parent_style,
|
layout_parent_style,
|
||||||
/* pseudo = */ None,
|
/* pseudo = */ None,
|
||||||
),
|
)
|
||||||
};
|
);
|
||||||
|
|
||||||
let mut pseudo_styles = EagerPseudoStyles::default();
|
let mut pseudo_styles = EagerPseudoStyles::default();
|
||||||
if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
|
if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
|
||||||
let layout_parent_style_for_pseudo =
|
let layout_parent_style_for_pseudo =
|
||||||
if primary_style.style.is_display_contents() {
|
if primary_style.style().is_display_contents() {
|
||||||
layout_parent_style
|
layout_parent_style
|
||||||
} else {
|
} else {
|
||||||
Some(&*primary_style.style)
|
Some(primary_style.style())
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, inputs) in pseudo_array.iter_mut().enumerate() {
|
for (i, inputs) in pseudo_array.iter_mut().enumerate() {
|
||||||
|
@ -294,23 +344,23 @@ where
|
||||||
let style =
|
let style =
|
||||||
self.cascade_style_and_visited(
|
self.cascade_style_and_visited(
|
||||||
inputs,
|
inputs,
|
||||||
Some(&*primary_style.style),
|
Some(&*primary_style.style()),
|
||||||
layout_parent_style_for_pseudo,
|
layout_parent_style_for_pseudo,
|
||||||
Some(&pseudo),
|
Some(&pseudo),
|
||||||
);
|
);
|
||||||
|
|
||||||
if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
|
if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
|
||||||
eager_pseudo_is_definitely_not_generated(&pseudo, &style) {
|
eager_pseudo_is_definitely_not_generated(&pseudo, &style.style) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
pseudo_styles.set(&pseudo, style);
|
pseudo_styles.set(&pseudo, style.style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementStyles {
|
ResolvedElementStyles {
|
||||||
primary: Some(primary_style.style),
|
primary: primary_style,
|
||||||
pseudos: pseudo_styles,
|
pseudos: pseudo_styles,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -323,7 +373,7 @@ where
|
||||||
layout_parent_style: Option<&ComputedValues>,
|
layout_parent_style: Option<&ComputedValues>,
|
||||||
) -> Option<Arc<ComputedValues>> {
|
) -> Option<Arc<ComputedValues>> {
|
||||||
let rules = self.match_pseudo(
|
let rules = self.match_pseudo(
|
||||||
&originating_element_style.style,
|
originating_element_style.style(),
|
||||||
pseudo,
|
pseudo,
|
||||||
VisitedHandlingMode::AllLinksUnvisited
|
VisitedHandlingMode::AllLinksUnvisited
|
||||||
);
|
);
|
||||||
|
@ -333,9 +383,9 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut visited_rules = None;
|
let mut visited_rules = None;
|
||||||
if originating_element_style.style.get_visited_style().is_some() {
|
if originating_element_style.style().get_visited_style().is_some() {
|
||||||
visited_rules = self.match_pseudo(
|
visited_rules = self.match_pseudo(
|
||||||
&originating_element_style.style,
|
originating_element_style.style(),
|
||||||
pseudo,
|
pseudo,
|
||||||
VisitedHandlingMode::RelevantLinkVisited,
|
VisitedHandlingMode::RelevantLinkVisited,
|
||||||
);
|
);
|
||||||
|
@ -346,10 +396,10 @@ where
|
||||||
rules: Some(rules),
|
rules: Some(rules),
|
||||||
visited_rules
|
visited_rules
|
||||||
},
|
},
|
||||||
Some(&originating_element_style.style),
|
Some(originating_element_style.style()),
|
||||||
layout_parent_style,
|
layout_parent_style,
|
||||||
Some(pseudo),
|
Some(pseudo),
|
||||||
))
|
).style)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_primary(
|
fn match_primary(
|
||||||
|
@ -485,6 +535,12 @@ where
|
||||||
cascade_visited: CascadeVisitedMode,
|
cascade_visited: CascadeVisitedMode,
|
||||||
pseudo: Option<&PseudoElement>,
|
pseudo: Option<&PseudoElement>,
|
||||||
) -> Arc<ComputedValues> {
|
) -> Arc<ComputedValues> {
|
||||||
|
debug_assert!(
|
||||||
|
self.element.implemented_pseudo_element().is_none() || pseudo.is_none(),
|
||||||
|
"Pseudo-elements can't have other pseudos!"
|
||||||
|
);
|
||||||
|
debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
|
||||||
|
|
||||||
let mut cascade_flags = CascadeFlags::empty();
|
let mut cascade_flags = CascadeFlags::empty();
|
||||||
|
|
||||||
if self.element.skip_root_and_item_based_display_fixup() ||
|
if self.element.skip_root_and_item_based_display_fixup() ||
|
||||||
|
|
|
@ -419,9 +419,9 @@ where
|
||||||
layout_parent_style.as_ref().map(|s| &**s)
|
layout_parent_style.as_ref().map(|s| &**s)
|
||||||
);
|
);
|
||||||
|
|
||||||
let is_display_contents = primary_style.style.is_display_contents();
|
let is_display_contents = primary_style.style().is_display_contents();
|
||||||
|
|
||||||
style = Some(primary_style.style);
|
style = Some(primary_style.0.into());
|
||||||
if !is_display_contents {
|
if !is_display_contents {
|
||||||
layout_parent_style = style.clone();
|
layout_parent_style = style.clone();
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,7 @@ where
|
||||||
.resolve_style(
|
.resolve_style(
|
||||||
style.as_ref().map(|s| &**s),
|
style.as_ref().map(|s| &**s),
|
||||||
layout_parent_style.as_ref().map(|s| &**s)
|
layout_parent_style.as_ref().map(|s| &**s)
|
||||||
)
|
).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the style for a single node.
|
/// Calculates the style for a single node.
|
||||||
|
@ -646,6 +646,10 @@ where
|
||||||
traversal_data.current_dom_depth);
|
traversal_data.current_dom_depth);
|
||||||
|
|
||||||
context.thread_local.bloom_filter.assert_complete(element);
|
context.thread_local.bloom_filter.assert_complete(element);
|
||||||
|
debug_assert_eq!(
|
||||||
|
context.thread_local.bloom_filter.matching_depth(),
|
||||||
|
traversal_data.current_dom_depth
|
||||||
|
);
|
||||||
|
|
||||||
// This is only relevant for animations as of right now.
|
// This is only relevant for animations as of right now.
|
||||||
important_rules_changed = true;
|
important_rules_changed = true;
|
||||||
|
@ -655,9 +659,9 @@ where
|
||||||
// Now that our bloom filter is set up, try the style sharing
|
// Now that our bloom filter is set up, try the style sharing
|
||||||
// cache.
|
// cache.
|
||||||
match target.share_style_if_possible(context) {
|
match target.share_style_if_possible(context) {
|
||||||
Some(styles) => {
|
Some(shareable_element) => {
|
||||||
context.thread_local.statistics.styles_shared += 1;
|
context.thread_local.statistics.styles_shared += 1;
|
||||||
styles
|
shareable_element.borrow_data().unwrap().share_styles()
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
context.thread_local.statistics.elements_matched += 1;
|
context.thread_local.statistics.elements_matched += 1;
|
||||||
|
@ -678,9 +682,9 @@ where
|
||||||
.sharing_cache
|
.sharing_cache
|
||||||
.insert_if_possible(
|
.insert_if_possible(
|
||||||
&element,
|
&element,
|
||||||
new_styles.primary(),
|
new_styles.primary.style(),
|
||||||
&mut target,
|
Some(&mut target),
|
||||||
context.thread_local.bloom_filter.matching_depth(),
|
traversal_data.current_dom_depth,
|
||||||
);
|
);
|
||||||
|
|
||||||
new_styles
|
new_styles
|
||||||
|
@ -709,15 +713,25 @@ where
|
||||||
let cascade_inputs =
|
let cascade_inputs =
|
||||||
ElementCascadeInputs::new_from_element_data(data);
|
ElementCascadeInputs::new_from_element_data(data);
|
||||||
|
|
||||||
let mut resolver =
|
let new_styles = {
|
||||||
StyleResolverForElement::new(
|
let mut resolver =
|
||||||
element,
|
StyleResolverForElement::new(
|
||||||
context,
|
element,
|
||||||
RuleInclusion::All,
|
context,
|
||||||
PseudoElementResolution::IfApplicable
|
RuleInclusion::All,
|
||||||
);
|
PseudoElementResolution::IfApplicable
|
||||||
|
);
|
||||||
|
|
||||||
resolver.cascade_styles_with_default_parents(cascade_inputs)
|
resolver.cascade_styles_with_default_parents(cascade_inputs)
|
||||||
|
};
|
||||||
|
context.thread_local.sharing_cache.insert_if_possible(
|
||||||
|
&element,
|
||||||
|
new_styles.primary.style(),
|
||||||
|
None,
|
||||||
|
traversal_data.current_dom_depth,
|
||||||
|
);
|
||||||
|
|
||||||
|
new_styles
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -727,9 +727,11 @@ pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawSe
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actually `PseudoElementResolution` doesn't matter.
|
// Actually `PseudoElementResolution` doesn't matter.
|
||||||
StyleResolverForElement::new(element, &mut context, RuleInclusion::All, PseudoElementResolution::IfApplicable)
|
let style: Arc<ComputedValues> =
|
||||||
|
StyleResolverForElement::new(element, &mut context, RuleInclusion::All, PseudoElementResolution::IfApplicable)
|
||||||
.cascade_style_and_visited_with_default_parents(inputs)
|
.cascade_style_and_visited_with_default_parents(inputs)
|
||||||
.into()
|
.into();
|
||||||
|
style.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue