diff --git a/components/style/data.rs b/components/style/data.rs index 7e42511b559..a1cc8b59f65 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -9,7 +9,7 @@ use dom::TElement; use properties::ComputedValues; use properties::longhands::display::computed_value as display; -use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; +use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; use rule_tree::StrongRuleNode; use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage, Snapshot}; #[cfg(feature = "servo")] use std::collections::HashMap; @@ -199,13 +199,13 @@ impl StoredRestyleHint { // In the middle of an animation only restyle, we don't need to // propagate any restyle hints, and we need to remove ourselves. if traversal_flags.for_animation_only() { - if self.0.contains(RESTYLE_CSS_ANIMATIONS) { - self.0.remove(RESTYLE_CSS_ANIMATIONS); + if self.0.intersects(RestyleHint::for_animations()) { + self.0.remove(RestyleHint::for_animations()); } return Self::empty(); } - debug_assert!(!self.0.contains(RESTYLE_CSS_ANIMATIONS), + debug_assert!(!self.0.intersects(RestyleHint::for_animations()), "There should not be any animation restyle hints \ during normal traversal"); @@ -259,7 +259,7 @@ impl StoredRestyleHint { /// Returns true if the hint has animation-only restyle. pub fn has_animation_hint(&self) -> bool { - self.0.contains(RESTYLE_CSS_ANIMATIONS) + self.0.intersects(RestyleHint::for_animations()) } } diff --git a/components/style/dom.rs b/components/style/dom.rs index ea2383f409e..0221160b3e9 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -14,6 +14,7 @@ use data::ElementData; use element_state::ElementState; use font_metrics::FontMetricsProvider; use properties::{ComputedValues, PropertyDeclarationBlock}; +use rule_tree::CascadeLevel; use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement}; use selectors::matching::ElementSelectorFlags; use shared_lock::Locked; @@ -320,6 +321,14 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + AnimationRules(None, None) } + /// Get this element's animation rule by the cascade level. + fn get_animation_rule_by_cascade(&self, + _pseudo: Option<&PseudoElement>, + _cascade_level: CascadeLevel) + -> Option>> { + None + } + /// Get this element's animation rule. fn get_animation_rule(&self, _pseudo: Option<&PseudoElement>) -> Option>> { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 610686ab6de..45dce1d304a 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -528,6 +528,17 @@ impl<'le> TElement for GeckoElement<'le> { self.get_transition_rule(pseudo)) } + fn get_animation_rule_by_cascade(&self, + pseudo: Option<&PseudoElement>, + cascade_level: ServoCascadeLevel) + -> Option>> { + match cascade_level { + ServoCascadeLevel::Animations => self.get_animation_rule(pseudo), + ServoCascadeLevel::Transitions => self.get_transition_rule(pseudo), + _ => panic!("Unsupported cascade level for getting the animation rule") + } + } + fn get_animation_rule(&self, pseudo: Option<&PseudoElement>) -> Option>> { get_animation_rule(self, pseudo, CascadeLevel::Animations) diff --git a/components/style/matching.rs b/components/style/matching.rs index 595f7a10eb9..80459785733 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -19,7 +19,7 @@ use dom::{AnimationRules, SendElement, TElement, TNode}; use font_metrics::FontMetricsProvider; use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade}; use properties::longhands::display::computed_value as display; -use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_CSS_ANIMATIONS, RestyleHint}; +use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleHint}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode}; use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl}; use selectors::bloom::BloomFilter; @@ -984,24 +984,43 @@ pub trait MatchMethods : TElement { } }; - // RESTYLE_CSS_ANIMATIONS is processed prior to other restyle hints - // in the name of animation-only traversal. Rest of restyle hints + // RESTYLE_CSS_ANIMATIONS or RESTYLE_CSS_TRANSITIONS is processed prior to other + // restyle hints in the name of animation-only traversal. Rest of restyle hints // will be processed in a subsequent normal traversal. - if hint.contains(RESTYLE_CSS_ANIMATIONS) { + if hint.intersects(RestyleHint::for_animations()) { debug_assert!(context.shared.traversal_flags.for_animation_only()); - let animation_rule = self.get_animation_rule(None); - replace_rule_node(CascadeLevel::Animations, - animation_rule.as_ref(), - primary_rules); - - let pseudos = &mut element_styles.pseudos; - for pseudo in pseudos.keys().iter().filter(|p| p.is_before_or_after()) { - let animation_rule = self.get_animation_rule(Some(&pseudo)); - let pseudo_rules = &mut pseudos.get_mut(&pseudo).unwrap().rules; - replace_rule_node(CascadeLevel::Animations, + use data::EagerPseudoStyles; + let mut replace_rule_node_for_animation = |level: CascadeLevel, + primary_rules: &mut StrongRuleNode, + pseudos: &mut EagerPseudoStyles| { + let animation_rule = self.get_animation_rule_by_cascade(None, level); + replace_rule_node(level, animation_rule.as_ref(), - pseudo_rules); + primary_rules); + + for pseudo in pseudos.keys().iter().filter(|p| p.is_before_or_after()) { + let animation_rule = self.get_animation_rule_by_cascade(Some(&pseudo), level); + let pseudo_rules = &mut pseudos.get_mut(&pseudo).unwrap().rules; + replace_rule_node(level, + animation_rule.as_ref(), + pseudo_rules); + } + }; + + // Apply Transition rules and Animation rules if the corresponding restyle hint + // is contained. + let pseudos = &mut element_styles.pseudos; + if hint.contains(RESTYLE_CSS_TRANSITIONS) { + replace_rule_node_for_animation(CascadeLevel::Transitions, + primary_rules, + pseudos); + } + + if hint.contains(RESTYLE_CSS_ANIMATIONS) { + replace_rule_node_for_animation(CascadeLevel::Animations, + primary_rules, + pseudos); } } else if hint.contains(RESTYLE_STYLE_ATTRIBUTE) { let style_attribute = self.style_attribute(); diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index 8256722494a..406de50b157 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -48,6 +48,11 @@ bitflags! { /// of their descendants. const RESTYLE_LATER_SIBLINGS = 0x08, + /// Replace the style data coming from CSS transitions without updating + /// any other style data. This hint is only processed in animation-only + /// traversal which is prior to normal traversal. + const RESTYLE_CSS_TRANSITIONS = 0x10, + /// Replace the style data coming from CSS animations without updating /// any other style data. This hint is only processed in animation-only /// traversal which is prior to normal traversal. @@ -87,6 +92,7 @@ pub fn assert_restyle_hints_match() { // (RESTYLE_SELF | RESTYLE_DESCENDANTS). nsRestyleHint_eRestyle_Subtree => RESTYLE_DESCENDANTS, nsRestyleHint_eRestyle_LaterSiblings => RESTYLE_LATER_SIBLINGS, + nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS, nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS, nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE, } @@ -96,7 +102,12 @@ impl RestyleHint { /// The subset hints that affect the styling of a single element during the /// traversal. pub fn for_self() -> Self { - RESTYLE_SELF | RESTYLE_STYLE_ATTRIBUTE | RESTYLE_CSS_ANIMATIONS + RESTYLE_SELF | RESTYLE_STYLE_ATTRIBUTE | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS + } + + /// The subset hints that are used for animation restyle. + pub fn for_animations() -> Self { + RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS } } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index d16fab6695a..8c7d2175a16 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -1788,12 +1788,15 @@ pub extern "C" fn Servo_NoteExplicitHints(element: RawGeckoElementBorrowed, debug!("Servo_NoteExplicitHints: {:?}, restyle_hint={:?}, change_hint={:?}", element, restyle_hint, change_hint); debug_assert!(restyle_hint == structs::nsRestyleHint_eRestyle_CSSAnimations || - (restyle_hint.0 & structs::nsRestyleHint_eRestyle_CSSAnimations.0) == 0, - "eRestyle_CSSAnimations should only appear by itself"); + restyle_hint == structs::nsRestyleHint_eRestyle_CSSTransitions || + (restyle_hint.0 & (structs::nsRestyleHint_eRestyle_CSSAnimations.0 | + structs::nsRestyleHint_eRestyle_CSSTransitions.0)) == 0, + "eRestyle_CSSAnimations or eRestyle_CSSTransitions should only appear by itself"); let mut maybe_data = element.mutate_data(); let maybe_restyle_data = maybe_data.as_mut().and_then(|d| unsafe { - maybe_restyle(d, element, restyle_hint == structs::nsRestyleHint_eRestyle_CSSAnimations) + maybe_restyle(d, element, restyle_hint == structs::nsRestyleHint_eRestyle_CSSAnimations || + restyle_hint == structs::nsRestyleHint_eRestyle_CSSTransitions) }); if let Some(restyle_data) = maybe_restyle_data { let restyle_hint: RestyleHint = restyle_hint.into();