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

View file

@ -1116,7 +1116,7 @@ impl LayoutThread {
let el = node.as_element().unwrap(); let el = node.as_element().unwrap();
if let Some(mut d) = element.mutate_data() { if let Some(mut d) = element.mutate_data() {
if d.has_styles() { 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() { if let Some(p) = el.parent_element() {
@ -1152,7 +1152,7 @@ impl LayoutThread {
if needs_dirtying { if needs_dirtying {
if let Some(mut d) = element.mutate_data() { if let Some(mut d) = element.mutate_data() {
if d.has_styles() { 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 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. // Stash the data on the element for processing by the style system.
restyle_data.hint.insert(restyle.hint.into()); style_data.restyle.hint.insert(restyle.hint.into());
restyle_data.damage = restyle.damage; style_data.restyle.damage = restyle.damage;
debug!("Noting restyle for {:?}: {:?}", el, restyle_data); debug!("Noting restyle for {:?}: {:?}", el, style_data.restyle);
} }
// Create a layout context for use throughout the following passes. // 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 /// 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 /// either before or during restyle traversal, and is cleared at the end of node
/// processing. /// processing.
#[derive(Debug, Default)] #[derive(Debug)]
pub struct RestyleData { pub struct RestyleData {
/// The restyle hint, which indicates whether selectors need to be rematched /// The restyle hint, which indicates whether selectors need to be rematched
/// for this element, its children, and its descendants. /// for this element, its children, and its descendants.
pub hint: RestyleHint, pub hint: RestyleHint,
/// Whether we reframed/reconstructed any ancestor or self. /// A few flags to have in mind.
pub reconstructed_ancestor: bool, flags: RestyleFlags,
/// The restyle damage, indicating what kind of layout changes are required /// The restyle damage, indicating what kind of layout changes are required
/// afte restyling. /// afte restyling.
@ -366,9 +375,53 @@ pub struct RestyleData {
} }
impl RestyleData { impl RestyleData {
/// Returns true if this RestyleData might invalidate the current style. fn new() -> Self {
pub fn has_invalidations(&self) -> bool { Self {
self.hint.has_self_invalidations() 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. /// The computed styles for the element and its pseudo-elements.
styles: Option<ElementStyles>, styles: Option<ElementStyles>,
/// Restyle tracking. We separate this into a separate allocation so that /// Restyle state.
/// we can drop it when no restyles are pending on the elemnt. pub restyle: RestyleData,
restyle: Option<Box<RestyleData>>,
} }
/// The kind of restyle that a single element should do. /// The kind of restyle that a single element should do.
@ -402,6 +454,14 @@ pub enum RestyleKind {
} }
impl ElementData { 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, /// Invalidates style for this element, its descendants, and later siblings,
/// based on the snapshot of the element that we took when attributes or /// based on the snapshot of the element that we took when attributes or
/// state changed. /// state changed.
@ -437,7 +497,7 @@ impl ElementData {
pub fn new(existing: Option<ElementStyles>) -> Self { pub fn new(existing: Option<ElementStyles>) -> Self {
ElementData { ElementData {
styles: existing, styles: existing,
restyle: None, restyle: RestyleData::new(),
} }
} }
@ -448,7 +508,7 @@ impl ElementData {
/// Returns whether we have any outstanding style invalidation. /// Returns whether we have any outstanding style invalidation.
pub fn has_invalidations(&self) -> bool { 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 /// Returns the kind of restyling that we're going to need to do on this
@ -465,10 +525,7 @@ impl ElementData {
return RestyleKind::MatchAndCascade; return RestyleKind::MatchAndCascade;
} }
debug_assert!(self.restyle.is_some()); let hint = self.restyle.hint;
let restyle_data = self.restyle.as_ref().unwrap();
let hint = restyle_data.hint;
if shared_context.traversal_flags.for_animation_only() { if shared_context.traversal_flags.for_animation_only() {
// return either CascadeWithReplacements or CascadeOnly in case of // return either CascadeWithReplacements or CascadeOnly in case of
// animation-only restyle. // animation-only restyle.
@ -488,7 +545,8 @@ impl ElementData {
return RestyleKind::CascadeWithReplacements(hint & RestyleHint::replacements()); 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; return RestyleKind::CascadeOnly;
} }
@ -513,13 +571,6 @@ impl ElementData {
self.styles.as_mut().expect("Calling styles_mut() on unstyled 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. /// Sets the computed element styles.
pub fn set_styles(&mut self, styles: ElementStyles) { pub fn set_styles(&mut self, styles: ElementStyles) {
self.styles = Some(styles); self.styles = Some(styles);
@ -558,47 +609,9 @@ impl ElementData {
important_rules != other_important_rules important_rules != other_important_rules
} }
/// Returns true if the Element has a RestyleData. /// Drops any restyle state from the element.
pub fn has_restyle(&self) -> bool { pub fn clear_restyle_state(&mut self) {
self.restyle.is_some() self.restyle.clear();
}
/// 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")
} }
/// Returns SMIL overriden value if exists. /// 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, Some(d) => d,
None => return false, None => return false,
}; };
return data.get_restyle() return data.restyle.hint.has_animation_hint()
.map_or(false, |r| r.hint.has_animation_hint());
} }
/// Gets declarations from XBL bindings from the element. Only gecko element could have this. /// 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 // We can't just return here because there may also be attribute
// changes as well that imply additional hints. // changes as well that imply additional hints.
let mut data = self.data.as_mut().unwrap(); 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(); 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 invalidated_self {
if let Some(ref mut data) = self.data { 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 { match self.data {
None => return false, None => return false,
Some(ref data) => { Some(ref data) => {
if let Some(restyle) = data.get_restyle() { if data.restyle.hint.contains_subtree() {
if restyle.hint.contains_subtree() {
return false; return false;
} }
} }
} }
}
let mut sibling_invalidations = InvalidationVector::new(); let mut sibling_invalidations = InvalidationVector::new();
@ -494,7 +492,7 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
if invalidated_self { if invalidated_self {
if let Some(ref mut data) = self.data { 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 { if self.fully_invalid {
debug!("process_invalidations: fully_invalid({:?})", debug!("process_invalidations: fully_invalid({:?})",
element); element);
data.ensure_restyle().hint.insert(RestyleHint::restyle_subtree()); data.restyle.hint.insert(RestyleHint::restyle_subtree());
return true; return true;
} }
} }
@ -165,19 +165,17 @@ impl StylesheetInvalidationSet {
return false; return false;
} }
if let Some(ref r) = data.get_restyle() { if data.restyle.hint.contains_subtree() {
if r.hint.contains_subtree() {
debug!("process_invalidations_in_subtree: {:?} was already invalid", debug!("process_invalidations_in_subtree: {:?} was already invalid",
element); element);
return false; return false;
} }
}
for scope in &self.invalid_scopes { for scope in &self.invalid_scopes {
if scope.matches(element) { if scope.matches(element) {
debug!("process_invalidations_in_subtree: {:?} matched {:?}", debug!("process_invalidations_in_subtree: {:?} matched {:?}",
element, scope); element, scope);
data.ensure_restyle().hint.insert(RestyleHint::restyle_subtree()); data.restyle.hint.insert(RestyleHint::restyle_subtree());
return true; return true;
} }
} }

View file

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

View file

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

View file

@ -10,7 +10,6 @@ use data::{ElementData, ElementStyles};
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode}; use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint}; use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint};
use matching::{ChildCascadeRequirement, MatchMethods}; use matching::{ChildCascadeRequirement, MatchMethods};
use selector_parser::RestyleDamage;
use sharing::{StyleSharingBehavior, StyleSharingTarget}; use sharing::{StyleSharingBehavior, StyleSharingTarget};
#[cfg(feature = "servo")] use servo_config::opts; #[cfg(feature = "servo")] use servo_config::opts;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -296,10 +295,8 @@ pub trait DomTraversal<E: TElement> : Sync {
if el.is_native_anonymous() { if el.is_native_anonymous() {
if let Some(parent) = el.traversal_parent() { if let Some(parent) = el.traversal_parent() {
let parent_data = parent.borrow_data().unwrap(); let parent_data = parent.borrow_data().unwrap();
let going_to_reframe = parent_data.get_restyle().map_or(false, |r| { let going_to_reframe =
r.reconstructed_ancestor || parent_data.restyle.reconstructed_self_or_ancestor();
r.damage.contains(RestyleDamage::reconstruct())
});
let mut is_before_or_after_pseudo = false; let mut is_before_or_after_pseudo = false;
if let Some(pseudo) = el.implemented_pseudo_element() { if let Some(pseudo) = el.implemented_pseudo_element() {
@ -337,9 +334,8 @@ pub trait DomTraversal<E: TElement> : Sync {
Some(d) => d, Some(d) => d,
None => return false, None => return false,
}; };
return data.get_restyle() return data.restyle.hint.has_animation_hint() ||
.map_or(false, |r| r.hint.has_animation_hint() || data.restyle.hint.has_recascade_self();
r.hint.has_recascade_self());
} }
// If the dirty descendants bit is set, we need to traverse no // If the dirty descendants bit is set, we need to traverse no
@ -360,8 +356,6 @@ pub trait DomTraversal<E: TElement> : Sync {
return true; 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 // If we have a restyle hint or need to recascade, we need to
// visit the element. // 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 // since that can return true even if we have a restyle hint
// indicating that the element's descendants (but not necessarily // indicating that the element's descendants (but not necessarily
// the element) need restyling. // the element) need restyling.
if !r.hint.is_empty() { if !data.restyle.hint.is_empty() {
return true; return true;
} }
}
// Servo uses the post-order traversal for flow construction, so // Servo uses the post-order traversal for flow construction, so
// we need to traverse any element with damage so that we can perform // 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 // We also need to traverse nodes with explicit damage and no other
// restyle data, so that this damage can be cleared. // restyle data, so that this damage can be cleared.
if (cfg!(feature = "servo") || traversal_flags.for_reconstruct()) && 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; 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 // Now that matching and cascading is done, clear the bits corresponding to
// those operations and compute the propagated restyle hint. // those operations and compute the propagated restyle hint.
let mut propagated_hint = match data.get_restyle_mut() { let mut propagated_hint = {
None => RestyleHint::empty(),
Some(r) => {
debug_assert!(context.shared.traversal_flags.for_animation_only() || 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 restyle hint should be handled during \
animation-only restyles"); 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 // FIXME(bholley): Need to handle explicitly-inherited reset properties
// somewhere. // somewhere.
propagated_hint.insert(hint.into()); propagated_hint.insert(hint);
trace!("propagated_hint={:?} \ trace!("propagated_hint={:?} \
is_display_none={:?}, implementing_pseudo={:?}", is_display_none={:?}, implementing_pseudo={:?}",
@ -730,10 +720,9 @@ pub fn recalc_style_at<E, D>(traversal: &D,
DontLog) && DontLog) &&
(has_dirty_descendants_for_this_restyle || (has_dirty_descendants_for_this_restyle ||
!propagated_hint.is_empty()) { !propagated_hint.is_empty()) {
let reconstructed_ancestor = data.get_restyle().map_or(false, |r| { let reconstructed_ancestor =
r.reconstructed_ancestor || data.restyle.reconstructed_self_or_ancestor();
r.damage.contains(RestyleDamage::reconstruct())
});
preprocess_children::<E, D>( preprocess_children::<E, D>(
context, context,
element, 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 // data here, since we won't need to perform a post-traversal to pick up
// any change hints. // any change hints.
if context.shared.traversal_flags.for_reconstruct() { if context.shared.traversal_flags.for_reconstruct() {
data.clear_restyle(); data.clear_restyle_state();
} }
if context.shared.traversal_flags.for_animation_only() { 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); debug!("compute_style: {:?} (kind={:?})", element, kind);
if data.has_styles() {
data.restyle.set_restyled();
}
match kind { match kind {
MatchAndCascade => { MatchAndCascade => {
debug_assert!(!context.shared.traversal_flags.for_animation_only(), debug_assert!(!context.shared.traversal_flags.for_animation_only(),
@ -870,31 +863,24 @@ where
continue; 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: {:?}", trace!(" > {:?} -> {:?} + {:?}, pseudo: {:?}",
child, child,
child_data.get_restyle().map(|r| &r.hint), child_data.restyle.hint,
propagated_hint, propagated_hint,
child.implemented_pseudo_element()); 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 // Propagate the parent restyle hint, that may make us restyle the whole
// subtree. // subtree.
restyle_data.reconstructed_ancestor = reconstructed_ancestor; if reconstructed_ancestor {
restyle_data.hint.insert(propagated_hint); 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; 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] #[no_mangle]
@ -2461,7 +2461,7 @@ unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut<ElementData>,
bindings::Gecko_SetOwnerDocumentNeedsStyleFlush(element.0); bindings::Gecko_SetOwnerDocumentNeedsStyleFlush(element.0);
// Ensure and return the RestyleData. // Ensure and return the RestyleData.
Some(data.ensure_restyle()) Some(&mut data.restyle)
} }
#[no_mangle] #[no_mangle]
@ -2518,13 +2518,16 @@ pub extern "C" fn Servo_NoteExplicitHints(element: RawGeckoElementBorrowed,
pub extern "C" fn Servo_TakeChangeHint(element: RawGeckoElementBorrowed) -> nsChangeHint pub extern "C" fn Servo_TakeChangeHint(element: RawGeckoElementBorrowed) -> nsChangeHint
{ {
let element = GeckoElement(element); let element = GeckoElement(element);
let damage = if let Some(mut data) = element.mutate_data() { let damage = match element.mutate_data() {
let d = data.get_restyle().map_or(GeckoRestyleDamage::empty(), |r| r.damage); Some(mut data) => {
data.clear_restyle(); let damage = data.restyle.damage;
d data.clear_restyle_state();
} else { damage
}
None => {
warn!("Trying to get change hint from unstyled element"); warn!("Trying to get change hint from unstyled element");
GeckoRestyleDamage::empty() GeckoRestyleDamage::empty()
}
}; };
debug!("Servo_TakeChangeHint: {:?}, damage={:?}", element, damage); debug!("Servo_TakeChangeHint: {:?}, damage={:?}", element, damage);

View file

@ -7,7 +7,7 @@ use servo_arc::Arc;
use std::mem::{size_of, align_of}; use std::mem::{size_of, align_of};
use style; use style;
use style::applicable_declarations::ApplicableDeclarationBlock; 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::gecko::selector_parser as real;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::rule_tree::{RuleNode, StrongRuleNode}; 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_computed_style, ComputedStyle, 32);
size_of_test!(test_size_of_element_styles, ElementStyles, 48); 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_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); size_of_test!(test_size_of_property_declaration, style::properties::PropertyDeclaration, 32);