Bug 1355343: Take all the snapshots into account. r=bholley

I've chosen this approach mainly because there's no other good way to guarantee
the model is correct than holding the snapshots alive until a style refresh.

What I tried before this (storing them in a sort of "immutable element data") is
a pain, since we call into style from the frame constructor and other content
notifications, which makes keeping track of which snapshots should be cleared an
which shouldn't an insane task.

Ideally we'd have a single entry-point for style, but that's not the case right
now, and changing that requires pretty non-trivial changes to the frame
constructor.

MozReview-Commit-ID: FF1KWZv2iBM
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
Emilio Cobos Álvarez 2017-05-07 16:36:47 +02:00
parent 7d45aad9b4
commit 46bf5d61f0
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
16 changed files with 439 additions and 308 deletions

View file

@ -13,7 +13,6 @@ use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
use selector_parser::RestyleDamage;
#[cfg(feature = "servo")] use servo_config::opts;
use std::borrow::BorrowMut;
use stylist::Stylist;
/// 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.
@ -200,7 +199,9 @@ pub trait DomTraversal<E: TElement> : Sync {
/// appended children without restyling the parent.
/// If traversal_flag::ANIMATION_ONLY is specified, style only elements for
/// animations.
fn pre_traverse(root: E, stylist: &Stylist, traversal_flags: TraversalFlags)
fn pre_traverse(root: E,
shared_context: &SharedStyleContext,
traversal_flags: TraversalFlags)
-> PreTraverseToken
{
debug_assert!(!(traversal_flags.for_reconstruct() &&
@ -226,14 +227,12 @@ pub trait DomTraversal<E: TElement> : Sync {
// Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which
// we propagate to the next sibling element.
if let Some(mut data) = root.mutate_data() {
if let Some(r) = data.get_restyle_mut() {
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);
}
let later_siblings = data.compute_final_hint(root, shared_context);
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);
}
}
}
@ -375,6 +374,7 @@ pub trait DomTraversal<E: TElement> : Sync {
return true;
}
trace!("{:?} doesn't need traversal", el);
false
}
@ -390,7 +390,7 @@ pub trait DomTraversal<E: TElement> : Sync {
log: LogBehavior) -> bool
{
// See the comment on `cascade_node` for why we allow this on Gecko.
debug_assert!(cfg!(feature = "gecko") || parent_data.has_current_styles());
debug_assert!(cfg!(feature = "gecko") || parent.has_current_styles(parent_data));
// If the parent computed display:none, we don't style the subtree.
if parent_data.styles().is_display_none() {
@ -597,11 +597,13 @@ pub fn recalc_style_at<E, D>(traversal: &D,
{
context.thread_local.begin_element(element, &data);
context.thread_local.statistics.elements_traversed += 1;
debug_assert!(!element.has_snapshot() || element.handled_snapshot(),
"Should've handled snapshots here already");
debug_assert!(data.get_restyle().map_or(true, |r| {
r.snapshot.is_none() && !r.has_sibling_invalidations()
!r.has_sibling_invalidations()
}), "Should've computed the final hint and handled later_siblings already");
let compute_self = !data.has_current_styles();
let compute_self = !element.has_current_styles(data);
let mut inherited_style_changed = false;
debug!("recalc_style_at: {:?} (compute_self={:?}, dirty_descendants={:?}, data={:?})",
@ -613,9 +615,9 @@ pub fn recalc_style_at<E, D>(traversal: &D,
// If we're restyling this element to display:none, throw away all style
// data in the subtree, notify the caller to early-return.
let display_none = data.styles().is_display_none();
if display_none {
debug!("New element style is display:none - clearing data from descendants.");
if data.styles().is_display_none() {
debug!("{:?} style is display:none - clearing data from descendants.",
element);
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
}
@ -636,15 +638,15 @@ pub fn recalc_style_at<E, D>(traversal: &D,
r.hint.propagate(&context.shared.traversal_flags)
},
};
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={:?}, \
is_display_none={:?}, implementing_pseudo={:?}",
propagated_hint, inherited_style_changed,
data.styles().is_display_none(),
element.implemented_pseudo_element());
debug_assert!(element.has_current_styles(data) ||
context.shared.traversal_flags.for_animation_only(),
"Should have computed style or haven't yet valid computed \
style in case of animation-only restyle");
let has_dirty_descendants_for_this_restyle =
if context.shared.traversal_flags.for_animation_only() {
@ -779,6 +781,18 @@ fn preprocess_children<E, D>(traversal: &D,
continue;
}
// Handle element snapshots and sibling restyle hints.
//
// NB: This will be a no-op if there's no restyle data and no snapshot.
let later_siblings =
child_data.compute_final_hint(child, traversal.shared_context());
trace!(" > {:?} -> {:?} + {:?}, later_siblings: {:?}",
child,
child_data.get_restyle().map(|r| &r.hint),
propagated_hint,
later_siblings);
// 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.
@ -786,6 +800,7 @@ fn preprocess_children<E, D>(traversal: &D,
damage_handled.is_empty() && !child_data.has_restyle() {
continue;
}
let mut restyle_data = child_data.ensure_restyle();
// Propagate the parent and sibling restyle hint.
@ -793,11 +808,6 @@ fn preprocess_children<E, D>(traversal: &D,
restyle_data.hint.insert(&propagated_hint);
}
// Handle element snapshots and sibling restyle hints.
let stylist = &traversal.shared_context().stylist;
let later_siblings = restyle_data.compute_final_hint(child, stylist);
trace!(" > {:?} -> {:?}, later_siblings: {:?}",
child, restyle_data.hint, later_siblings);
if later_siblings {
propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into());
}
@ -805,9 +815,11 @@ fn preprocess_children<E, D>(traversal: &D,
// Store the damage already handled by ancestors.
restyle_data.set_damage_handled(damage_handled);
// If properties that we inherited from the parent changed, we need to recascade.
// If properties that we inherited from the parent changed, we need to
// recascade.
//
// FIXME(bholley): Need to handle explicitly-inherited reset properties somewhere.
// FIXME(bholley): Need to handle explicitly-inherited reset properties
// somewhere.
if parent_inherited_style_changed {
restyle_data.recascade = true;
}