servo: Merge #15175 - Interpolate servo animation values and add them to the cascade (from BorisChiou:animation/interpolate_and_cascade); r=heycam
authorBoris Chiou <boris.chiou@gmail.com>
Wed, 25 Jan 2017 23:36:33 -0800
changeset 340652 407489790bb3fc4cc351a3f434a5ac1aa45482c6
parent 340651 622276ef25a651bde499da96b982b842487b36f1
child 340653 59941ce59674a6465cb2a7fd666e312cf92c93ec
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1317209
servo: Merge #15175 - Interpolate servo animation values and add them to the cascade (from BorisChiou:animation/interpolate_and_cascade); r=heycam These are the servo-side changes for [bug 1317209](https://bugzilla.mozilla.org/show_bug.cgi?id=1317209). @Manishearth, @emilio, and @heycam have already reviewed them there. Please merge these patches until the gecko-side changes for [bug 1317209](https://bugzilla.mozilla.org/show_bug.cgi?id=1317209) is landed. --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix [bug 1317209](https://bugzilla.mozilla.org/show_bug.cgi?id=1317209). - [X] These changes do not require tests because there are existing tests for this in mozilla-central Source-Repo: https://github.com/servo/servo Source-Revision: e4a1cb6f87ac1ba2035c833dca5d9619edeefb8d
servo/components/style/build_gecko.rs
servo/components/style/dom.rs
servo/components/style/gecko/wrapper.rs
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/gecko_bindings/structs_debug.rs
servo/components/style/gecko_bindings/structs_release.rs
servo/components/style/matching.rs
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
--- a/servo/components/style/build_gecko.rs
+++ b/servo/components/style/build_gecko.rs
@@ -486,16 +486,17 @@ mod bindings {
             .whitelisted_function("Servo_.*")
             .whitelisted_function("Gecko_.*");
         let structs_types = [
             "RawGeckoDocument",
             "RawGeckoElement",
             "RawGeckoNode",
             "RawGeckoAnimationValueList",
             "RawServoAnimationValue",
+            "RawServoAnimationValueList",
             "RawGeckoPresContext",
             "RawGeckoPresContextOwned",
             "ThreadSafeURIHolder",
             "ThreadSafePrincipalHolder",
             "CSSPseudoClassType",
             "TraversalRootBehavior",
             "FontFamilyList",
             "FontFamilyType",
@@ -588,16 +589,17 @@ mod bindings {
             "RawGeckoElement",
             "RawGeckoDocument",
             "RawServoDeclarationBlockStrong",
             "RawGeckoPresContext",
         ];
         let servo_borrow_types = [
             "nsCSSValue",
             "RawGeckoAnimationValueList",
+            "RawServoAnimationValueList",
         ];
         for &ty in structs_types.iter() {
             builder = builder.hide_type(ty)
                 .raw_line(format!("use gecko_bindings::structs::{};", ty));
             // TODO this is hacky, figure out a better way to do it without
             // hardcoding everything...
             if ty.starts_with("nsStyle") {
                 builder = builder
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -222,16 +222,21 @@ fn fmt_subtree<F, N: TNode>(f: &mut fmt:
 /// A trait used to synthesize presentational hints for HTML element attributes.
 pub trait PresentationalHintsSynthetizer {
     /// Generate the proper applicable declarations due to presentational hints,
     /// and insert them into `hints`.
     fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
         where V: Push<ApplicableDeclarationBlock>;
 }
 
+/// The animation rules. The first one is for Animation cascade level, and the second one is for
+/// Transition cascade level.
+pub struct AnimationRules(pub Option<Arc<RwLock<PropertyDeclarationBlock>>>,
+                          pub Option<Arc<RwLock<PropertyDeclarationBlock>>>);
+
 /// The element trait, the main abstraction the style crate acts over.
 pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer {
     /// The concrete node type.
     type ConcreteNode: TNode<ConcreteElement = Self>;
 
     /// Get this element as a node.
     fn as_node(&self) -> Self::ConcreteNode;
 
@@ -243,16 +248,21 @@ pub trait TElement : PartialEq + Debug +
         } else {
             self.parent_element()
         }
     }
 
     /// Get this element's style attribute.
     fn style_attribute(&self) -> Option<&Arc<RwLock<PropertyDeclarationBlock>>>;
 
+    /// Get this element's animation rules.
+    fn get_animation_rules(&self, _pseudo: Option<&PseudoElement>) -> AnimationRules {
+        AnimationRules(None, None)
+    }
+
     /// Get this element's state, for non-tree-structural pseudos.
     fn get_state(&self) -> ElementState;
 
     /// Whether this element has an attribute with a given namespace.
     fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool;
 
     /// Whether an attribute value equals `value`.
     fn attr_equals(&self, namespace: &Namespace, attr: &LocalName, value: &Atom) -> bool;
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -11,34 +11,36 @@
 //! `components/script/layout_wrapper.rs`.
 //!
 //! This theoretically should live in its own crate, but now it lives in the
 //! style system it's kind of pointless in the Stylo case, and only Servo forces
 //! the separation between the style system implementation and everything else.
 
 use atomic_refcell::AtomicRefCell;
 use data::ElementData;
-use dom::{LayoutIterator, NodeInfo, TElement, TNode, UnsafeNode};
+use dom::{AnimationRules, LayoutIterator, NodeInfo, TElement, TNode, UnsafeNode};
 use dom::{OpaqueNode, PresentationalHintsSynthetizer};
 use element_state::ElementState;
 use error_reporting::StdoutErrorReporter;
 use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement};
 use gecko::snapshot_helpers;
 use gecko_bindings::bindings;
 use gecko_bindings::bindings::{Gecko_DropStyleChildrenIterator, Gecko_MaybeCreateStyleChildrenIterator};
 use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetLastChild, Gecko_GetNextStyleChild};
 use gecko_bindings::bindings::{Gecko_GetServoDeclarationBlock, Gecko_IsHTMLElementInHTMLDocument};
 use gecko_bindings::bindings::{Gecko_IsLink, Gecko_IsRootElement, Gecko_MatchesElement};
 use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink, Gecko_Namespace};
 use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
 use gecko_bindings::bindings::Gecko_ClassOrClassList;
+use gecko_bindings::bindings::Gecko_GetAnimationRule;
 use gecko_bindings::bindings::Gecko_GetStyleContext;
 use gecko_bindings::structs;
 use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode};
 use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext};
