Auto merge of #17523 - emilio:cleanup-traversal, r=nox

style: Cleanup traversal.rs

Mostly reflowing comments that have become too long or two short with
refactorings, and using consistent indentation.

<!-- 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/17523)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-06-27 07:08:42 -07:00 committed by GitHub
commit f93fccac57

View file

@ -38,6 +38,7 @@ bitflags! {
/// Traverse without generating any change hints. /// Traverse without generating any change hints.
const FOR_RECONSTRUCT = 0x04, const FOR_RECONSTRUCT = 0x04,
/// Traverse triggered by CSS rule changes. /// Traverse triggered by CSS rule changes.
///
/// Traverse and update all elements with CSS animations since /// Traverse and update all elements with CSS animations since
/// @keyframes rules may have changed /// @keyframes rules may have changed
const FOR_CSS_RULE_CHANGES = 0x08, const FOR_CSS_RULE_CHANGES = 0x08,
@ -67,8 +68,8 @@ impl TraversalFlags {
self.contains(FOR_CSS_RULE_CHANGES) self.contains(FOR_CSS_RULE_CHANGES)
} }
/// Returns true if the traversal is to compute the default computed /// Returns true if the traversal is to compute the default computed styles
/// styles for an element. /// for an element.
pub fn for_default_styles(&self) -> bool { pub fn for_default_styles(&self) -> bool {
self.contains(FOR_DEFAULT_STYLES) self.contains(FOR_DEFAULT_STYLES)
} }
@ -102,7 +103,9 @@ pub enum LogBehavior {
} }
use self::LogBehavior::*; use self::LogBehavior::*;
impl LogBehavior { impl LogBehavior {
fn allow(&self) -> bool { matches!(*self, MayLog) } fn allow(&self) -> bool {
matches!(*self, MayLog)
}
} }
/// The kind of traversals we could perform. /// The kind of traversals we could perform.
@ -172,12 +175,13 @@ pub trait DomTraversal<E: TElement> : Sync {
/// The only communication between siblings is that they both /// The only communication between siblings is that they both
/// fetch-and-subtract the parent's children count. This makes it safe to /// fetch-and-subtract the parent's children count. This makes it safe to
/// call durign the parallel traversal. /// call durign the parallel traversal.
fn handle_postorder_traversal(&self, fn handle_postorder_traversal(
thread_local: &mut Self::ThreadLocalContext, &self,
root: OpaqueNode, thread_local: &mut Self::ThreadLocalContext,
mut node: E::ConcreteNode, root: OpaqueNode,
children_to_process: isize) mut node: E::ConcreteNode,
{ children_to_process: isize
) {
// If the postorder step is a no-op, don't bother. // If the postorder step is a no-op, don't bother.
if !Self::needs_postorder_traversal() { if !Self::needs_postorder_traversal() {
return; return;
@ -202,8 +206,8 @@ pub trait DomTraversal<E: TElement> : Sync {
node = parent.as_node(); node = parent.as_node();
} }
} else { } else {
// Otherwise record the number of children to process when the // Otherwise record the number of children to process when the time
// time comes. // comes.
node.as_element().unwrap() node.as_element().unwrap()
.store_children_to_process(children_to_process); .store_children_to_process(children_to_process);
} }
@ -213,19 +217,22 @@ pub trait DomTraversal<E: TElement> : Sync {
/// a traversal is needed. Returns a token that allows the caller to prove /// a traversal is needed. Returns a token that allows the caller to prove
/// that the call happened. /// that the call happened.
/// ///
/// The traversal_flag is used in Gecko. /// The traversal_flags is used in Gecko.
///
/// If traversal_flag::UNSTYLED_CHILDREN_ONLY is specified, style newly- /// If traversal_flag::UNSTYLED_CHILDREN_ONLY is specified, style newly-
/// appended children without restyling the parent. /// appended children without restyling the parent.
///
/// If traversal_flag::ANIMATION_ONLY is specified, style only elements for /// If traversal_flag::ANIMATION_ONLY is specified, style only elements for
/// animations. /// animations.
fn pre_traverse(root: E, fn pre_traverse(
shared_context: &SharedStyleContext, root: E,
traversal_flags: TraversalFlags) shared_context: &SharedStyleContext,
-> PreTraverseToken traversal_flags: TraversalFlags
{ ) -> PreTraverseToken {
debug_assert!(!(traversal_flags.for_reconstruct() && debug_assert!(!(traversal_flags.for_reconstruct() &&
traversal_flags.for_unstyled_children_only()), traversal_flags.for_unstyled_children_only()),
"must not specify FOR_RECONSTRUCT in combination with UNSTYLED_CHILDREN_ONLY"); "must not specify FOR_RECONSTRUCT in combination with \
UNSTYLED_CHILDREN_ONLY");
if traversal_flags.for_unstyled_children_only() { if traversal_flags.for_unstyled_children_only() {
if root.borrow_data().map_or(true, |d| d.has_styles() && d.styles.is_display_none()) { if root.borrow_data().map_or(true, |d| d.has_styles() && d.styles.is_display_none()) {
@ -253,16 +260,19 @@ pub trait DomTraversal<E: TElement> : Sync {
} }
} }
/// Returns true if traversal should visit a text node. The style system never /// Returns true if traversal should visit a text node. The style system
/// processes text nodes, but Servo overrides this to visit them for flow /// never processes text nodes, but Servo overrides this to visit them for
/// construction when necessary. /// flow construction when necessary.
fn text_node_needs_traversal(node: E::ConcreteNode) -> bool { fn text_node_needs_traversal(node: E::ConcreteNode) -> bool {
debug_assert!(node.is_text_node()); debug_assert!(node.is_text_node());
false false
} }
/// Returns true if traversal is needed for the given node and subtree. /// Returns true if traversal is needed for the given node and subtree.
fn node_needs_traversal(node: E::ConcreteNode, traversal_flags: TraversalFlags) -> bool { fn node_needs_traversal(
node: E::ConcreteNode,
traversal_flags: TraversalFlags
) -> bool {
// Non-incremental layout visits every node. // Non-incremental layout visits every node.
if is_servo_nonincremental_layout() { if is_servo_nonincremental_layout() {
return true; return true;
@ -277,21 +287,19 @@ pub trait DomTraversal<E: TElement> : Sync {
Some(el) => el, Some(el) => el,
}; };
// If the element is native-anonymous and an ancestor frame will // If the element is native-anonymous and an ancestor frame will be
// be reconstructed, the child and all its descendants will be // reconstructed, the child and all its descendants will be destroyed.
// destroyed. In that case, we wouldn't need to traverse the // In that case, we wouldn't need to traverse the subtree...
// subtree...
// //
// Except if there could be transitions of pseudo-elements, in // Except if there could be transitions of pseudo-elements, in which
// which
// case we still need to process them, unfortunately. // case we still need to process them, unfortunately.
// //
// We need to conservatively continue the traversal to style the // We need to conservatively continue the traversal to style the
// pseudo-element in order to properly process potentially-new // pseudo-element in order to properly process potentially-new
// transitions that we won't see otherwise. // transitions that we won't see otherwise.
// //
// But it may be that we no longer match, so detect that case // But it may be that we no longer match, so detect that case and act
// and act appropriately here. // appropriately here.
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();
@ -322,9 +330,9 @@ pub trait DomTraversal<E: TElement> : Sync {
} }
} }
// In case of animation-only traversal we need to traverse // In case of animation-only traversal we need to traverse the element
// the element if the element has animation only dirty // if the element has animation only dirty descendants bit,
// descendants bit, animation-only restyle hint or recascade. // animation-only restyle hint or recascade.
if traversal_flags.for_animation_only() { if traversal_flags.for_animation_only() {
// Skip elements that have no style data since animation-only // Skip elements that have no style data since animation-only
// restyle is not necessary for the elements. // restyle is not necessary for the elements.
@ -345,14 +353,14 @@ pub trait DomTraversal<E: TElement> : Sync {
data.restyle.hint.has_recascade_self(); data.restyle.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 matter
// matter what. Skip examining the ElementData. // what. Skip examining the ElementData.
if el.has_dirty_descendants() { if el.has_dirty_descendants() {
return true; return true;
} }
// Check the element data. If it doesn't exist, we need to visit // Check the element data. If it doesn't exist, we need to visit the
// the element. // element.
let data = match el.borrow_data() { let data = match el.borrow_data() {
Some(d) => d, Some(d) => d,
None => return true, None => return true,
@ -363,20 +371,20 @@ pub trait DomTraversal<E: TElement> : Sync {
return true; return true;
} }
// 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
// visit the element. // element.
// //
// Note that this is different than checking has_current_styles(), // Note that this is different than checking has_current_styles(),
// 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
// indicating that the element's descendants (but not necessarily // that the element's descendants (but not necessarily the element) need
// the element) need restyling. // restyling.
if !data.restyle.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
// we need to traverse any element with damage so that we can perform // to traverse any element with damage so that we can perform fixup /
// fixup / reconstruction on our way back up the tree. // reconstruction on our way back up the tree.
// //
// 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.
@ -394,18 +402,23 @@ pub trait DomTraversal<E: TElement> : Sync {
/// ///
/// This may be called multiple times when processing an element, so we pass /// This may be called multiple times when processing an element, so we pass
/// a parameter to keep the logs tidy. /// a parameter to keep the logs tidy.
fn should_traverse_children(&self, fn should_traverse_children(
thread_local: &mut ThreadLocalStyleContext<E>, &self,
parent: E, thread_local: &mut ThreadLocalStyleContext<E>,
parent_data: &ElementData, parent: E,
log: LogBehavior) -> bool parent_data: &ElementData,
{ log: LogBehavior
) -> bool {
// See the comment on `cascade_node` for why we allow this on Gecko. // See the comment on `cascade_node` for why we allow this on Gecko.
debug_assert!(cfg!(feature = "gecko") || parent.has_current_styles(parent_data)); debug_assert!(cfg!(feature = "gecko") ||
parent.has_current_styles(parent_data));
// If the parent computed display:none, we don't style the subtree. // If the parent computed display:none, we don't style the subtree.
if parent_data.styles.is_display_none() { if parent_data.styles.is_display_none() {
if log.allow() { debug!("Parent {:?} is display:none, culling traversal", parent); } if log.allow() {
debug!("Parent {:?} is display:none, culling traversal",
parent);
}
return false; return false;
} }
@ -416,14 +429,13 @@ pub trait DomTraversal<E: TElement> : Sync {
// explicit children to an insertion point (or hide them entirely). It // explicit children to an insertion point (or hide them entirely). It
// may also specify a scoped stylesheet, which changes the rules that // may also specify a scoped stylesheet, which changes the rules that
// apply within the subtree. These two effects can invalidate the result // apply within the subtree. These two effects can invalidate the result
// of property inheritance and selector matching (respectively) within the // of property inheritance and selector matching (respectively) within
// subtree. // the subtree.
// //
// To avoid wasting work, we defer initial styling of XBL subtrees // To avoid wasting work, we defer initial styling of XBL subtrees until
// until frame construction, which does an explicit traversal of the // frame construction, which does an explicit traversal of the unstyled
// unstyled children after shuffling the subtree. That explicit // children after shuffling the subtree. That explicit traversal may in
// traversal may in turn find other bound elements, which get handled // turn find other bound elements, which get handled in the same way.
// in the same way.
// //
// We explicitly avoid handling restyles here (explicitly removing or // We explicitly avoid handling restyles here (explicitly removing or
// changing bindings), since that adds complexity and is rarer. If it // changing bindings), since that adds complexity and is rarer. If it
@ -432,7 +444,10 @@ pub trait DomTraversal<E: TElement> : Sync {
// an Element is changed. // an Element is changed.
if cfg!(feature = "gecko") && thread_local.is_initial_style() && if cfg!(feature = "gecko") && thread_local.is_initial_style() &&
parent_data.styles.primary().has_moz_binding() { parent_data.styles.primary().has_moz_binding() {
if log.allow() { debug!("Parent {:?} has XBL binding, deferring traversal", parent); } if log.allow() {
debug!("Parent {:?} has XBL binding, deferring traversal",
parent);
}
return false; return false;
} }
@ -441,13 +456,24 @@ pub trait DomTraversal<E: TElement> : Sync {
/// Helper for the traversal implementations to select the children that /// Helper for the traversal implementations to select the children that
/// should be enqueued for processing. /// should be enqueued for processing.
fn traverse_children<F>(&self, thread_local: &mut Self::ThreadLocalContext, parent: E, mut f: F) fn traverse_children<F>(
where F: FnMut(&mut Self::ThreadLocalContext, E::ConcreteNode) &self,
thread_local: &mut Self::ThreadLocalContext,
parent: E,
mut f: F
)
where
F: FnMut(&mut Self::ThreadLocalContext, E::ConcreteNode)
{ {
// Check if we're allowed to traverse past this element. // Check if we're allowed to traverse past this element.
let should_traverse = let should_traverse =
self.should_traverse_children(thread_local.borrow_mut(), parent, self.should_traverse_children(
&parent.borrow_data().unwrap(), MayLog); thread_local.borrow_mut(),
parent,
&parent.borrow_data().unwrap(),
MayLog
);
thread_local.borrow_mut().end_element(parent); thread_local.borrow_mut().end_element(parent);
if !should_traverse { if !should_traverse {
return; return;
@ -461,7 +487,7 @@ pub trait DomTraversal<E: TElement> : Sync {
if !self.shared_context().traversal_flags.for_reconstruct() { if !self.shared_context().traversal_flags.for_reconstruct() {
let el = kid.as_element(); let el = kid.as_element();
if el.as_ref().and_then(|el| el.borrow_data()) if el.as_ref().and_then(|el| el.borrow_data())
.map_or(false, |d| d.has_styles()) { .map_or(false, |d| d.has_styles()) {
if self.shared_context().traversal_flags.for_animation_only() { if self.shared_context().traversal_flags.for_animation_only() {
unsafe { parent.set_animation_only_dirty_descendants(); } unsafe { parent.set_animation_only_dirty_descendants(); }
} else { } else {
@ -474,9 +500,9 @@ pub trait DomTraversal<E: TElement> : Sync {
} }
} }
/// Ensures the existence of the ElementData, and returns it. This can't live /// Ensures the existence of the ElementData, and returns it. This can't
/// on TNode because of the trait-based separation between Servo's script /// live on TNode because of the trait-based separation between Servo's
/// and layout crates. /// script and layout crates.
/// ///
/// This is only safe to call in top-down traversal before processing the /// This is only safe to call in top-down traversal before processing the
/// children of |element|. /// children of |element|.
@ -504,9 +530,10 @@ pub trait DomTraversal<E: TElement> : Sync {
} }
/// Helper for the function below. /// Helper for the function below.
fn resolve_style_internal<E, F>(context: &mut StyleContext<E>, fn resolve_style_internal<E, F>(
element: E, ensure_data: &F) context: &mut StyleContext<E>,
-> Option<E> element: E, ensure_data: &F
) -> Option<E>
where E: TElement, where E: TElement,
F: Fn(E), F: Fn(E),
{ {
@ -539,16 +566,18 @@ fn resolve_style_internal<E, F>(context: &mut StyleContext<E>,
context.thread_local.end_element(element); context.thread_local.end_element(element);
if !context.shared.traversal_flags.for_default_styles() { if !context.shared.traversal_flags.for_default_styles() {
// Conservatively mark us as having dirty descendants, since there might // Conservatively mark us as having dirty descendants, since there
// be other unstyled siblings we miss when walking straight up the parent // might be other unstyled siblings we miss when walking straight up
// chain. No need to do this if we're computing default styles, since // the parent chain.
//
// No need to do this if we're computing default styles, since
// resolve_default_style will want the tree to be left as it is. // resolve_default_style will want the tree to be left as it is.
unsafe { element.note_descendants::<DirtyDescendants>() }; unsafe { element.note_descendants::<DirtyDescendants>() };
} }
} }
// If we're display:none and none of our ancestors are, we're the root // If we're display:none and none of our ancestors are, we're the root of a
// of a display:none subtree. // display:none subtree.
if display_none_root.is_none() && data.styles.is_display_none() { if display_none_root.is_none() && data.styles.is_display_none() {
display_none_root = Some(element); display_none_root = Some(element);
} }
@ -557,8 +586,8 @@ fn resolve_style_internal<E, F>(context: &mut StyleContext<E>,
} }
/// Manually resolve style by sequentially walking up the parent chain to the /// Manually resolve style by sequentially walking up the parent chain to the
/// first styled Element, ignoring pending restyles. The resolved style is /// first styled Element, ignoring pending restyles. The resolved style is made
/// made available via a callback, and can be dropped by the time this function /// available via a callback, and can be dropped by the time this function
/// returns in the display:none subtree case. /// returns in the display:none subtree case.
pub fn resolve_style<E, F, G, H>(context: &mut StyleContext<E>, element: E, pub fn resolve_style<E, F, G, H>(context: &mut StyleContext<E>, element: E,
ensure_data: &F, clear_data: &G, callback: H) ensure_data: &F, clear_data: &G, callback: H)
@ -574,14 +603,15 @@ pub fn resolve_style<E, F, G, H>(context: &mut StyleContext<E>, element: E,
let display_none_root = resolve_style_internal(context, element, ensure_data); let display_none_root = resolve_style_internal(context, element, ensure_data);
// Make them available for the scope of the callback. The callee may use the // Make them available for the scope of the callback. The callee may use the
// argument, or perform any other processing that requires the styles to exist // argument, or perform any other processing that requires the styles to
// on the Element. // exist on the Element.
callback(&element.borrow_data().unwrap().styles); callback(&element.borrow_data().unwrap().styles);
// Clear any styles in display:none subtrees or subtrees not in the document, // Clear any styles in display:none subtrees or subtrees not in the
// to leave the tree in a valid state. For display:none subtrees, we leave // document, to leave the tree in a valid state. For display:none subtrees,
// the styles on the display:none root, but for subtrees not in the document, // we leave the styles on the display:none root, but for subtrees not in the
// we clear styles all the way up to the root of the disconnected subtree. // document, we clear styles all the way up to the root of the disconnected
// subtree.
let in_doc = element.as_node().is_in_doc(); let in_doc = element.as_node().is_in_doc();
if !in_doc || display_none_root.is_some() { if !in_doc || display_none_root.is_some() {
let mut curr = element; let mut curr = element;
@ -606,15 +636,18 @@ pub fn resolve_style<E, F, G, H>(context: &mut StyleContext<E>, element: E,
/// only taking into account user agent and user cascade levels. The resolved /// only taking into account user agent and user cascade levels. The resolved
/// style is made available via a callback, and will be dropped by the time this /// style is made available via a callback, and will be dropped by the time this
/// function returns. /// function returns.
pub fn resolve_default_style<E, F, G, H>(context: &mut StyleContext<E>, pub fn resolve_default_style<E, F, G, H>(
element: E, context: &mut StyleContext<E>,
ensure_data: &F, element: E,
set_data: &G, ensure_data: &F,
callback: H) set_data: &G,
where E: TElement, callback: H
F: Fn(E), )
G: Fn(E, Option<ElementData>) -> Option<ElementData>, where
H: FnOnce(&ElementStyles) E: TElement,
F: Fn(E),
G: Fn(E, Option<ElementData>) -> Option<ElementData>,
H: FnOnce(&ElementStyles),
{ {
// Save and clear out element data from the element and its ancestors. // Save and clear out element data from the element and its ancestors.
let mut old_data: SmallVec<[(E, Option<ElementData>); 8]> = SmallVec::new(); let mut old_data: SmallVec<[(E, Option<ElementData>); 8]> = SmallVec::new();
@ -633,8 +666,8 @@ pub fn resolve_default_style<E, F, G, H>(context: &mut StyleContext<E>,
resolve_style_internal(context, element, ensure_data); resolve_style_internal(context, element, ensure_data);
// Make them available for the scope of the callback. The callee may use the // Make them available for the scope of the callback. The callee may use the
// argument, or perform any other processing that requires the styles to exist // argument, or perform any other processing that requires the styles to
// on the Element. // exist on the Element.
callback(&element.borrow_data().unwrap().styles); callback(&element.borrow_data().unwrap().styles);
// Swap the old element data back into the element and its ancestors. // Swap the old element data back into the element and its ancestors.
@ -646,13 +679,16 @@ pub fn resolve_default_style<E, F, G, H>(context: &mut StyleContext<E>,
/// Calculates the style for a single node. /// Calculates the style for a single node.
#[inline] #[inline]
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub fn recalc_style_at<E, D>(traversal: &D, pub fn recalc_style_at<E, D>(
traversal_data: &PerLevelTraversalData, traversal: &D,
context: &mut StyleContext<E>, traversal_data: &PerLevelTraversalData,
element: E, context: &mut StyleContext<E>,
data: &mut ElementData) element: E,
where E: TElement, data: &mut ElementData
D: DomTraversal<E> )
where
E: TElement,
D: DomTraversal<E>,
{ {
context.thread_local.begin_element(element, data); context.thread_local.begin_element(element, data);
context.thread_local.statistics.elements_traversed += 1; context.thread_local.statistics.elements_traversed += 1;
@ -662,7 +698,8 @@ pub fn recalc_style_at<E, D>(traversal: &D,
let compute_self = !element.has_current_styles(data); let compute_self = !element.has_current_styles(data);
let mut hint = RestyleHint::empty(); let mut hint = RestyleHint::empty();
debug!("recalc_style_at: {:?} (compute_self={:?}, dirty_descendants={:?}, data={:?})", debug!("recalc_style_at: {:?} (compute_self={:?}, \
dirty_descendants={:?}, data={:?})",
element, compute_self, element.has_dirty_descendants(), data); element, compute_self, element.has_dirty_descendants(), data);
// Compute style for this element if necessary. // Compute style for this element if necessary.
@ -677,8 +714,8 @@ pub fn recalc_style_at<E, D>(traversal: &D,
ChildCascadeRequirement::CanSkipCascade => {} ChildCascadeRequirement::CanSkipCascade => {}
}; };
// We must always cascade native anonymous subtrees, since they inherit styles // We must always cascade native anonymous subtrees, since they inherit
// from their first non-NAC ancestor. // styles from their first non-NAC ancestor.
if element.is_native_anonymous() { if element.is_native_anonymous() {
hint |= RECASCADE_SELF; hint |= RECASCADE_SELF;
} }
@ -725,12 +762,14 @@ pub fn recalc_style_at<E, D>(traversal: &D,
// Preprocess children, propagating restyle hints and handling sibling // Preprocess children, propagating restyle hints and handling sibling
// relationships. // relationships.
if traversal.should_traverse_children(&mut context.thread_local, let should_traverse_children = traversal.should_traverse_children(
element, &mut context.thread_local,
&data, element,
DontLog) && &data,
(has_dirty_descendants_for_this_restyle || DontLog
!propagated_hint.is_empty()) { );
if should_traverse_children &&
(has_dirty_descendants_for_this_restyle || !propagated_hint.is_empty()) {
let reconstructed_ancestor = let reconstructed_ancestor =
data.restyle.reconstructed_self_or_ancestor(); data.restyle.reconstructed_self_or_ancestor();
@ -753,36 +792,37 @@ pub fn recalc_style_at<E, D>(traversal: &D,
unsafe { element.unset_animation_only_dirty_descendants(); } unsafe { element.unset_animation_only_dirty_descendants(); }
} }
// There are two cases when we want to clear the dity descendants bit // There are two cases when we want to clear the dity descendants bit here
// here after styling this element. // after styling this element.
// //
// The first case is when this element is the root of a display:none // The first case is when this element is the root of a display:none
// subtree, even if the style didn't change (since, if the style did // subtree, even if the style didn't change (since, if the style did change,
// change, we'd have already cleared it above). // we'd have already cleared it above).
// //
// This keeps the tree in a valid state without requiring the DOM to // This keeps the tree in a valid state without requiring the DOM to check
// check display:none on the parent when inserting new children (which // display:none on the parent when inserting new children (which can be
// can be moderately expensive). Instead, DOM implementations can // moderately expensive). Instead, DOM implementations can unconditionally
// unconditionally set the dirty descendants bit on any styled parent, // set the dirty descendants bit on any styled parent, and let the traversal
// and let the traversal sort it out. // sort it out.
// //
// The second case is when we are in a restyle for reconstruction, // The second case is when we are in a restyle for reconstruction, where we
// where we won't need to perform a post-traversal to pick up any // won't need to perform a post-traversal to pick up any change hints.
// change hints.
if data.styles.is_display_none() || if data.styles.is_display_none() ||
context.shared.traversal_flags.for_reconstruct() { context.shared.traversal_flags.for_reconstruct() {
unsafe { element.unset_dirty_descendants(); } unsafe { element.unset_dirty_descendants(); }
} }
} }
fn compute_style<E, D>(_traversal: &D, fn compute_style<E, D>(
traversal_data: &PerLevelTraversalData, _traversal: &D,
context: &mut StyleContext<E>, traversal_data: &PerLevelTraversalData,
element: E, context: &mut StyleContext<E>,
data: &mut ElementData) element: E,
-> ChildCascadeRequirement data: &mut ElementData
where E: TElement, ) -> ChildCascadeRequirement
D: DomTraversal<E>, where
E: TElement,
D: DomTraversal<E>,
{ {
use data::RestyleKind::*; use data::RestyleKind::*;
use sharing::StyleSharingResult::*; use sharing::StyleSharingResult::*;
@ -830,9 +870,8 @@ fn compute_style<E, D>(_traversal: &D,
} }
CascadeWithReplacements(flags) => { CascadeWithReplacements(flags) => {
// Skipping full matching, load cascade inputs from previous values. // Skipping full matching, load cascade inputs from previous values.
context.thread_local.current_element_info *context.cascade_inputs_mut() =
.as_mut().unwrap() ElementCascadeInputs::new_from_element_data(data);
.cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
let important_rules_changed = element.replace_rules(flags, context); let important_rules_changed = element.replace_rules(flags, context);
element.cascade_primary_and_pseudos( element.cascade_primary_and_pseudos(
context, context,
@ -842,9 +881,8 @@ fn compute_style<E, D>(_traversal: &D,
} }
CascadeOnly => { CascadeOnly => {
// Skipping full matching, load cascade inputs from previous values. // Skipping full matching, load cascade inputs from previous values.
context.thread_local.current_element_info *context.cascade_inputs_mut() =
.as_mut().unwrap() ElementCascadeInputs::new_from_element_data(data);
.cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
element.cascade_primary_and_pseudos( element.cascade_primary_and_pseudos(
context, context,
data, data,
@ -904,12 +942,21 @@ where
} }
/// Clear style data for all the subtree under `el`. /// Clear style data for all the subtree under `el`.
pub fn clear_descendant_data<E: TElement, F: Fn(E)>(el: E, clear_data: &F) { pub fn clear_descendant_data<E, F>(
el: E,
clear_data: &F
)
where
E: TElement,
F: Fn(E),
{
for kid in el.as_node().traversal_children() { for kid in el.as_node().traversal_children() {
if let Some(kid) = kid.as_element() { if let Some(kid) = kid.as_element() {
// We maintain an invariant that, if an element has data, all its ancestors // We maintain an invariant that, if an element has data, all its
// have data as well. By consequence, any element without data has no // ancestors have data as well.
// descendants with data. //
// By consequence, any element without data has no descendants with
// data.
if kid.get_data().is_some() { if kid.get_data().is_some() {
clear_data(kid); clear_data(kid);
clear_descendant_data(kid, clear_data); clear_descendant_data(kid, clear_data);