style: Handle TraversalRestyleBehavior::ForReconstruct in the Servo restyle.

This commit is contained in:
Cameron McCormack 2017-04-04 19:32:47 +08:00
parent 2bbeb21551
commit 3f71c80e2f
5 changed files with 89 additions and 23 deletions

View file

@ -31,12 +31,14 @@ pub struct PerLevelTraversalData {
}
bitflags! {
/// Represents that target elements of the traversal.
/// Flags that control the traversal process.
pub flags TraversalFlags: u8 {
/// Traverse only unstyled children.
const UNSTYLED_CHILDREN_ONLY = 0x01,
/// Traverse only elements for animation restyles
/// Traverse only elements for animation restyles.
const ANIMATION_ONLY = 0x02,
/// Traverse without generating any change hints.
const FOR_RECONSTRUCT = 0x04,
}
}
@ -50,6 +52,11 @@ impl TraversalFlags {
pub fn for_unstyled_children_only(&self) -> bool {
self.contains(UNSTYLED_CHILDREN_ONLY)
}
/// Returns true if the traversal is for a frame reconstruction.
pub fn for_reconstruct(&self) -> bool {
self.contains(FOR_RECONSTRUCT)
}
}
/// This structure exists to enforce that callers invoke pre_traverse, and also
@ -148,6 +155,10 @@ pub trait DomTraversal<E: TElement> : Sync {
fn pre_traverse(root: E, stylist: &Stylist, traversal_flags: TraversalFlags)
-> PreTraverseToken
{
debug_assert!(!(traversal_flags.for_reconstruct() &&
traversal_flags.for_unstyled_children_only()),
"must not specify FOR_RECONSTRUCT in combination with 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()) {
return PreTraverseToken {
@ -165,12 +176,18 @@ pub trait DomTraversal<E: TElement> : Sync {
// we need a special case for the root.
//
// Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which
// we will drop on the floor. To prevent missed restyles, we assert against
// restyling a root with later siblings.
// we propagate to the next sibling element.
if let Some(mut data) = root.mutate_data() {
if let Some(r) = data.get_restyle_mut() {
debug_assert!(root.next_sibling_element().is_none());
let _later_siblings = r.compute_final_hint(root, stylist);
let later_siblings = r.compute_final_hint(root, stylist);
if later_siblings {
if let Some(next) = root.next_sibling_element() {
if let Some(mut next_data) = next.mutate_data() {
let hint = StoredRestyleHint::subtree_and_later_siblings();
next_data.ensure_restyle().hint.insert(&hint);
}
}
}
}
}
@ -195,6 +212,10 @@ pub trait DomTraversal<E: TElement> : Sync {
return true;
}
if traversal_flags.for_reconstruct() {
return true;
}
match node.as_element() {
None => Self::text_node_needs_traversal(node),
Some(el) => {
@ -264,7 +285,11 @@ pub trait DomTraversal<E: TElement> : Sync {
// 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.
if cfg!(feature = "servo") &&
//
// 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;
@ -342,11 +367,15 @@ pub trait DomTraversal<E: TElement> : Sync {
for kid in parent.as_node().children() {
if Self::node_needs_traversal(kid, self.shared_context().traversal_flags) {
let el = kid.as_element();
if el.as_ref().and_then(|el| el.borrow_data())
.map_or(false, |d| d.has_styles())
{
unsafe { parent.set_dirty_descendants(); }
// If we are in a restyle for reconstruction, there is no need to
// perform a post-traversal, so we don't need to set the dirty
// descendants bit on the parent.
if !self.shared_context().traversal_flags.for_reconstruct() {
let el = kid.as_element();
if el.as_ref().and_then(|el| el.borrow_data())
.map_or(false, |d| d.has_styles()) {
unsafe { parent.set_dirty_descendants(); }
}
}
f(thread_local, kid);
}
@ -557,20 +586,34 @@ pub fn recalc_style_at<E, D>(traversal: &D,
inherited_style_changed);
}
// 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();
}
if context.shared.traversal_flags.for_animation_only() {
unsafe { element.unset_animation_only_dirty_descendants(); }
}
// Make sure the dirty descendants bit is not set for the root of a
// display:none subtree, even if the style didn't change (since, if
// the style did change, we'd have already cleared it above).
// There are two cases when we want to clear the dity descendants bit
// here after styling this element.
//
// 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
// change, we'd have already cleared it above).
//
// This keeps the tree in a valid state without requiring the DOM to
// check display:none on the parent when inserting new children (which
// can be moderately expensive). Instead, DOM implementations can
// unconditionally set the dirty descendants bit on any styled parent,
// and let the traversal sort it out.
if data.styles().is_display_none() {
//
// 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() {
unsafe { element.unset_dirty_descendants(); }
}
}