mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Process animation-only traversal.
All traversal processes are like this: 1. Traverse only elements that have this restyle hint (animation-only traversal) RESTYLE_CSS_ANIMATIONS is stripped off from restyle hints of the elements 2. Traverse all dirty elements (normal traversal) 3. Create a SequentialTask if we have updated CSS Animations properties in the normal traversal 4. Traverse elements that need to have updated animation style as a result of 3 (second animation-only traversal)
This commit is contained in:
parent
be332fe5a4
commit
e4fe416c9d
2 changed files with 48 additions and 8 deletions
|
@ -134,6 +134,8 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
/// The traversal_flag is used in Gecko.
|
/// The traversal_flag 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
|
||||||
|
/// animations.
|
||||||
fn pre_traverse(root: E, stylist: &Stylist, traversal_flags: TraversalFlags)
|
fn pre_traverse(root: E, stylist: &Stylist, traversal_flags: TraversalFlags)
|
||||||
-> PreTraverseToken
|
-> PreTraverseToken
|
||||||
{
|
{
|
||||||
|
@ -158,7 +160,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
PreTraverseToken {
|
PreTraverseToken {
|
||||||
traverse: Self::node_needs_traversal(root.as_node()),
|
traverse: Self::node_needs_traversal(root.as_node(), traversal_flags.for_animation_only()),
|
||||||
unstyled_children_only: false,
|
unstyled_children_only: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,7 +174,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) -> bool {
|
fn node_needs_traversal(node: E::ConcreteNode, animation_only: bool) -> bool {
|
||||||
// Non-incremental layout visits every node.
|
// Non-incremental layout visits every node.
|
||||||
if cfg!(feature = "servo") && opts::get().nonincremental_layout {
|
if cfg!(feature = "servo") && opts::get().nonincremental_layout {
|
||||||
return true;
|
return true;
|
||||||
|
@ -181,6 +183,22 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
match node.as_element() {
|
match node.as_element() {
|
||||||
None => Self::text_node_needs_traversal(node),
|
None => Self::text_node_needs_traversal(node),
|
||||||
Some(el) => {
|
Some(el) => {
|
||||||
|
// 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 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
|
// If the dirty descendants bit is set, we need to traverse no
|
||||||
// matter what. Skip examining the ElementData.
|
// matter what. Skip examining the ElementData.
|
||||||
if el.has_dirty_descendants() {
|
if el.has_dirty_descendants() {
|
||||||
|
@ -293,7 +311,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
for kid in parent.as_node().children() {
|
for kid in parent.as_node().children() {
|
||||||
if Self::node_needs_traversal(kid) {
|
if Self::node_needs_traversal(kid, self.shared_context().animation_only_restyle) {
|
||||||
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())
|
||||||
|
@ -471,21 +489,38 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
None => empty_hint,
|
None => empty_hint,
|
||||||
Some(r) => {
|
Some(r) => {
|
||||||
r.recascade = false;
|
r.recascade = false;
|
||||||
mem::replace(&mut r.hint, empty_hint).propagate()
|
if r.hint.has_animation_hint() {
|
||||||
|
debug_assert!(context.shared.animation_only_restyle,
|
||||||
|
"animation restyle hint should be handled during animation-only restyles");
|
||||||
|
// Drop animation restyle hint.
|
||||||
|
let propagated_hint = r.hint.propagate();
|
||||||
|
r.hint.remove_animation_hint();
|
||||||
|
propagated_hint
|
||||||
|
} else {
|
||||||
|
mem::replace(&mut r.hint, empty_hint).propagate()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
debug_assert!(data.has_current_styles());
|
debug_assert!(data.has_current_styles() || context.shared.animation_only_restyle,
|
||||||
|
"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);
|
trace!("propagated_hint={:?}, inherited_style_changed={:?}", propagated_hint, inherited_style_changed);
|
||||||
|
|
||||||
// Preprocess children, propagating restyle hints and handling sibling relationships.
|
// Preprocess children, propagating restyle hints and handling sibling relationships.
|
||||||
if traversal.should_traverse_children(&mut context.thread_local, element, &data, DontLog) &&
|
if traversal.should_traverse_children(&mut context.thread_local, element, &data, DontLog) &&
|
||||||
(element.has_dirty_descendants() || !propagated_hint.is_empty() || inherited_style_changed) {
|
((!context.shared.animation_only_restyle && element.has_dirty_descendants()) ||
|
||||||
|
(context.shared.animation_only_restyle && element.has_animation_only_dirty_descendants()) ||
|
||||||
|
!propagated_hint.is_empty() ||
|
||||||
|
inherited_style_changed) {
|
||||||
let damage_handled = data.get_restyle().map_or(RestyleDamage::empty(), |r| {
|
let damage_handled = data.get_restyle().map_or(RestyleDamage::empty(), |r| {
|
||||||
r.damage_handled() | r.damage.handled_for_descendants()
|
r.damage_handled() | r.damage.handled_for_descendants()
|
||||||
});
|
});
|
||||||
preprocess_children(traversal, element, propagated_hint, damage_handled, inherited_style_changed);
|
preprocess_children(traversal, element, propagated_hint, damage_handled, inherited_style_changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.shared.animation_only_restyle {
|
||||||
|
unsafe { element.unset_animation_only_dirty_descendants(); }
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the dirty descendants bit is not set for the root of a
|
// 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
|
// display:none subtree, even if the style didn't change (since, if
|
||||||
// the style did change, we'd have already cleared it above).
|
// the style did change, we'd have already cleared it above).
|
||||||
|
|
|
@ -83,8 +83,8 @@ use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
|
||||||
use style::supports::parse_condition_or_declaration;
|
use style::supports::parse_condition_or_declaration;
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use style::timer::Timer;
|
use style::timer::Timer;
|
||||||
|
use style::traversal::{ANIMATION_ONLY, UNSTYLED_CHILDREN_ONLY};
|
||||||
use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags};
|
use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags};
|
||||||
use style::traversal::UNSTYLED_CHILDREN_ONLY;
|
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use super::stylesheet_loader::StylesheetLoader;
|
use super::stylesheet_loader::StylesheetLoader;
|
||||||
|
|
||||||
|
@ -202,6 +202,11 @@ pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
|
||||||
_ => TraversalFlags::empty(),
|
_ => TraversalFlags::empty(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if element.has_animation_only_dirty_descendants() ||
|
||||||
|
element.has_animation_restyle_hints() {
|
||||||
|
traverse_subtree(element, raw_data, traversal_flags | ANIMATION_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
traverse_subtree(element, raw_data, traversal_flags);
|
traverse_subtree(element, raw_data, traversal_flags);
|
||||||
|
|
||||||
element.has_dirty_descendants() || element.mutate_data().unwrap().has_restyle()
|
element.has_dirty_descendants() || element.mutate_data().unwrap().has_restyle()
|
||||||
|
@ -1612,7 +1617,7 @@ pub extern "C" fn Servo_AssertTreeIsClean(root: RawGeckoElementBorrowed) {
|
||||||
|
|
||||||
let root = GeckoElement(root);
|
let root = GeckoElement(root);
|
||||||
fn assert_subtree_is_clean<'le>(el: GeckoElement<'le>) {
|
fn assert_subtree_is_clean<'le>(el: GeckoElement<'le>) {
|
||||||
debug_assert!(!el.has_dirty_descendants());
|
debug_assert!(!el.has_dirty_descendants() && !el.has_animation_only_dirty_descendants());
|
||||||
for child in el.as_node().children() {
|
for child in el.as_node().children() {
|
||||||
if let Some(child) = child.as_element() {
|
if let Some(child) = child.as_element() {
|
||||||
assert_subtree_is_clean(child);
|
assert_subtree_is_clean(child);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue