Do a second pass on the sharing cache to reuse style by rule node identity.

MozReview-Commit-ID: H67j3Sbt3gr
This commit is contained in:
Bobby Holley 2017-09-08 16:03:24 -07:00
parent 7a7070e075
commit 1c9b39a8e8
6 changed files with 122 additions and 8 deletions

View file

@ -22,8 +22,9 @@ use std::ops::{Deref, DerefMut};
use style_resolver::{PrimaryStyle, ResolvedElementStyles, ResolvedStyle};
bitflags! {
/// Various flags stored on ElementData.
#[derive(Default)]
flags ElementDataFlags: u8 {
pub flags ElementDataFlags: u8 {
/// Whether the styles changed for this restyle.
const WAS_RESTYLED = 1 << 0,
/// Whether the last traversal of this element did not do
@ -37,6 +38,13 @@ bitflags! {
const TRAVERSED_WITHOUT_STYLING = 1 << 1,
/// Whether we reframed/reconstructed any ancestor or self.
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,
}
}
@ -202,7 +210,7 @@ pub struct ElementData {
pub hint: RestyleHint,
/// Flags.
flags: ElementDataFlags,
pub flags: ElementDataFlags,
}
/// The kind of restyle that a single element should do.
@ -276,11 +284,17 @@ impl ElementData {
/// Returns this element's primary style as a resolved style to use for sharing.
pub fn share_primary_style(&self) -> PrimaryStyle {
PrimaryStyle(ResolvedStyle::new(self.styles.primary().clone()))
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())
}
@ -442,6 +456,28 @@ impl ElementData {
#[cfg(feature = "servo")]
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.
#[cfg(feature = "gecko")]
pub fn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize {