Auto merge of #17348 - emilio:bup, r=bholley

style: Inline RestyleData.

Bug: 1368236
MozReview-Commit-ID: 49s3SO0PMHf

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17348)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-06-15 20:28:56 -07:00 committed by GitHub
commit 33766b2714
11 changed files with 183 additions and 202 deletions

View file

@ -171,19 +171,14 @@ impl<T: ThreadSafeLayoutNode> ThreadSafeLayoutNodeHelpers for T {
let damage = {
let data = node.get_raw_data().unwrap();
if let Some(r) = data.style_data.element_data.borrow().get_restyle() {
// We're reflowing a node that just got a restyle, and so the
// damage has been computed and stored in the RestyleData.
r.damage
} else if !data.layout_data.borrow().flags.contains(::data::HAS_BEEN_TRAVERSED) {
if !data.layout_data.borrow().flags.contains(::data::HAS_BEEN_TRAVERSED) {
// We're reflowing a node that was styled for the first time and
// has never been visited by layout. Return rebuild_and_reflow,
// because that's what the code expects.
RestyleDamage::rebuild_and_reflow()
} else {
// We're reflowing a node whose style data didn't change, but whose
// layout may change due to changes in ancestors or descendants.
RestyleDamage::empty()
data.style_data.element_data.borrow().restyle.damage
}
};

View file

@ -1116,7 +1116,7 @@ impl LayoutThread {
let el = node.as_element().unwrap();
if let Some(mut d) = element.mutate_data() {
if d.has_styles() {
d.ensure_restyle().hint.insert(RestyleHint::restyle_subtree());
d.restyle.hint.insert(RestyleHint::restyle_subtree());
}
}
if let Some(p) = el.parent_element() {
@ -1152,7 +1152,7 @@ impl LayoutThread {
if needs_dirtying {
if let Some(mut d) = element.mutate_data() {
if d.has_styles() {
d.ensure_restyle().hint.insert(RestyleHint::restyle_subtree());
d.restyle.hint.insert(RestyleHint::restyle_subtree());
}
}
}
@ -1197,12 +1197,11 @@ impl LayoutThread {
}
let mut style_data = style_data.borrow_mut();
let mut restyle_data = style_data.ensure_restyle();
// Stash the data on the element for processing by the style system.
restyle_data.hint.insert(restyle.hint.into());
restyle_data.damage = restyle.damage;
debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
style_data.restyle.hint.insert(restyle.hint.into());
style_data.restyle.damage = restyle.damage;
debug!("Noting restyle for {:?}: {:?}", el, style_data.restyle);
}
// Create a layout context for use throughout the following passes.

View file

@ -348,17 +348,26 @@ impl ElementStyles {
}
}
bitflags! {
flags RestyleFlags: u8 {
/// Whether the styles changed for this restyle.
const WAS_RESTYLED = 1 << 0,
/// Whether we reframed/reconstructed any ancestor or self.
const ANCESTOR_WAS_RECONSTRUCTED = 1 << 1,
}
}
/// Transient data used by the restyle algorithm. This structure is instantiated
/// either before or during restyle traversal, and is cleared at the end of node
/// processing.
#[derive(Debug, Default)]
#[derive(Debug)]
pub struct RestyleData {
/// The restyle hint, which indicates whether selectors need to be rematched
/// for this element, its children, and its descendants.
pub hint: RestyleHint,
/// Whether we reframed/reconstructed any ancestor or self.
pub reconstructed_ancestor: bool,
/// A few flags to have in mind.
flags: RestyleFlags,
/// The restyle damage, indicating what kind of layout changes are required
/// afte restyling.
@ -366,9 +375,53 @@ pub struct RestyleData {
}
impl RestyleData {
/// Returns true if this RestyleData might invalidate the current style.
pub fn has_invalidations(&self) -> bool {
self.hint.has_self_invalidations()
fn new() -> Self {
Self {
hint: RestyleHint::empty(),
flags: RestyleFlags::empty(),
damage: RestyleDamage::empty(),
}
}
/// Clear all the restyle state associated with this element.
fn clear(&mut self) {
*self = Self::new();
}
/// Returns whether this element or any ancestor is going to be
/// reconstructed.
pub fn reconstructed_self_or_ancestor(&self) -> bool {
self.reconstructed_ancestor() ||
self.damage.contains(RestyleDamage::reconstruct())
}
/// Returns whether any ancestor of this element was restyled.
fn reconstructed_ancestor(&self) -> bool {
self.flags.contains(ANCESTOR_WAS_RECONSTRUCTED)
}
/// Sets the flag that tells us whether we've reconstructed an ancestor.
pub fn set_reconstructed_ancestor(&mut self) {
// If it weren't for animation-only traversals, we could assert
// `!self.reconstructed_ancestor()` here.
self.flags.insert(ANCESTOR_WAS_RECONSTRUCTED);
}
/// Mark this element as restyled, which is useful to know whether we need
/// to do a post-traversal.
pub fn set_restyled(&mut self) {
self.flags.insert(WAS_RESTYLED);
}
/// Mark this element as restyled, which is useful to know whether we need
/// to do a post-traversal.
pub fn is_restyle(&self) -> bool {
self.flags.contains(WAS_RESTYLED)
}
/// Returns whether this element has been part of a restyle.
pub fn contains_restyle_data(&self) -> bool {
self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
}
}
@ -382,9 +435,8 @@ pub struct ElementData {
/// The computed styles for the element and its pseudo-elements.
styles: Option<ElementStyles>,
/// Restyle tracking. We separate this into a separate allocation so that
/// we can drop it when no restyles are pending on the elemnt.
restyle: Option<Box<RestyleData>>,
/// Restyle state.
pub restyle: RestyleData,
}
/// The kind of restyle that a single element should do.
@ -402,6 +454,14 @@ pub enum RestyleKind {
}
impl ElementData {
/// Borrows both styles and restyle mutably at the same time.
pub fn styles_and_restyle_mut(
&mut self
) -> (&mut ElementStyles, &mut RestyleData) {
(self.styles.as_mut().unwrap(),
&mut self.restyle)
}
/// Invalidates style for this element, its descendants, and later siblings,
/// based on the snapshot of the element that we took when attributes or
/// state changed.
@ -437,7 +497,7 @@ impl ElementData {
pub fn new(existing: Option<ElementStyles>) -> Self {
ElementData {
styles: existing,
restyle: None,
restyle: RestyleData::new(),
}
}
@ -448,7 +508,7 @@ impl ElementData {
/// Returns whether we have any outstanding style invalidation.
pub fn has_invalidations(&self) -> bool {
self.restyle.as_ref().map_or(false, |r| r.has_invalidations())
self.restyle.hint.has_self_invalidations()
}
/// Returns the kind of restyling that we're going to need to do on this
@ -465,10 +525,7 @@ impl ElementData {
return RestyleKind::MatchAndCascade;
}
debug_assert!(self.restyle.is_some());
let restyle_data = self.restyle.as_ref().unwrap();
let hint = restyle_data.hint;
let hint = self.restyle.hint;
if shared_context.traversal_flags.for_animation_only() {
// return either CascadeWithReplacements or CascadeOnly in case of
// animation-only restyle.
@ -488,7 +545,8 @@ impl ElementData {
return RestyleKind::CascadeWithReplacements(hint & RestyleHint::replacements());
}
debug_assert!(hint.has_recascade_self(), "We definitely need to do something!");
debug_assert!(hint.has_recascade_self(),
"We definitely need to do something!");
return RestyleKind::CascadeOnly;
}
@ -513,13 +571,6 @@ impl ElementData {
self.styles.as_mut().expect("Calling styles_mut() on unstyled ElementData")
}
/// Borrows both styles and restyle mutably at the same time.
pub fn styles_and_restyle_mut(&mut self) -> (&mut ElementStyles,
Option<&mut RestyleData>) {
(self.styles.as_mut().unwrap(),
self.restyle.as_mut().map(|r| &mut **r))
}
/// Sets the computed element styles.
pub fn set_styles(&mut self, styles: ElementStyles) {
self.styles = Some(styles);
@ -558,47 +609,9 @@ impl ElementData {
important_rules != other_important_rules
}
/// Returns true if the Element has a RestyleData.
pub fn has_restyle(&self) -> bool {
self.restyle.is_some()
}
/// Drops any RestyleData.
pub fn clear_restyle(&mut self) {
self.restyle = None;
}
/// Creates a RestyleData if one doesn't exist.
///
/// Asserts that the Element has been styled.
pub fn ensure_restyle(&mut self) -> &mut RestyleData {
debug_assert!(self.styles.is_some(), "restyling unstyled element");
if self.restyle.is_none() {
self.restyle = Some(Box::new(RestyleData::default()));
}
self.restyle.as_mut().unwrap()
}
/// Gets a reference to the restyle data, if any.
pub fn get_restyle(&self) -> Option<&RestyleData> {
self.restyle.as_ref().map(|r| &**r)
}
/// Gets a reference to the restyle data. Panic if the element does not
/// have restyle data.
pub fn restyle(&self) -> &RestyleData {
self.get_restyle().expect("Calling restyle without RestyleData")
}
/// Gets a mutable reference to the restyle data, if any.
pub fn get_restyle_mut(&mut self) -> Option<&mut RestyleData> {
self.restyle.as_mut().map(|r| &mut **r)
}
/// Gets a mutable reference to the restyle data. Panic if the element does
/// not have restyle data.
pub fn restyle_mut(&mut self) -> &mut RestyleData {
self.get_restyle_mut().expect("Calling restyle_mut without RestyleData")
/// Drops any restyle state from the element.
pub fn clear_restyle_state(&mut self) {
self.restyle.clear();
}
/// Returns SMIL overriden value if exists.

View file

@ -567,8 +567,7 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
Some(d) => d,
None => return false,
};
return data.get_restyle()
.map_or(false, |r| r.hint.has_animation_hint());
return data.restyle.hint.has_animation_hint()
}
/// Gets declarations from XBL bindings from the element. Only gecko element could have this.

View file

@ -114,7 +114,7 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
// We can't just return here because there may also be attribute
// changes as well that imply additional hints.
let mut data = self.data.as_mut().unwrap();
data.ensure_restyle().hint.insert(RestyleHint::restyle_subtree().into());
data.restyle.hint.insert(RestyleHint::restyle_subtree());
}
let mut classes_removed = SmallVec::<[Atom; 8]>::new();
@ -211,7 +211,7 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
if invalidated_self {
if let Some(ref mut data) = self.data {
data.ensure_restyle().hint.insert(RESTYLE_SELF.into());
data.restyle.hint.insert(RESTYLE_SELF);
}
}
@ -287,13 +287,11 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
match self.data {
None => return false,
Some(ref data) => {
if let Some(restyle) = data.get_restyle() {
if restyle.hint.contains_subtree() {
if data.restyle.hint.contains_subtree() {
return false;
}
}
}
}
let mut sibling_invalidations = InvalidationVector::new();
@ -494,7 +492,7 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
if invalidated_self {
if let Some(ref mut data) = self.data {
data.ensure_restyle().hint.insert(RESTYLE_SELF.into());
data.restyle.hint.insert(RESTYLE_SELF);
}
}

View file

@ -134,7 +134,7 @@ impl StylesheetInvalidationSet {
if self.fully_invalid {
debug!("process_invalidations: fully_invalid({:?})",
element);
data.ensure_restyle().hint.insert(RestyleHint::restyle_subtree());
data.restyle.hint.insert(RestyleHint::restyle_subtree());
return true;
}
}
@ -165,19 +165,17 @@ impl StylesheetInvalidationSet {
return false;
}
if let Some(ref r) = data.get_restyle() {
if r.hint.contains_subtree() {
if data.restyle.hint.contains_subtree() {
debug!("process_invalidations_in_subtree: {:?} was already invalid",
element);
return false;
}
}
for scope in &self.invalid_scopes {
if scope.matches(element) {
debug!("process_invalidations_in_subtree: {:?} matched {:?}",
element, scope);
data.ensure_restyle().hint.insert(RestyleHint::restyle_subtree());
data.restyle.hint.insert(RestyleHint::restyle_subtree());
return true;
}
}

View file

@ -707,8 +707,7 @@ trait PrivateMatchMethods: TElement {
// for followup work to make the optimization here more optimal by considering
// each bit individually.
let skip_applying_damage =
restyle.damage.contains(RestyleDamage::reconstruct()) ||
restyle.reconstructed_ancestor;
restyle.reconstructed_self_or_ancestor();
let difference = self.compute_style_difference(&old_values,
&new_values,
@ -1187,12 +1186,10 @@ pub trait MatchMethods : TElement {
}
});
if matches_different_pseudos {
if let Some(r) = data.get_restyle_mut() {
if matches_different_pseudos && data.restyle.is_restyle() {
// Any changes to the matched pseudo-elements trigger
// reconstruction.
r.damage |= RestyleDamage::reconstruct();
}
data.restyle.damage |= RestyleDamage::reconstruct();
}
}
@ -1257,16 +1254,11 @@ pub trait MatchMethods : TElement {
/// Computes and applies restyle damage.
fn accumulate_damage(&self,
shared_context: &SharedStyleContext,
restyle: Option<&mut RestyleData>,
restyle: &mut RestyleData,
old_values: Option<&ComputedValues>,
new_values: &Arc<ComputedValues>,
pseudo: Option<&PseudoElement>)
-> ChildCascadeRequirement {
let restyle = match restyle {
Some(r) => r,
None => return ChildCascadeRequirement::MustCascadeChildren,
};
let old_values = match old_values {
Some(v) => v,
None => return ChildCascadeRequirement::MustCascadeChildren,

View file

@ -377,10 +377,10 @@ impl<E: TElement> StyleSharingTarget<E> {
// We used to have pseudos (because we had styles).
// Check for damage from the set of pseudos changing or
// pseudos being restyled.
let (styles, restyle_data) = data.styles_and_restyle_mut();
if let Some(restyle_data) = restyle_data {
let (styles, mut restyle_data) = data.styles_and_restyle_mut();
let old_pseudos = &styles.pseudos;
let new_pseudos = &shared_style.pseudos;
if !old_pseudos.has_same_pseudos_as(new_pseudos) {
restyle_data.damage |= RestyleDamage::reconstruct();
} else {
@ -394,7 +394,7 @@ impl<E: TElement> StyleSharingTarget<E> {
new_pseudos.get(&pseudo).unwrap().values();
self.element.accumulate_damage(
&shared_context,
Some(restyle_data),
restyle_data,
old_values,
new_values,
Some(&pseudo)
@ -402,13 +402,13 @@ impl<E: TElement> StyleSharingTarget<E> {
}
}
}
}
let old_values = data.get_styles_mut()
.and_then(|s| s.primary.values.take());
let old_values =
data.get_styles_mut().and_then(|s| s.primary.values.take());
self.element.accumulate_damage(
&shared_context,
data.get_restyle_mut(),
&mut data.restyle,
old_values.as_ref().map(|v| &**v),
shared_style.primary.values(),
None
@ -597,9 +597,6 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
match sharing_result {
Ok(shared_style) => {
// Yay, cache hit. Share the style.
debug_assert_eq!(data.has_styles(), data.has_restyle());
let child_cascade_requirement =
target.accumulate_damage_when_sharing(shared_context,
&shared_style,

View file

@ -10,7 +10,6 @@ use data::{ElementData, ElementStyles};
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint};
use matching::{ChildCascadeRequirement, MatchMethods};
use selector_parser::RestyleDamage;
use sharing::{StyleSharingBehavior, StyleSharingTarget};
#[cfg(feature = "servo")] use servo_config::opts;
use smallvec::SmallVec;
@ -296,10 +295,8 @@ pub trait DomTraversal<E: TElement> : Sync {
if el.is_native_anonymous() {
if let Some(parent) = el.traversal_parent() {
let parent_data = parent.borrow_data().unwrap();
let going_to_reframe = parent_data.get_restyle().map_or(false, |r| {
r.reconstructed_ancestor ||
r.damage.contains(RestyleDamage::reconstruct())
});
let going_to_reframe =
parent_data.restyle.reconstructed_self_or_ancestor();
let mut is_before_or_after_pseudo = false;
if let Some(pseudo) = el.implemented_pseudo_element() {
@ -337,9 +334,8 @@ pub trait DomTraversal<E: TElement> : Sync {
Some(d) => d,
None => return false,
};
return data.get_restyle()
.map_or(false, |r| r.hint.has_animation_hint() ||
r.hint.has_recascade_self());
return data.restyle.hint.has_animation_hint() ||
data.restyle.hint.has_recascade_self();
}
// If the dirty descendants bit is set, we need to traverse no
@ -360,8 +356,6 @@ pub trait DomTraversal<E: TElement> : Sync {
return true;
}
// Check the restyle data.
if let Some(r) = data.get_restyle() {
// If we have a restyle hint or need to recascade, we need to
// visit the element.
//
@ -369,10 +363,9 @@ pub trait DomTraversal<E: TElement> : Sync {
// since that can return true even if we have a restyle hint
// indicating that the element's descendants (but not necessarily
// the element) need restyling.
if !r.hint.is_empty() {
if !data.restyle.hint.is_empty() {
return true;
}
}
// Servo uses the post-order traversal for flow construction, so
// we need to traverse any element with damage so that we can perform
@ -381,7 +374,7 @@ pub trait DomTraversal<E: TElement> : Sync {
// We also need to traverse nodes with explicit damage and no other
// restyle data, so that this damage can be cleared.
if (cfg!(feature = "servo") || traversal_flags.for_reconstruct()) &&
data.get_restyle().map_or(false, |r| !r.damage.is_empty()) {
!data.restyle.damage.is_empty() {
return true;
}
@ -690,20 +683,17 @@ pub fn recalc_style_at<E, D>(traversal: &D,
// Now that matching and cascading is done, clear the bits corresponding to
// those operations and compute the propagated restyle hint.
let mut propagated_hint = match data.get_restyle_mut() {
None => RestyleHint::empty(),
Some(r) => {
let mut propagated_hint = {
debug_assert!(context.shared.traversal_flags.for_animation_only() ||
!r.hint.has_animation_hint(),
!data.restyle.hint.has_animation_hint(),
"animation restyle hint should be handled during \
animation-only restyles");
r.hint.propagate(&context.shared.traversal_flags)
},
data.restyle.hint.propagate(&context.shared.traversal_flags)
};
// FIXME(bholley): Need to handle explicitly-inherited reset properties
// somewhere.
propagated_hint.insert(hint.into());
propagated_hint.insert(hint);
trace!("propagated_hint={:?} \
is_display_none={:?}, implementing_pseudo={:?}",
@ -730,10 +720,9 @@ pub fn recalc_style_at<E, D>(traversal: &D,
DontLog) &&
(has_dirty_descendants_for_this_restyle ||
!propagated_hint.is_empty()) {
let reconstructed_ancestor = data.get_restyle().map_or(false, |r| {
r.reconstructed_ancestor ||
r.damage.contains(RestyleDamage::reconstruct())
});
let reconstructed_ancestor =
data.restyle.reconstructed_self_or_ancestor();
preprocess_children::<E, D>(
context,
element,
@ -746,7 +735,7 @@ pub fn recalc_style_at<E, D>(traversal: &D,
// data here, since we won't need to perform a post-traversal to pick up
// any change hints.
if context.shared.traversal_flags.for_reconstruct() {
data.clear_restyle();
data.clear_restyle_state();
}
if context.shared.traversal_flags.for_animation_only() {
@ -792,6 +781,10 @@ fn compute_style<E, D>(_traversal: &D,
debug!("compute_style: {:?} (kind={:?})", element, kind);
if data.has_styles() {
data.restyle.set_restyled();
}
match kind {
MatchAndCascade => {
debug_assert!(!context.shared.traversal_flags.for_animation_only(),
@ -870,31 +863,24 @@ where
continue;
}
// Handle element snapshots and invalidation of descendants and siblings
// as needed.
//
// NB: This will be a no-op if there's no restyle data and no snapshot.
child_data.invalidate_style_if_needed(child, &context.shared);
trace!(" > {:?} -> {:?} + {:?}, pseudo: {:?}",
child,
child_data.get_restyle().map(|r| &r.hint),
child_data.restyle.hint,
propagated_hint,
child.implemented_pseudo_element());
// If the child doesn't have pre-existing RestyleData and we don't have
// any reason to create one, avoid the useless allocation and move on to
// the next child.
if !reconstructed_ancestor && propagated_hint.is_empty() && !child_data.has_restyle() {
continue;
}
let mut restyle_data = child_data.ensure_restyle();
// Propagate the parent restyle hint, that may make us restyle the whole
// subtree.
restyle_data.reconstructed_ancestor = reconstructed_ancestor;
restyle_data.hint.insert(propagated_hint);
if reconstructed_ancestor {
child_data.restyle.set_reconstructed_ancestor();
}
child_data.restyle.hint.insert(propagated_hint);
// Handle element snapshots and invalidation of descendants and siblings
// as needed.
//
// NB: This will be a no-op if there's no snapshot.
child_data.invalidate_style_if_needed(child, &context.shared);
}
}

View file

@ -301,7 +301,7 @@ pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
return false;
}
element.has_dirty_descendants() || element.borrow_data().unwrap().has_restyle()
element.has_dirty_descendants() || element.borrow_data().unwrap().restyle.contains_restyle_data()
}
#[no_mangle]
@ -2461,7 +2461,7 @@ unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut<ElementData>,
bindings::Gecko_SetOwnerDocumentNeedsStyleFlush(element.0);
// Ensure and return the RestyleData.
Some(data.ensure_restyle())
Some(&mut data.restyle)
}
#[no_mangle]
@ -2518,13 +2518,16 @@ pub extern "C" fn Servo_NoteExplicitHints(element: RawGeckoElementBorrowed,
pub extern "C" fn Servo_TakeChangeHint(element: RawGeckoElementBorrowed) -> nsChangeHint
{
let element = GeckoElement(element);
let damage = if let Some(mut data) = element.mutate_data() {
let d = data.get_restyle().map_or(GeckoRestyleDamage::empty(), |r| r.damage);
data.clear_restyle();
d
} else {
let damage = match element.mutate_data() {
Some(mut data) => {
let damage = data.restyle.damage;
data.clear_restyle_state();
damage
}
None => {
warn!("Trying to get change hint from unstyled element");
GeckoRestyleDamage::empty()
}
};
debug!("Servo_TakeChangeHint: {:?}, damage={:?}", element, damage);

View file

@ -7,7 +7,7 @@ use servo_arc::Arc;
use std::mem::{size_of, align_of};
use style;
use style::applicable_declarations::ApplicableDeclarationBlock;
use style::data::{ComputedStyle, ElementData, ElementStyles};
use style::data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
use style::gecko::selector_parser as real;
use style::properties::ComputedValues;
use style::rule_tree::{RuleNode, StrongRuleNode};
@ -34,6 +34,7 @@ size_of_test!(test_size_of_option_rule_node, Option<StrongRuleNode>, 8);
size_of_test!(test_size_of_computed_style, ComputedStyle, 32);
size_of_test!(test_size_of_element_styles, ElementStyles, 48);
size_of_test!(test_size_of_element_data, ElementData, 56);
size_of_test!(test_size_of_restyle_data, RestyleData, 8);
size_of_test!(test_size_of_property_declaration, style::properties::PropertyDeclaration, 32);