+use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
 use gecko_bindings::structs::NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
 use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
 use parking_lot::RwLock;
 use parser::ParserContextExtraData;
 use properties::{ComputedValues, parse_style_attribute};
 use properties::PropertyDeclarationBlock;
 use selector_parser::{ElementExt, Snapshot};
 use selectors::Element;
@@ -330,16 +332,25 @@ impl<'le> TElement for GeckoElement<'le>
         unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
     }
 
     fn style_attribute(&self) -> Option<&Arc<RwLock<PropertyDeclarationBlock>>> {
         let declarations = unsafe { Gecko_GetServoDeclarationBlock(self.0) };
         declarations.map(|s| s.as_arc_opt()).unwrap_or(None)
     }
 
+    fn get_animation_rules(&self, pseudo: Option<&PseudoElement>) -> AnimationRules {
+        let atom_ptr = pseudo.map(|p| p.as_atom().as_ptr()).unwrap_or(ptr::null_mut());
+        unsafe {
+            AnimationRules(
+                Gecko_GetAnimationRule(self.0, atom_ptr, CascadeLevel::Animations).into_arc_opt(),
+                Gecko_GetAnimationRule(self.0, atom_ptr, CascadeLevel::Transitions).into_arc_opt())
+        }
+    }
+
     fn get_state(&self) -> ElementState {
         unsafe {
             ElementState::from_bits_truncate(Gecko_ElementState(self.0))
         }
     }
 
     #[inline]
     fn has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool {
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -1,18 +1,20 @@
 /* automatically generated by rust-bindgen */
 
 pub use nsstring::{nsACString, nsAString};
 type nsACString_internal = nsACString;
 type nsAString_internal = nsAString;
+use gecko_bindings::structs::EffectCompositor_CascadeLevel;
 use gecko_bindings::structs::RawGeckoDocument;
 use gecko_bindings::structs::RawGeckoElement;
 use gecko_bindings::structs::RawGeckoNode;
 use gecko_bindings::structs::RawGeckoAnimationValueList;
 use gecko_bindings::structs::RawServoAnimationValue;
+use gecko_bindings::structs::RawServoAnimationValueBorrowedList;
 use gecko_bindings::structs::RawGeckoPresContext;
 use gecko_bindings::structs::RawGeckoPresContextOwned;
 use gecko_bindings::structs::ThreadSafeURIHolder;
 use gecko_bindings::structs::ThreadSafePrincipalHolder;
 use gecko_bindings::structs::CSSPseudoClassType;
 use gecko_bindings::structs::TraversalRootBehavior;
 use gecko_bindings::structs::FontFamilyList;
 use gecko_bindings::structs::FontFamilyType;
@@ -219,16 +221,18 @@ pub type RawGeckoPresContextBorrowedOrNu
 pub type nsCSSValueBorrowed<'a> = &'a nsCSSValue;
 pub type nsCSSValueBorrowedOrNull<'a> = Option<&'a nsCSSValue>;
 pub type nsCSSValueBorrowedMut<'a> = &'a mut nsCSSValue;
 pub type nsCSSValueBorrowedMutOrNull<'a> = Option<&'a mut nsCSSValue>;
 pub type RawGeckoAnimationValueListBorrowed<'a> = &'a RawGeckoAnimationValueList;
 pub type RawGeckoAnimationValueListBorrowedOrNull<'a> = Option<&'a RawGeckoAnimationValueList>;
 pub type RawGeckoAnimationValueListBorrowedMut<'a> = &'a mut RawGeckoAnimationValueList;
 pub type RawGeckoAnimationValueListBorrowedMutOrNull<'a> = Option<&'a mut RawGeckoAnimationValueList>;
+pub type RawServoAnimationValueBorrowedListBorrowed<'a> = &'a RawServoAnimationValueBorrowedList;
+pub type RawServoAnimationValueBorrowedListBorrowedOrNull<'a> = Option<&'a RawServoAnimationValueBorrowedList>;
 
 extern "C" {
     pub fn Gecko_EnsureTArrayCapacity(aArray: *mut ::std::os::raw::c_void,
                                       aCapacity: usize, aElementSize: usize);
 }
 extern "C" {
     pub fn Gecko_ClearPODTArray(aArray: *mut ::std::os::raw::c_void,
                                 aElementSize: usize, aElementAlign: usize);
@@ -494,16 +498,22 @@ extern "C" {
                                           classList: *mut *mut *mut nsIAtom)
      -> u32;
 }
 extern "C" {
     pub fn Gecko_GetServoDeclarationBlock(element: RawGeckoElementBorrowed)
      -> RawServoDeclarationBlockStrongBorrowedOrNull;
 }
 extern "C" {
+    pub fn Gecko_GetAnimationRule(element: RawGeckoElementBorrowed,
+                                  aAtom: *mut nsIAtom,
+                                  aCascadeLevel: EffectCompositor_CascadeLevel)
+     -> RawServoDeclarationBlockStrong;
+}
+extern "C" {
     pub fn Gecko_Atomize(aString: *const ::std::os::raw::c_char, aLength: u32)
      -> *mut nsIAtom;
 }
 extern "C" {
     pub fn Gecko_AddRefAtom(aAtom: *mut nsIAtom);
 }
 extern "C" {
     pub fn Gecko_ReleaseAtom(aAtom: *mut nsIAtom);
@@ -1213,16 +1223,26 @@ extern "C" {
     pub fn Servo_RestyleWithAddedDeclaration(set: RawServoStyleSetBorrowed,
                                              declarations:
                                                  RawServoDeclarationBlockBorrowed,
                                              previous_style:
                                                  ServoComputedValuesBorrowed)
      -> ServoComputedValuesStrong;
 }
 extern "C" {
+    pub fn Servo_AnimationValues_Interpolate(from: RawServoAnimationValueBorrowed,
+                                             to: RawServoAnimationValueBorrowed,
+                                             progress: f64)
+     -> RawServoAnimationValueStrong;
+}
+extern "C" {
+    pub fn Servo_AnimationValues_Uncompute(value: RawServoAnimationValueBorrowedListBorrowed)
+     -> RawServoDeclarationBlockStrong;
+}
+extern "C" {
     pub fn Servo_AnimationValues_Populate(arg1:
                                               RawGeckoAnimationValueListBorrowedMut,
                                           arg2:
                                               RawServoDeclarationBlockBorrowed,
                                           arg3: ServoComputedValuesBorrowed,
                                           arg4:
                                               ServoComputedValuesBorrowedOrNull,
                                           arg5: RawGeckoPresContextBorrowed);
--- a/servo/components/style/gecko_bindings/structs_debug.rs
+++ b/servo/components/style/gecko_bindings/structs_debug.rs
@@ -5268,16 +5268,22 @@ pub mod root {
         #[test]
         fn bindgen_test_layout_StyleComplexColor() {
             assert_eq!(::std::mem::size_of::<StyleComplexColor>() , 8usize);
             assert_eq!(::std::mem::align_of::<StyleComplexColor>() , 4usize);
         }
         impl Clone for StyleComplexColor {
             fn clone(&self) -> Self { *self }
         }
+        #[repr(u32)]
+        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+        pub enum EffectCompositor_CascadeLevel {
+            Animations = 0,
+            Transitions = 1,
+        }
         #[repr(C)]
         #[derive(Debug)]
         pub struct PropertyStyleAnimationValuePair {
             pub mProperty: root::nsCSSPropertyID,
             pub mValue: root::mozilla::StyleAnimationValue,
             pub mServoValue: root::RefPtr<root::RawServoAnimationValue>,
         }
         #[test]
@@ -17948,16 +17954,20 @@ pub mod root {
     pub type RawGeckoElementBorrowedOrNull = *const root::RawGeckoElement;
     pub type RawGeckoDocumentBorrowed = *const root::RawGeckoDocument;
     pub type RawGeckoDocumentBorrowedOrNull = *const root::RawGeckoDocument;
     pub type RawGeckoPresContextOwned = *mut root::RawGeckoPresContext;
     pub type RawGeckoPresContextBorrowed = *const root::RawGeckoPresContext;
     pub type RawGeckoPresContextBorrowedMut = *mut root::RawGeckoPresContext;
     pub type RawGeckoAnimationValueListBorrowedMut =
         *mut root::RawGeckoAnimationValueList;
+    pub type RawServoAnimationValueBorrowedList =
+        root::nsTArray<*const root::RawServoAnimationValue>;
+    pub type RawServoAnimationValueBorrowedListBorrowed =
+        *const root::RawServoAnimationValueBorrowedList;
     #[repr(u32)]
     #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
     pub enum nsCSSTokenSerializationType {
         eCSSTokenSerialization_Nothing = 0,
         eCSSTokenSerialization_Whitespace = 1,
         eCSSTokenSerialization_AtKeyword_or_Hash = 2,
         eCSSTokenSerialization_Number = 3,
         eCSSTokenSerialization_Dimension = 4,
--- a/servo/components/style/gecko_bindings/structs_release.rs
+++ b/servo/components/style/gecko_bindings/structs_release.rs
@@ -5186,16 +5186,22 @@ pub mod root {
         #[test]
         fn bindgen_test_layout_StyleComplexColor() {
             assert_eq!(::std::mem::size_of::<StyleComplexColor>() , 8usize);
             assert_eq!(::std::mem::align_of::<StyleComplexColor>() , 4usize);
         }
         impl Clone for StyleComplexColor {
             fn clone(&self) -> Self { *self }
         }
+        #[repr(u32)]
+        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+        pub enum EffectCompositor_CascadeLevel {
+            Animations = 0,
+            Transitions = 1,
+        }
         #[repr(C)]
         #[derive(Debug)]
         pub struct PropertyStyleAnimationValuePair {
             pub mProperty: root::nsCSSPropertyID,
             pub mValue: root::mozilla::StyleAnimationValue,
             pub mServoValue: root::RefPtr<root::RawServoAnimationValue>,
         }
         #[test]
@@ -17721,16 +17727,20 @@ pub mod root {
     pub type RawGeckoElementBorrowedOrNull = *const root::RawGeckoElement;
     pub type RawGeckoDocumentBorrowed = *const root::RawGeckoDocument;
     pub type RawGeckoDocumentBorrowedOrNull = *const root::RawGeckoDocument;
     pub type RawGeckoPresContextOwned = *mut root::RawGeckoPresContext;
     pub type RawGeckoPresContextBorrowed = *const root::RawGeckoPresContext;
     pub type RawGeckoPresContextBorrowedMut = *mut root::RawGeckoPresContext;
     pub type RawGeckoAnimationValueListBorrowedMut =
         *mut root::RawGeckoAnimationValueList;
+    pub type RawServoAnimationValueBorrowedList =
+        root::nsTArray<*const root::RawServoAnimationValue>;
+    pub type RawServoAnimationValueBorrowedListBorrowed =
+        *const root::RawServoAnimationValueBorrowedList;
     #[repr(u32)]
     #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
     pub enum nsCSSTokenSerializationType {
         eCSSTokenSerialization_Nothing = 0,
         eCSSTokenSerialization_Whitespace = 1,
         eCSSTokenSerialization_AtKeyword_or_Hash = 2,
         eCSSTokenSerialization_Number = 3,
         eCSSTokenSerialization_Dimension = 4,
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -584,32 +584,36 @@ impl<E: TElement> PrivateMatchMethods fo
 pub trait MatchMethods : TElement {
     /// Runs selector matching of this element, and returns the result.
     fn match_element(&self, context: &StyleContext<Self>, parent_bf: Option<&BloomFilter>)
                      -> MatchResults
     {
         let mut applicable_declarations: Vec<ApplicableDeclarationBlock> = Vec::with_capacity(16);
         let stylist = &context.shared.stylist;
         let style_attribute = self.style_attribute();
+        let animation_rules = self.get_animation_rules(None);
 
         // Compute the primary rule node.
         let mut primary_relations =
             stylist.push_applicable_declarations(self,
                                                  parent_bf,
                                                  style_attribute,
+                                                 animation_rules,
                                                  None,
                                                  &mut applicable_declarations,
                                                  MatchingReason::ForStyling);
         let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
 
         // Compute the pseudo rule nodes.
         let mut per_pseudo: PseudoRuleNodes = HashMap::with_hasher(Default::default());
         SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
             debug_assert!(applicable_declarations.is_empty());
+            let pseudo_animation_rules = self.get_animation_rules(Some(&pseudo));
             stylist.push_applicable_declarations(self, parent_bf, None,
+                                                 pseudo_animation_rules,
                                                  Some(&pseudo.clone()),
                                                  &mut applicable_declarations,
                                                  MatchingReason::ForStyling);
 
             if !applicable_declarations.is_empty() {
                 let rule_node = compute_rule_node(context, &mut applicable_declarations);
                 per_pseudo.insert(pseudo, rule_node);
             }
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -3,29 +3,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Selector matching.
 
 #![deny(missing_docs)]
 
 use {Atom, LocalName};
 use data::ComputedStyle;
-use dom::{PresentationalHintsSynthetizer, TElement};
+use dom::{AnimationRules, PresentationalHintsSynthetizer, TElement};
 use error_reporting::StdoutErrorReporter;
 use keyframes::KeyframesAnimation;
 use media_queries::Device;
 use parking_lot::RwLock;
 use properties::{self, CascadeFlags, ComputedValues, INHERIT_ALL, Importance};
 use properties::{PropertyDeclaration, PropertyDeclarationBlock};
 use quickersort::sort_by;
 use restyle_hints::{RestyleHint, DependencySet};
 use rule_tree::{RuleTree, StrongRuleNode, StyleSource};
 use selector_parser::{ElementExt, SelectorImpl, PseudoElement, Snapshot};
 use selectors::Element;
 use selectors::bloom::BloomFilter;
+use selectors::matching::{AFFECTED_BY_ANIMATIONS, AFFECTED_BY_TRANSITIONS};
 use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
 use selectors::matching::{MatchingReason, StyleRelations, matches_complex_selector};
 use selectors::parser::{Selector, SimpleSelector, LocalName as LocalNameSelector, ComplexSelector};
 use sink::Push;
 use smallvec::VecLike;
 use std::borrow::Borrow;
 use std::collections::HashMap;
 use std::fmt;
@@ -381,16 +382,17 @@ impl Stylist {
             return None;
         }
 
         let mut declarations = vec![];
 
         self.push_applicable_declarations(element,
                                           None,
                                           None,
+                                          AnimationRules(None, None),
                                           Some(pseudo),
                                           &mut declarations,
                                           MatchingReason::ForStyling);
 
         let rule_node =
             self.rule_tree.insert_ordered_rules(
                 declarations.into_iter().map(|a| (a.source, a.importance)));
 
@@ -485,16 +487,17 @@ impl Stylist {
     ///
     /// The returned `StyleRelations` indicate hints about which kind of rules
     /// have matched.
     pub fn push_applicable_declarations<E, V>(
                                         &self,
                                         element: &E,
                                         parent_bf: Option<&BloomFilter>,
                                         style_attribute: Option<&Arc<RwLock<PropertyDeclarationBlock>>>,
+                                        animation_rules: AnimationRules,
                                         pseudo_element: Option<&PseudoElement>,
                                         applicable_declarations: &mut V,
                                         reason: MatchingReason) -> StyleRelations
         where E: ElementExt +
                  fmt::Debug +
                  PresentationalHintsSynthetizer,
               V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>
     {
@@ -555,61 +558,82 @@ impl Stylist {
                     Push::push(
                         applicable_declarations,
                         ApplicableDeclarationBlock::from_declarations(sa.clone(), Importance::Normal));
                 }
             }
 
             debug!("style attr: {:?}", relations);
 
-            // Step 5: Author-supplied `!important` rules.
+            // Step 5: Animations.
+            // The animations sheet (CSS animations, script-generated animations,
+            // and CSS transitions that are no longer tied to CSS markup)
+            if let Some(anim) = animation_rules.0 {
+                relations |= AFFECTED_BY_ANIMATIONS;
+                Push::push(
+                    applicable_declarations,
+                    ApplicableDeclarationBlock::from_declarations(anim.clone(), Importance::Normal));
+            }
+            debug!("animation: {:?}", relations);
+
+            // Step 6: Author-supplied `!important` rules.
             map.author.get_all_matching_rules(element,
                                               parent_bf,
                                               applicable_declarations,
                                               &mut relations,
                                               reason,
                                               Importance::Important);
 
             debug!("author important: {:?}", relations);
 
-            // Step 6: `!important` style attributes.
+            // Step 7: `!important` style attributes.
             if let Some(sa) = style_attribute {
                 if sa.read().any_important() {
                     relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
                     Push::push(
                         applicable_declarations,
                         ApplicableDeclarationBlock::from_declarations(sa.clone(), Importance::Important));
                 }
             }
 
             debug!("style attr important: {:?}", relations);
 
-            // Step 7: User `!important` rules.
+            // Step 8: User `!important` rules.
             map.user.get_all_matching_rules(element,
                                             parent_bf,
                                             applicable_declarations,
                                             &mut relations,
                                             reason,
                                             Importance::Important);
 
             debug!("user important: {:?}", relations);
         } else {
             debug!("skipping non-agent rules");
         }
 
-        // Step 8: UA `!important` rules.
+        // Step 9: UA `!important` rules.
         map.user_agent.get_all_matching_rules(element,
                                               parent_bf,
                                               applicable_declarations,
                                               &mut relations,
                                               reason,
                                               Importance::Important);
 
         debug!("UA important: {:?}", relations);
 
+        // Step 10: Transitions.
+        // The transitions sheet (CSS transitions that are tied to CSS markup)
+        if let Some(anim) = animation_rules.1 {
+            relations |= AFFECTED_BY_TRANSITIONS;
+            Push::push(
+                applicable_declarations,
+                ApplicableDeclarationBlock::from_declarations(anim.clone(), Importance::Normal));
+        }
+        debug!("transition: {:?}", relations);
+
         debug!("push_applicable_declarations: shareable: {:?}", relations);
 
         relations
     }
 
     /// Return whether the device is dirty, that is, whether the screen size or
     /// media type have changed (for now).
     #[inline]
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -34,16 +34,18 @@ use style::gecko_bindings::bindings::{Ra
 use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed};
 use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong};
 use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong};
 use style::gecko_bindings::bindings::{nsACString, nsAString};
 use style::gecko_bindings::bindings::RawGeckoAnimationValueListBorrowedMut;
 use style::gecko_bindings::bindings::RawGeckoElementBorrowed;
 use style::gecko_bindings::bindings::RawGeckoPresContextBorrowed;
 use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed;
+use style::gecko_bindings::bindings::RawServoAnimationValueBorrowedListBorrowed;
+use style::gecko_bindings::bindings::RawServoAnimationValueStrong;
 use style::gecko_bindings::bindings::RawServoImportRuleBorrowed;
 use style::gecko_bindings::bindings::ServoComputedValuesBorrowedOrNull;
 use style::gecko_bindings::bindings::nsTArrayBorrowed_uintptr_t;
 use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID};
 use style::gecko_bindings::structs::{ThreadSafePrincipalHolder, ThreadSafeURIHolder};
 use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint};
 use style::gecko_bindings::structs::Loader;
@@ -53,17 +55,17 @@ use style::gecko_bindings::structs::nsre
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasBoxFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI};
 use style::parallel;
 use style::parser::{ParserContext, ParserContextExtraData};
 use style::properties::{CascadeFlags, ComputedValues, Importance, PropertyDeclaration};
 use style::properties::{PropertyDeclarationParseResult, PropertyDeclarationBlock, PropertyId};
 use style::properties::{apply_declarations, parse_one_declaration};
-use style::properties::animated_properties::AnimationValue;
+use style::properties::animated_properties::{AnimationValue, Interpolate};
 use style::restyle_hints::RestyleHint;
 use style::selector_parser::PseudoElementCascadeType;
 use style::sequential;
 use style::string_cache::Atom;
 use style::stylesheets::{CssRule, CssRules, Origin, Stylesheet, StyleRule, ImportRule};
 use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
 use style::supports::parse_condition_or_declaration;
 use style::thread_state;
@@ -160,16 +162,49 @@ pub extern "C" fn Servo_TraverseSubtree(
                                         raw_data: RawServoStyleSetBorrowed,
                                         behavior: structs::TraversalRootBehavior) -> () {
     let element = GeckoElement(root);
     debug!("Servo_TraverseSubtree: {:?}", element);
     traverse_subtree(element, raw_data,
                      behavior == structs::TraversalRootBehavior::UnstyledChildrenOnly);
 }
 
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValues_Interpolate(from: RawServoAnimationValueBorrowed,
+                                                    to: RawServoAnimationValueBorrowed,
+                                                    progress: f64)
+     -> RawServoAnimationValueStrong
+{
+    let from_value = AnimationValue::as_arc(&from);
+    let to_value = AnimationValue::as_arc(&to);
+    if let Ok(value) = from_value.interpolate(to_value, progress) {
+        Arc::new(value).into_strong()
+    } else {
+        RawServoAnimationValueStrong::null()
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValues_Uncompute(value: RawServoAnimationValueBorrowedListBorrowed)
+     -> RawServoDeclarationBlockStrong
+{
+    let uncomputed_values = value.into_iter()
+                                 .map(|v| {
+                                     let raw_anim = unsafe { v.as_ref().unwrap() };
+                                     let anim = AnimationValue::as_arc(&raw_anim);
+                                     (anim.uncompute(), Importance::Normal)
+                                 })
+                                 .collect();
+
+    Arc::new(RwLock::new(PropertyDeclarationBlock {
+        declarations: uncomputed_values,
+        important_count: 0,
+    })).into_strong()
+}
+
 /// Takes a ServoAnimationValues and populates it with the animation values corresponding
 /// to a given property declaration block
 #[no_mangle]
 pub extern "C" fn Servo_AnimationValues_Populate(anim: RawGeckoAnimationValueListBorrowedMut,
                                                  declarations: RawServoDeclarationBlockBorrowed,
                                                  style: ServoComputedValuesBorrowed,
                                                  parent_style: ServoComputedValuesBorrowedOrNull,
                                                  pres_context: RawGeckoPresContextBorrowed)