--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -343,53 +343,108 @@ impl ElementStyles {
}
/// Whether this element `display` value is `none`.
pub fn is_display_none(&self) -> bool {
self.primary.values().get_box().clone_display() == display::T::none
}
}
+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.
pub damage: RestyleDamage,
}
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, reconstructed: bool) {
+ // TODO(emilio): We should be able to assert this, except for
+ // animation-only traversals.
+ // debug_assert!(!self.reconstructed_ancestor(),
+ // "How did we knew this?");
+ if reconstructed {
+ 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, restyled: bool) {
+ if restyled {
+ self.flags.insert(WAS_RESTYLED);
+ }
+ }
+
+ fn is_empty(&self) -> bool {
+ self.hint.is_empty() && self.damage.is_empty() && self.flags.is_empty()
+ }
+
+ fn contains_restyle_data(&self) -> bool {
+ !self.is_empty()
}
}
/// Style system data associated with an Element.
///
/// In Gecko, this hangs directly off the Element. Servo, this is embedded
/// inside of layout data, which itself hangs directly off the Element. In
/// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety.
#[derive(Debug)]
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.
#[derive(Debug)]
pub enum RestyleKind {
/// We need to run selector matching plus re-cascade, that is, a full
/// restyle.
MatchAndCascade,
@@ -397,16 +452,30 @@ pub enum RestyleKind {
/// attribute, or animation rules.
CascadeWithReplacements(RestyleHint),
/// We only need to recascade, for example, because only inherited
/// properties in the parent changed.
CascadeOnly,
}
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)
+ }
+
+ /// Whether this element was restyled, or has any other restyle state that
+ /// may make us do a post-traversal.
+ pub fn contains_restyle_data(&self) -> bool {
+ self.restyle.contains_restyle_data()
+ }
+
/// 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.
pub fn invalidate_style_if_needed<'a, E: TElement>(
&mut self,
element: E,
shared_context: &SharedStyleContext)
{
@@ -432,48 +501,45 @@ impl ElementData {
}
}
/// Trivially construct an ElementData.
pub fn new(existing: Option<ElementStyles>) -> Self {
ElementData {
styles: existing,
- restyle: None,
+ restyle: RestyleData::new(),
}
}
/// Returns true if this element has a computed style.
pub fn has_styles(&self) -> bool {
self.styles.is_some()
}
/// 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
/// element, based of the stored restyle hint.
pub fn restyle_kind(&self,
shared_context: &SharedStyleContext)
-> RestyleKind {
debug_assert!(!self.has_styles() || self.has_invalidations(),
"Should've stopped earlier");
if !self.has_styles() {
debug_assert!(!shared_context.traversal_flags.for_animation_only(),
"Unstyled element shouldn't be traversed during \
animation-only traversal");
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.
if hint.has_animation_hint() {
return RestyleKind::CascadeWithReplacements(hint & RestyleHint::for_animations());
}
return RestyleKind::CascadeOnly;
}
@@ -483,17 +549,18 @@ impl ElementData {
}
if hint.has_replacements() {
debug_assert!(!hint.has_animation_hint(),
"Animation only restyle hint should have already processed");
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;
}
/// Gets the element styles, if any.
pub fn get_styles(&self) -> Option<&ElementStyles> {
self.styles.as_ref()
}
@@ -508,23 +575,16 @@ impl ElementData {
}
/// Gets a mutable reference to the element styles. Panic if the element has
/// never been styled.
pub fn styles_mut(&mut self) -> &mut ElementStyles {
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);
}
/// Sets the computed element rules, and returns whether the rules changed.
pub fn set_primary_rules(&mut self, rules: StrongRuleNode) -> bool {
if !self.has_styles() {
@@ -553,57 +613,19 @@ impl ElementData {
guards: &StylesheetGuards) -> bool {
debug_assert!(self.has_styles());
let (important_rules, _custom) =
self.styles().primary.rules.get_properties_overriding_animations(&guards);
let (other_important_rules, _custom) = rules.get_properties_overriding_animations(&guards);
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.
pub fn get_smil_override(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> {
if cfg!(feature = "servo") {
// Servo has no knowledge of a SMIL rule, so just avoid looking for it.
return None;
}
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -562,18 +562,17 @@ pub trait TElement : Eq + PartialEq + De
fn has_css_transitions(&self) -> bool;
/// Returns true if the element has animation restyle hints.
fn has_animation_restyle_hints(&self) -> bool {
let data = match self.borrow_data() {
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.
fn get_declarations_from_xbl_bindings<V>(&self,
_pseudo_element: Option<&PseudoElement>,
_applicable_declarations: &mut V)
-> bool
where V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> {
--- a/servo/components/style/invalidation/element/invalidator.rs
+++ b/servo/components/style/invalidation/element/invalidator.rs
@@ -109,17 +109,17 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
// force a restyle here. Matching doesn't depend on the actual visited
// state at all, so we can't look at matching results to decide what to
// do for this case.
if state_changes.intersects(IN_VISITED_OR_UNVISITED_STATE) {
trace!(" > visitedness change, force subtree restyle");
// 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();
let mut classes_added = SmallVec::<[Atom; 8]>::new();
if snapshot.class_changed() {
// TODO(emilio): Do this more efficiently!
snapshot.each_class(|c| {
if !self.element.has_class(c, CaseSensitivity::CaseSensitive) {
@@ -206,17 +206,17 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
)
}
collector.invalidates_self
};
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);
}
}
debug!("Collected invalidations (self: {}): ", invalidated_self);
debug!(" > descendants: {:?}", descendant_invalidations);
debug!(" > siblings: {:?}", sibling_invalidations);
self.invalidate_descendants(&descendant_invalidations);
self.invalidate_siblings(&mut sibling_invalidations);
@@ -282,20 +282,18 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
debug!("StyleTreeInvalidator::invalidate_descendants({:?})",
self.element);
debug!(" > {:?}", invalidations);
match self.data {
None => return false,
Some(ref data) => {
- if let Some(restyle) = data.get_restyle() {
- if restyle.hint.contains_subtree() {
- return false;
- }
+ if data.restyle.hint.contains_subtree() {
+ return false;
}
}
}
let mut sibling_invalidations = InvalidationVector::new();
let mut any_children = false;
for child in self.element.as_node().traversal_children() {
@@ -489,17 +487,17 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
sibling_invalidations.push(next_invalidation);
}
}
CompoundSelectorMatchingResult::NotMatched => {}
}
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);
}
}
// TODO(emilio): For pseudo-elements this should be mostly false, except
// for the weird pseudos in <input type="number">.
//
// We should be able to do better here!
let effective_for_next =
--- a/servo/components/style/invalidation/stylesheets.rs
+++ b/servo/components/style/invalidation/stylesheets.rs
@@ -129,17 +129,17 @@ impl StylesheetInvalidationSet {
let mut data = match element.mutate_data() {
Some(data) => data,
None => return false,
};
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;
}
}
if self.invalid_scopes.is_empty() {
debug!("process_invalidations: empty invalidation set");
return false;
}
@@ -160,29 +160,27 @@ impl StylesheetInvalidationSet {
Some(data) => data,
None => return false,
};
if !data.has_styles() {
return false;
}
- if let Some(ref r) = data.get_restyle() {
- if r.hint.contains_subtree() {
- debug!("process_invalidations_in_subtree: {:?} was already invalid",
- element);
- return false;
- }
+ 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;
}
}
let mut any_children_invalid = false;
for child in element.as_node().traversal_children() {
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -692,18 +692,17 @@ trait PrivateMatchMethods: TElement {
// If an ancestor is already getting reconstructed by Gecko's top-down
// frame constructor, no need to apply damage. Similarly if we already
// have an explicitly stored ReconstructFrame hint.
//
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1301258#c12
// 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,
pseudo);
if !skip_applying_damage {
restyle.damage |= difference.damage;
}
difference.change.into()
@@ -1173,21 +1172,19 @@ pub trait MatchMethods : TElement {
matches_different_pseudos |= pseudos.remove_rules(
&pseudo,
visited_handling
);
}
});
if matches_different_pseudos {
- if let Some(r) = data.get_restyle_mut() {
- // Any changes to the matched pseudo-elements trigger
- // reconstruction.
- r.damage |= RestyleDamage::reconstruct();
- }
+ // Any changes to the matched pseudo-elements trigger
+ // reconstruction.
+ data.restyle.damage |= RestyleDamage::reconstruct();
}
}
/// Applies selector flags to an element, deferring mutations of the parent
/// until after the traversal.
///
/// TODO(emilio): This is somewhat inefficient, because of a variety of
/// reasons:
@@ -1242,26 +1239,21 @@ 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,
};
// ::before and ::after are element-backed in Gecko, so they do the
// damage calculation for themselves, when there's an actual pseudo.
let is_existing_before_or_after =
--- a/servo/components/style/sharing/mod.rs
+++ b/servo/components/style/sharing/mod.rs
@@ -372,48 +372,48 @@ impl<E: TElement> StyleSharingTarget<E>
// should cause a reconstruct).
// 2) We have restyle damage from the eager pseudo computed
// styles.
// 3) We have restyle damage from our own computed styles.
if data.has_styles() {
// 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 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 {
- // It's a bit unfortunate that we have to keep
- // mapping PseudoElements back to indices
- // here....
- for pseudo in old_pseudos.keys() {
- let old_values =
- old_pseudos.get(&pseudo).unwrap().values.as_ref().map(|v| &**v);
- let new_values =
- new_pseudos.get(&pseudo).unwrap().values();
- self.element.accumulate_damage(
- &shared_context,
- Some(restyle_data),
- old_values,
- new_values,
- Some(&pseudo)
- );
- }
+ 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 {
+ // It's a bit unfortunate that we have to keep
+ // mapping PseudoElements back to indices
+ // here....
+ for pseudo in old_pseudos.keys() {
+ let old_values =
+ old_pseudos.get(&pseudo).unwrap().values.as_ref().map(|v| &**v);
+ let new_values =
+ new_pseudos.get(&pseudo).unwrap().values();
+ self.element.accumulate_damage(
+ &shared_context,
+ restyle_data,
+ old_values,
+ new_values,
+ Some(&pseudo)
+ );
}
}
}
- 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
)
}
}
/// A cache miss result.
@@ -592,19 +592,16 @@ impl<E: TElement> StyleSharingCandidateC
&shared_context,
bloom_filter,
selector_flags_map
);
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,
data);
data.set_styles(shared_style);
return StyleSharingResult::StyleWasShared(i, child_cascade_requirement)
}
--- a/servo/components/style/traversal.rs
+++ b/servo/components/style/traversal.rs
@@ -5,17 +5,16 @@
//! Traversing the DOM tree; the bloom filter.
use atomic_refcell::AtomicRefCell;
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
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;
use std::borrow::BorrowMut;
/// A per-traversal-level chunk of data. This is sent down by the traversal, and
/// currently only holds the dom depth for the bloom filter.
///
@@ -291,20 +290,18 @@ pub trait DomTraversal<E: TElement> : Sy
// pseudo-element in order to properly process potentially-new
// transitions that we won't see otherwise.
//
// But it may be that we no longer match, so detect that case
// and act appropriately here.
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() {
if pseudo.is_before_or_after() {
is_before_or_after_pseudo = true;
let still_match =
parent_data.styles().pseudos.get(&pseudo).is_some();
@@ -332,19 +329,18 @@ pub trait DomTraversal<E: TElement> : Sy
if el.has_animation_only_dirty_descendants() {
return true;
}
let data = match el.borrow_data() {
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
// matter what. Skip examining the ElementData.
if el.has_dirty_descendants() {
return true;
}
@@ -355,38 +351,35 @@ pub trait DomTraversal<E: TElement> : Sy
None => return true,
};
// If we don't have any style data, we need to visit the element.
if !data.has_styles() {
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.
- //
- // Note that this is different than checking has_current_styles(),
- // 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() {
- return true;
- }
+ // If we have a restyle hint or need to recascade, we need to
+ // visit the element.
+ //
+ // Note that this is different than checking has_current_styles(),
+ // 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 !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
// fixup / reconstruction on our way back up the tree.
//
// 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;
}
trace!("{:?} doesn't need traversal", el);
false
}
/// Returns true if traversal of this element's children is allowed. We use
@@ -685,30 +678,27 @@ pub fn recalc_style_at<E, D>(traversal:
debug!("{:?} style is display:none - clearing data from descendants.",
element);
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
}
}
// 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) => {
- debug_assert!(context.shared.traversal_flags.for_animation_only() ||
- !r.hint.has_animation_hint(),
- "animation restyle hint should be handled during \
- animation-only restyles");
- r.hint.propagate(&context.shared.traversal_flags)
- },
+ let mut propagated_hint = {
+ debug_assert!(context.shared.traversal_flags.for_animation_only() ||
+ !data.restyle.hint.has_animation_hint(),
+ "animation restyle hint should be handled during \
+ animation-only restyles");
+ 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={:?}",
propagated_hint,
data.styles().is_display_none(),
element.implemented_pseudo_element());
debug_assert!(element.has_current_styles(data) ||
context.shared.traversal_flags.for_animation_only(),
@@ -725,33 +715,32 @@ pub fn recalc_style_at<E, D>(traversal:
// Preprocess children, propagating restyle hints and handling sibling
// relationships.
if traversal.should_traverse_children(&mut context.thread_local,
element,
&data,
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,
propagated_hint,
reconstructed_ancestor,
)
}
// If we are in a restyle for reconstruction, drop the existing restyle
// 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() {
unsafe { element.unset_animation_only_dirty_descendants(); }
}
// There are two cases when we want to clear the dity descendants bit
// here after styling this element.
@@ -787,16 +776,19 @@ fn compute_style<E, D>(_traversal: &D,
use data::RestyleKind::*;
use sharing::StyleSharingResult::*;
context.thread_local.statistics.elements_styled += 1;
let kind = data.restyle_kind(context.shared);
debug!("compute_style: {:?} (kind={:?})", element, kind);
+ let had_styles = data.has_styles();
+ data.restyle.set_restyled(had_styles);
+
match kind {
MatchAndCascade => {
debug_assert!(!context.shared.traversal_flags.for_animation_only(),
"MatchAndCascade shouldn't be processed during \
animation-only traversal");
// Ensure the bloom filter is up to date.
context.thread_local.bloom_filter
.insert_parents_recovering(element,
@@ -865,41 +857,32 @@ where
let mut child_data =
unsafe { D::ensure_element_data(&child).borrow_mut() };
// If the child is unstyled, we don't need to set up any restyling.
if !child_data.has_styles() {
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);
+ child_data.restyle.set_reconstructed_ancestor(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);
}
}
/// Clear style data for all the subtree under `el`.
pub fn clear_descendant_data<E: TElement, F: Fn(E)>(el: E, clear_data: &F) {
for kid in el.as_node().traversal_children() {
if let Some(kid) = kid.as_element() {
// We maintain an invariant that, if an element has data, all its ancestors
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -296,17 +296,17 @@ pub extern "C" fn Servo_TraverseSubtree(
if restyle_behavior == Restyle::ForNewlyBoundElement {
// In this mode, we only ever restyle new elements, so there is no
// need for a post-traversal, and the borrow_data().unwrap() call below
// could panic, so we don't bother computing whether a post-traversal
// is required.
return false;
}
- element.has_dirty_descendants() || element.borrow_data().unwrap().has_restyle()
+ element.has_dirty_descendants() || element.borrow_data().unwrap().contains_restyle_data()
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValues_Interpolate(from: RawServoAnimationValueBorrowed,
to: RawServoAnimationValueBorrowed,
progress: f64)
-> RawServoAnimationValueStrong
{
@@ -2452,17 +2452,17 @@ unsafe fn maybe_restyle<'a>(data: &'a mu
} else {
p.note_descendants::<DirtyDescendants>();
}
};
bindings::Gecko_SetOwnerDocumentNeedsStyleFlush(element.0);
// Ensure and return the RestyleData.
- Some(data.ensure_restyle())
+ Some(&mut data.restyle)
}
#[no_mangle]
pub extern "C" fn Servo_Element_GetStyleRuleList(element: RawGeckoElementBorrowed,
rules: RawGeckoServoStyleRuleListBorrowedMut) {
let element = GeckoElement(element);
let data = match element.borrow_data() {
Some(element_data) => element_data,
@@ -2509,23 +2509,26 @@ pub extern "C" fn Servo_NoteExplicitHint
debug!("(Element not styled, discarding hints)");
}
}
#[no_mangle]
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 {
- warn!("Trying to get change hint from unstyled element");
- GeckoRestyleDamage::empty()
+ 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);
damage.as_change_hint()
}
#[no_mangle]
pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
--- a/servo/tests/unit/stylo/size_of.rs
+++ b/servo/tests/unit/stylo/size_of.rs
@@ -2,17 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use selectors::gecko_like_types as dummies;
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};
#[test]
fn size_of_selectors_dummy_types() {
assert_eq!(size_of::<dummies::PseudoClass>(), size_of::<real::NonTSPseudoClass>());
assert_eq!(align_of::<dummies::PseudoClass>(), align_of::<real::NonTSPseudoClass>());
@@ -29,16 +29,17 @@ fn size_of_selectors_dummy_types() {
// selectors (with the inline hashes) with as few cache misses as possible.
size_of_test!(test_size_of_rule, style::stylist::Rule, 32);
size_of_test!(test_size_of_option_arc_cv, Option<Arc<ComputedValues>>, 8);
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);
size_of_test!(test_size_of_application_declaration_block, ApplicableDeclarationBlock, 24);
// FIXME(bholley): This can shrink with a little bit of work.
// See https://github.com/servo/servo/issues/17280
size_of_test!(test_size_of_rule_node, RuleNode, 96);