From e4fe416c9d3ce81d1959c9f7043648d4cef4fd92 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Sun, 26 Mar 2017 18:47:34 +0900 Subject: [PATCH] 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) --- components/style/traversal.rs | 47 ++++++++++++++++++++++++++++++----- ports/geckolib/glue.rs | 9 +++++-- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 69e413d64a8..df70c29664c 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -134,6 +134,8 @@ pub trait DomTraversal : Sync { /// The traversal_flag is used in Gecko. /// If traversal_flag::UNSTYLED_CHILDREN_ONLY is specified, style newly- /// 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) -> PreTraverseToken { @@ -158,7 +160,7 @@ pub trait DomTraversal : Sync { } 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, } } @@ -172,7 +174,7 @@ pub trait DomTraversal : Sync { } /// 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. if cfg!(feature = "servo") && opts::get().nonincremental_layout { return true; @@ -181,6 +183,22 @@ pub trait DomTraversal : Sync { match node.as_element() { None => Self::text_node_needs_traversal(node), 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 // matter what. Skip examining the ElementData. if el.has_dirty_descendants() { @@ -293,7 +311,7 @@ pub trait DomTraversal : Sync { } 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(); if el.as_ref().and_then(|el| el.borrow_data()) .map_or(false, |d| d.has_styles()) @@ -471,21 +489,38 @@ pub fn recalc_style_at(traversal: &D, None => empty_hint, Some(r) => { 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); // Preprocess children, propagating restyle hints and handling sibling relationships. 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| { r.damage_handled() | r.damage.handled_for_descendants() }); 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 // display:none subtree, even if the style didn't change (since, if // the style did change, we'd have already cleared it above). diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index b1555cc7693..ac04ee62c3d 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -83,8 +83,8 @@ use style::stylesheets::StylesheetLoader as StyleStylesheetLoader; use style::supports::parse_condition_or_declaration; use style::thread_state; use style::timer::Timer; +use style::traversal::{ANIMATION_ONLY, UNSTYLED_CHILDREN_ONLY}; use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags}; -use style::traversal::UNSTYLED_CHILDREN_ONLY; use style_traits::ToCss; use super::stylesheet_loader::StylesheetLoader; @@ -202,6 +202,11 @@ pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed, _ => 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); 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); 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() { if let Some(child) = child.as_element() { assert_subtree_is_clean(child);