Bug 1331047: Implement the new traversal semantics for stylo. r=bholley,hiro

MozReview-Commit-ID: 4BXx9JpGZKX
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
Emilio Cobos Álvarez 2017-04-22 23:12:01 +02:00
parent 85ad961104
commit be0139ff3c
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
11 changed files with 548 additions and 363 deletions

View file

@ -264,88 +264,118 @@ pub trait DomTraversal<E: TElement> : Sync {
return true;
}
match node.as_element() {
None => Self::text_node_needs_traversal(node),
Some(el) => {
// If the element is native-anonymous and an ancestor frame will
// be reconstructed, the child and all its descendants will be
// destroyed. In that case, we don't need to traverse the subtree.
if el.is_native_anonymous() {
if let Some(parent) = el.parent_element() {
let parent_data = parent.borrow_data().unwrap();
if let Some(r) = parent_data.get_restyle() {
if (r.damage | r.damage_handled()).contains(RestyleDamage::reconstruct()) {
debug!("Element {:?} is in doomed NAC subtree - culling traversal", el);
return false;
}
let el = match node.as_element() {
None => return Self::text_node_needs_traversal(node),
Some(el) => el,
};
// If the element is native-anonymous and an ancestor frame will
// be reconstructed, the child and all its descendants will be
// destroyed. In that case, we wouldn't need to traverse the
// subtree...
//
// Except if there could be transitions of pseudo-elements, in
// which
// case we still need to process them, unfortunately.
//
// We need to conservatively continue the traversal to style the
// 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.parent_element() {
let parent_data = parent.borrow_data().unwrap();
let going_to_reframe = parent_data.get_restyle().map_or(false, |r| {
(r.damage | r.damage_handled())
.contains(RestyleDamage::reconstruct())
});
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();
if !still_match {
debug_assert!(going_to_reframe,
"We're removing a pseudo, so we \
should reframe!");
return false;
}
}
}
// In case of animation-only traversal we need to traverse
// the element if the element has animation only dirty
// descendants bit, animation-only restyle hint or recascade.
if traversal_flags.for_animation_only() {
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.recascade);
if going_to_reframe && !is_before_or_after_pseudo {
debug!("Element {:?} is in doomed NAC subtree, \
culling traversal", el);
return false;
}
// 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;
}
// Check the element data. If it doesn't exist, we need to visit
// the element.
let data = match el.borrow_data() {
Some(d) => d,
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() || r.recascade {
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 != RestyleDamage::empty())
{
return true;
}
false
},
}
}
// In case of animation-only traversal we need to traverse
// the element if the element has animation only dirty
// descendants bit, animation-only restyle hint or recascade.
if traversal_flags.for_animation_only() {
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.recascade);
}
// 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;
}
// Check the element data. If it doesn't exist, we need to visit
// the element.
let data = match el.borrow_data() {
Some(d) => d,
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() || r.recascade {
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()) {
return true;
}
false
}
/// Returns true if traversal of this element's children is allowed. We use
@ -396,7 +426,6 @@ pub trait DomTraversal<E: TElement> : Sync {
}
return true;
}
/// Helper for the traversal implementations to select the children that
@ -489,7 +518,9 @@ fn resolve_style_internal<E, F>(context: &mut StyleContext<E>,
// Compute our style.
context.thread_local.begin_element(element, &data);
element.match_and_cascade(context, &mut data, StyleSharingBehavior::Disallow);
element.match_and_cascade(context,
&mut data,
StyleSharingBehavior::Disallow);
context.thread_local.end_element(element);
// Conservatively mark us as having dirty descendants, since there might
@ -607,9 +638,13 @@ pub fn recalc_style_at<E, D>(traversal: &D,
};
debug_assert!(data.has_current_styles() ||
context.shared.traversal_flags.for_animation_only(),
"Should have computed style or haven't yet valid computed style in case of animation-only restyle");
trace!("propagated_hint={:?}, inherited_style_changed={:?}",
propagated_hint, inherited_style_changed);
"Should have computed style or haven't yet valid computed \
style in case of animation-only restyle");
trace!("propagated_hint={:?}, inherited_style_changed={:?}, \
is_display_none={:?}, implementing_pseudo={:?}",
propagated_hint, inherited_style_changed,
data.styles().is_display_none(),
element.implemented_pseudo_element());
let has_dirty_descendants_for_this_restyle =
if context.shared.traversal_flags.for_animation_only() {
@ -664,7 +699,8 @@ pub fn recalc_style_at<E, D>(traversal: &D,
// The second case is when we are in a restyle for reconstruction,
// where we won't need to perform a post-traversal to pick up any
// change hints.
if data.styles().is_display_none() || context.shared.traversal_flags.for_reconstruct() {
if data.styles().is_display_none() ||
context.shared.traversal_flags.for_reconstruct() {
unsafe { element.unset_dirty_descendants(); }
}
}