servo: Merge #16944 - Implement access to CSSKeyframesRule and CSSKeyframeRule for stylo (from upsuper:bug1345697); r=heycam
authorXidorn Quan <me@upsuper.org>
Thu, 18 May 2017 23:49:30 -0500
changeset 581034 55010a7ef404025159e6183af00fc5bb06506842
parent 581033 8d2ae9e042aa245afb3616ec11640738278256f7
child 581035 96c55b0b6a8cd98275f1181842528e3c423842d6
push id59752
push userbmo:standard8@mozilla.com
push dateFri, 19 May 2017 07:24:10 +0000
reviewersheycam
bugs1345697
milestone55.0a1
servo: Merge #16944 - Implement access to CSSKeyframesRule and CSSKeyframeRule for stylo (from upsuper:bug1345697); r=heycam This is the Servo side change for [bug 1345697](https://bugzilla.mozilla.org/show_bug.cgi?id=1345697). Source-Repo: https://github.com/servo/servo Source-Revision: eda64309f5b8f64476ca3301e610295cee5e05c4
servo/components/style/gecko/arc_types.rs
servo/components/style/gecko/generated/bindings.rs
servo/components/style/keyframes.rs
servo/components/style/stylesheets.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/stylesheets.rs
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -3,30 +3,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! This file lists all arc FFI types and defines corresponding addref
 //! and release functions. This list corresponds to ServoArcTypeList.h
 //! file in Gecko.
 
 #![allow(non_snake_case, missing_docs)]
 
-use gecko_bindings::bindings::{RawServoMediaList, RawServoMediaRule, RawServoNamespaceRule, RawServoPageRule};
+use gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframesRule};
+use gecko_bindings::bindings::{RawServoMediaList, RawServoMediaRule};
+use gecko_bindings::bindings::{RawServoNamespaceRule, RawServoPageRule};
 use gecko_bindings::bindings::{RawServoRuleNode, RawServoRuleNodeStrong, RawServoDocumentRule};
 use gecko_bindings::bindings::{RawServoStyleSheet, RawServoImportRule, RawServoSupportsRule};
 use gecko_bindings::bindings::{ServoComputedValues, ServoCssRules};
 use gecko_bindings::structs::{RawServoDeclarationBlock, RawServoStyleRule};
 use gecko_bindings::structs::RawServoAnimationValue;
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI};
+use keyframes::Keyframe;
 use media_queries::MediaList;
 use properties::{ComputedValues, PropertyDeclarationBlock};
 use properties::animated_properties::AnimationValue;
 use rule_tree::StrongRuleNode;
 use shared_lock::Locked;
 use std::{mem, ptr};
-use stylesheets::{CssRules, Stylesheet, StyleRule, ImportRule, MediaRule};
+use stylesheets::{CssRules, Stylesheet, StyleRule, ImportRule, KeyframesRule, MediaRule};
 use stylesheets::{NamespaceRule, PageRule, SupportsRule, DocumentRule};
 
 macro_rules! impl_arc_ffi {
     ($servo_type:ty => $gecko_type:ty [$addref:ident, $release:ident]) => {
         unsafe impl HasFFI for $servo_type {
             type FFIType = $gecko_type;
         }
         unsafe impl HasArcFFI for $servo_type {}
@@ -59,16 +62,22 @@ impl_arc_ffi!(Locked<StyleRule> => RawSe
               [Servo_StyleRule_AddRef, Servo_StyleRule_Release]);
 
 impl_arc_ffi!(Locked<ImportRule> => RawServoImportRule
               [Servo_ImportRule_AddRef, Servo_ImportRule_Release]);
 
 impl_arc_ffi!(AnimationValue => RawServoAnimationValue
               [Servo_AnimationValue_AddRef, Servo_AnimationValue_Release]);
 
+impl_arc_ffi!(Locked<Keyframe> => RawServoKeyframe
+              [Servo_Keyframe_AddRef, Servo_Keyframe_Release]);
+
+impl_arc_ffi!(Locked<KeyframesRule> => RawServoKeyframesRule
+              [Servo_KeyframesRule_AddRef, Servo_KeyframesRule_Release]);
+
 impl_arc_ffi!(Locked<MediaList> => RawServoMediaList
               [Servo_MediaList_AddRef, Servo_MediaList_Release]);
 
 impl_arc_ffi!(Locked<MediaRule> => RawServoMediaRule
               [Servo_MediaRule_AddRef, Servo_MediaRule_Release]);
 
 impl_arc_ffi!(Locked<NamespaceRule> => RawServoNamespaceRule
               [Servo_NamespaceRule_AddRef, Servo_NamespaceRule_Release]);
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -224,16 +224,26 @@ pub type RawServoStyleRuleBorrowedOrNull
 pub type RawServoImportRuleStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoImportRule>;
 pub type RawServoImportRuleBorrowed<'a> = &'a RawServoImportRule;
 pub type RawServoImportRuleBorrowedOrNull<'a> = Option<&'a RawServoImportRule>;
 enum RawServoImportRuleVoid { }
 pub struct RawServoImportRule(RawServoImportRuleVoid);
 pub type RawServoAnimationValueStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoAnimationValue>;
 pub type RawServoAnimationValueBorrowed<'a> = &'a RawServoAnimationValue;
 pub type RawServoAnimationValueBorrowedOrNull<'a> = Option<&'a RawServoAnimationValue>;
+pub type RawServoKeyframeStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoKeyframe>;
+pub type RawServoKeyframeBorrowed<'a> = &'a RawServoKeyframe;
+pub type RawServoKeyframeBorrowedOrNull<'a> = Option<&'a RawServoKeyframe>;
+enum RawServoKeyframeVoid { }
+pub struct RawServoKeyframe(RawServoKeyframeVoid);
+pub type RawServoKeyframesRuleStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoKeyframesRule>;
+pub type RawServoKeyframesRuleBorrowed<'a> = &'a RawServoKeyframesRule;
+pub type RawServoKeyframesRuleBorrowedOrNull<'a> = Option<&'a RawServoKeyframesRule>;
+enum RawServoKeyframesRuleVoid { }
+pub struct RawServoKeyframesRule(RawServoKeyframesRuleVoid);
 pub type RawServoMediaListStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoMediaList>;
 pub type RawServoMediaListBorrowed<'a> = &'a RawServoMediaList;
 pub type RawServoMediaListBorrowedOrNull<'a> = Option<&'a RawServoMediaList>;
 enum RawServoMediaListVoid { }
 pub struct RawServoMediaList(RawServoMediaListVoid);
 pub type RawServoMediaRuleStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoMediaRule>;
 pub type RawServoMediaRuleBorrowed<'a> = &'a RawServoMediaRule;
 pub type RawServoMediaRuleBorrowedOrNull<'a> = Option<&'a RawServoMediaRule>;
@@ -391,16 +401,28 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_AnimationValue_AddRef(ptr: RawServoAnimationValueBorrowed);
 }
 extern "C" {
     pub fn Servo_AnimationValue_Release(ptr: RawServoAnimationValueBorrowed);
 }
 extern "C" {
+    pub fn Servo_Keyframe_AddRef(ptr: RawServoKeyframeBorrowed);
+}
+extern "C" {
+    pub fn Servo_Keyframe_Release(ptr: RawServoKeyframeBorrowed);
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_AddRef(ptr: RawServoKeyframesRuleBorrowed);
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_Release(ptr: RawServoKeyframesRuleBorrowed);
+}
+extern "C" {
     pub fn Servo_MediaList_AddRef(ptr: RawServoMediaListBorrowed);
 }
 extern "C" {
     pub fn Servo_MediaList_Release(ptr: RawServoMediaListBorrowed);
 }
 extern "C" {
     pub fn Servo_MediaRule_AddRef(ptr: RawServoMediaRuleBorrowed);
 }
@@ -1800,16 +1822,38 @@ extern "C" {
     pub fn Servo_StyleRule_Debug(rule: RawServoStyleRuleBorrowed,
                                  result: *mut nsACString);
 }
 extern "C" {
     pub fn Servo_StyleRule_GetCssText(rule: RawServoStyleRuleBorrowed,
                                       result: *mut nsAString);
 }
 extern "C" {
+    pub fn Servo_Keyframe_Debug(rule: RawServoKeyframeBorrowed,
+                                result: *mut nsACString);
+}
+extern "C" {
+    pub fn Servo_Keyframe_GetCssText(rule: RawServoKeyframeBorrowed,
+                                     result: *mut nsAString);
+}
+extern "C" {
+    pub fn Servo_CssRules_GetKeyframesRuleAt(rules: ServoCssRulesBorrowed,
+                                             index: u32, line: *mut u32,
+                                             column: *mut u32)
+     -> RawServoKeyframesRuleStrong;
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_Debug(rule: RawServoKeyframesRuleBorrowed,
+                                     result: *mut nsACString);
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_GetCssText(rule: RawServoKeyframesRuleBorrowed,
+                                          result: *mut nsAString);
+}
+extern "C" {
     pub fn Servo_CssRules_GetMediaRuleAt(rules: ServoCssRulesBorrowed,
                                          index: u32, line: *mut u32,
                                          column: *mut u32)
      -> RawServoMediaRuleStrong;
 }
 extern "C" {
     pub fn Servo_MediaRule_Debug(rule: RawServoMediaRuleBorrowed,
                                  result: *mut nsACString);
@@ -1905,16 +1949,64 @@ extern "C" {
                                     declarations:
                                         RawServoDeclarationBlockBorrowed);
 }
 extern "C" {
     pub fn Servo_StyleRule_GetSelectorText(rule: RawServoStyleRuleBorrowed,
                                            result: *mut nsAString);
 }
 extern "C" {
+    pub fn Servo_Keyframe_GetKeyText(keyframe: RawServoKeyframeBorrowed,
+                                     result: *mut nsAString);
+}
+extern "C" {
+    pub fn Servo_Keyframe_SetKeyText(keyframe: RawServoKeyframeBorrowed,
+                                     text: *const nsACString) -> bool;
+}
+extern "C" {
+    pub fn Servo_Keyframe_GetStyle(keyframe: RawServoKeyframeBorrowed)
+     -> RawServoDeclarationBlockStrong;
+}
+extern "C" {
+    pub fn Servo_Keyframe_SetStyle(keyframe: RawServoKeyframeBorrowed,
+                                   declarations:
+                                       RawServoDeclarationBlockBorrowed);
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_GetName(rule: RawServoKeyframesRuleBorrowed)
+     -> *mut nsIAtom;
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_SetName(rule: RawServoKeyframesRuleBorrowed,
+                                       name: *mut nsIAtom);
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_GetCount(rule: RawServoKeyframesRuleBorrowed)
+     -> u32;
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_GetKeyframe(rule:
+                                               RawServoKeyframesRuleBorrowed,
+                                           index: u32)
+     -> RawServoKeyframeStrong;
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_FindRule(rule: RawServoKeyframesRuleBorrowed,
+                                        key: *const nsACString) -> u32;
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_AppendRule(rule: RawServoKeyframesRuleBorrowed,
+                                          sheet: RawServoStyleSheetBorrowed,
+                                          css: *const nsACString) -> bool;
+}
+extern "C" {
+    pub fn Servo_KeyframesRule_DeleteRule(rule: RawServoKeyframesRuleBorrowed,
+                                          index: u32);
+}
+extern "C" {
     pub fn Servo_MediaRule_GetMedia(rule: RawServoMediaRuleBorrowed)
      -> RawServoMediaListStrong;
 }
 extern "C" {
     pub fn Servo_NamespaceRule_GetPrefix(rule: RawServoNamespaceRuleBorrowed)
      -> *mut nsIAtom;
 }
 extern "C" {
--- a/servo/components/style/keyframes.rs
+++ b/servo/components/style/keyframes.rs
@@ -66,18 +66,31 @@ impl KeyframePercentage {
         };
 
         Ok(percentage)
     }
 }
 
 /// A keyframes selector is a list of percentages or from/to symbols, which are
 /// converted at parse time to percentages.
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Eq)]
 pub struct KeyframeSelector(Vec<KeyframePercentage>);
+
+impl ToCss for KeyframeSelector {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        let mut iter = self.0.iter();
+        iter.next().unwrap().to_css(dest)?;
+        for percentage in iter {
+            write!(dest, ", ")?;
+            percentage.to_css(dest)?;
+        }
+        Ok(())
+    }
+}
+
 impl KeyframeSelector {
     /// Return the list of percentages this selector contains.
     #[inline]
     pub fn percentages(&self) -> &[KeyframePercentage] {
         &self.0
     }
 
     /// A dummy public function so we can write a unit test for this.
@@ -103,30 +116,24 @@ pub struct Keyframe {
     /// Note that `!important` rules in keyframes don't apply, but we keep this
     /// `Arc` just for convenience.
     pub block: Arc<Locked<PropertyDeclarationBlock>>,
 }
 
 impl ToCssWithGuard for Keyframe {
     fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
     where W: fmt::Write {
-        let mut iter = self.selector.percentages().iter();
-        try!(iter.next().unwrap().to_css(dest));
-        for percentage in iter {
-            try!(write!(dest, ", "));
-            try!(percentage.to_css(dest));
-        }
+        self.selector.to_css(dest)?;
         try!(dest.write_str(" { "));
         try!(self.block.read_with(guard).to_css(dest));
         try!(dest.write_str(" }"));
         Ok(())
     }
 }
 
-
 impl Keyframe {
     /// Parse a CSS keyframe.
     pub fn parse(css: &str, parent_stylesheet: &Stylesheet)
                  -> Result<Arc<Locked<Self>>, ()> {
         let error_reporter = NullReporter;
         let context = ParserContext::new(parent_stylesheet.origin,
                                          &parent_stylesheet.url_data,
                                          &error_reporter,
--- a/servo/components/style/stylesheets.rs
+++ b/servo/components/style/stylesheets.rs
@@ -20,17 +20,17 @@ use error_reporting::{ParseErrorReporter
 use font_face::FontFaceRuleData;
 use font_face::parse_font_face_block;
 #[cfg(feature = "gecko")]
 pub use gecko::rules::{CounterStyleRule, FontFaceRule};
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::URLExtraData;
 #[cfg(feature = "gecko")]
 use gecko_bindings::sugar::refptr::RefPtr;
-use keyframes::{Keyframe, parse_keyframe_list};
+use keyframes::{Keyframe, KeyframeSelector, parse_keyframe_list};
 use media_queries::{Device, MediaList, parse_media_query_list};
 use parking_lot::RwLock;
 use parser::{PARSING_MODE_DEFAULT, Parse, ParserContext, log_css_error};
 use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
 use selector_parser::{SelectorImpl, SelectorParser};
 use selectors::parser::SelectorList;
 #[cfg(feature = "servo")]
 use servo_config::prefs::PREFS;
@@ -560,36 +560,52 @@ impl ToCssWithGuard for ImportRule {
 #[derive(Debug)]
 pub struct KeyframesRule {
     /// The name of the current animation.
     pub name: KeyframesName,
     /// The keyframes specified for this CSS rule.
     pub keyframes: Vec<Arc<Locked<Keyframe>>>,
     /// Vendor prefix type the @keyframes has.
     pub vendor_prefix: Option<VendorPrefix>,
+    /// The line and column of the rule's source code.
+    pub source_location: SourceLocation,
 }
 
 impl ToCssWithGuard for KeyframesRule {
     // Serialization of KeyframesRule is not specced.
     fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
     where W: fmt::Write {
         try!(dest.write_str("@keyframes "));
         try!(self.name.to_css(dest));
-        try!(dest.write_str(" { "));
+        try!(dest.write_str(" {"));
         let iter = self.keyframes.iter();
-        let mut first = true;
         for lock in iter {
-            if !first {
-                try!(dest.write_str(" "));
-            }
-            first = false;
+            try!(dest.write_str("\n"));
             let keyframe = lock.read_with(&guard);
             try!(keyframe.to_css(guard, dest));
         }
-        dest.write_str(" }")
+        dest.write_str("\n}")
+    }
+}
+
+impl KeyframesRule {
+    /// Returns the index of the last keyframe that matches the given selector.
+    /// If the selector is not valid, or no keyframe is found, returns None.
+    ///
+    /// Related spec:
+    /// https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule
+    pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
+        if let Ok(selector) = Parser::new(selector).parse_entirely(KeyframeSelector::parse) {
+            for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
+                if keyframe.read_with(guard).selector == selector {
+                    return Some(i);
+                }
+            }
+        }
+        None
     }
 }
 
 #[allow(missing_docs)]
 #[derive(Debug)]
 pub struct MediaRule {
     pub media_queries: Arc<Locked<MediaList>>,
     pub rules: Arc<Locked<CssRules>>,
@@ -1027,17 +1043,17 @@ enum AtRulePrelude {
     CounterStyle(CustomIdent),
     /// A @media rule prelude, with its media queries.
     Media(Arc<Locked<MediaList>>, SourceLocation),
     /// An @supports rule, with its conditional
     Supports(SupportsCondition, SourceLocation),
     /// A @viewport rule prelude.
     Viewport,
     /// A @keyframes rule, with its animation name and vendor prefix if exists.
-    Keyframes(KeyframesName, Option<VendorPrefix>),
+    Keyframes(KeyframesName, Option<VendorPrefix>, SourceLocation),
     /// A @page rule prelude.
     Page(SourceLocation),
     /// A @document rule, with its conditional.
     Document(DocumentCondition, SourceLocation),
 }
 
 
 impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
@@ -1252,17 +1268,17 @@ impl<'a, 'b> AtRuleParser for NestedRule
                 };
                 if cfg!(feature = "servo") &&
                    prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {
                     // Servo should not support @-moz-keyframes.
                     return Err(())
                 }
                 let name = KeyframesName::parse(self.context, input)?;
 
-                Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix)))
+                Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix, location)))
             },
             "page" => {
                 if cfg!(feature = "gecko") {
                     Ok(AtRuleType::WithBlock(AtRulePrelude::Page(location)))
                 } else {
                     Err(())
                 }
             },
@@ -1306,22 +1322,23 @@ impl<'a, 'b> AtRuleParser for NestedRule
                     source_location: location,
                 }))))
             }
             AtRulePrelude::Viewport => {
                 let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Viewport));
                 Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
                    try!(ViewportRule::parse(&context, input))))))
             }
-            AtRulePrelude::Keyframes(name, prefix) => {
+            AtRulePrelude::Keyframes(name, prefix, location) => {
                 let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes));
                 Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule {
                     name: name,
                     keyframes: parse_keyframe_list(&context, input, self.shared_lock),
                     vendor_prefix: prefix,
+                    source_location: location,
                 }))))
             }
             AtRulePrelude::Page(location) => {
                 let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Page));
                 let declarations = parse_property_declaration_list(&context, input);
                 Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
                     block: Arc::new(self.shared_lock.wrap(declarations)),
                     source_location: location,
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -24,16 +24,18 @@ use style::gecko::global_style_data::{GL
 use style::gecko::restyle_damage::GeckoRestyleDamage;
 use style::gecko::selector_parser::PseudoElement;
 use style::gecko::traversal::RecalcStyleOnly;
 use style::gecko::wrapper::GeckoElement;
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::{RawGeckoKeyframeListBorrowed, RawGeckoKeyframeListBorrowedMut};
 use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
 use style::gecko_bindings::bindings::{RawServoDocumentRule, RawServoDocumentRuleBorrowed};
+use style::gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframeBorrowed, RawServoKeyframeStrong};
+use style::gecko_bindings::bindings::{RawServoKeyframesRule, RawServoKeyframesRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoMediaList, RawServoMediaListBorrowed, RawServoMediaListStrong};
 use style::gecko_bindings::bindings::{RawServoMediaRule, RawServoMediaRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoPageRule, RawServoPageRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned};
 use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed};
 use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong};
 use style::gecko_bindings::bindings::{RawServoSupportsRule, RawServoSupportsRuleBorrowed};
@@ -69,17 +71,17 @@ use style::gecko_bindings::structs::Serv
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::structs::nsCSSValueSharedList;
 use style::gecko_bindings::structs::nsCompatibility;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties::{self, style_structs};
-use style::keyframes::KeyframesStepValue;
+use style::keyframes::{Keyframe, KeyframeSelector, KeyframesStepValue};
 use style::media_queries::{MediaList, parse_media_query_list};
 use style::parallel;
 use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
 use style::properties::{CascadeFlags, ComputedValues, Importance, ParsedDeclaration, StyleBuilder};
 use style::properties::{LonghandIdSet, PropertyDeclarationBlock, PropertyId};
 use style::properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP;
 use style::properties::animated_properties::{Animatable, AnimationValue, TransitionProperty};
 use style::properties::parse_one_declaration;
@@ -87,24 +89,25 @@ use style::restyle_hints::{self, Restyle
 use style::rule_tree::StyleSource;
 use style::selector_parser::PseudoElementCascadeType;
 use style::sequential;
 use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::Atom;
 use style::style_adjuster::StyleAdjuster;
 use style::stylearc::Arc;
 use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers};
-use style::stylesheets::{ImportRule, MediaRule, NamespaceRule, Origin};
+use style::stylesheets::{ImportRule, KeyframesRule, MediaRule, NamespaceRule, Origin};
 use style::stylesheets::{PageRule, Stylesheet, StyleRule, SupportsRule, DocumentRule};
 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, FOR_RECONSTRUCT, UNSTYLED_CHILDREN_ONLY};
 use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags};
+use style::values::{CustomIdent, KeyframesName};
 use style_traits::ToCss;
 use super::stylesheet_loader::StylesheetLoader;
 
 /*
  * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
  * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
  * those signatures as well, giving us a second declaration of all the Servo_* functions in this
  * crate. If there's a mismatch, LLVM will assert and abort, which is a rather awful thing to
@@ -769,16 +772,38 @@ pub extern "C" fn Servo_CssRules_DeleteR
     write_locked_arc(rules, |rules: &mut CssRules| {
         match rules.remove_rule(index as usize) {
             Ok(_) => nsresult::NS_OK,
             Err(err) => err.into()
         }
     })
 }
 
+macro_rules! impl_basic_rule_funcs_without_getter {
+    { ($rule_type:ty, $raw_type:ty),
+        debug: $debug:ident,
+        to_css: $to_css:ident,
+    } => {
+        #[no_mangle]
+        pub extern "C" fn $debug(rule: &$raw_type, result: *mut nsACString) {
+            read_locked_arc(rule, |rule: &$rule_type| {
+                write!(unsafe { result.as_mut().unwrap() }, "{:?}", *rule).unwrap();
+            })
+        }
+
+        #[no_mangle]
+        pub extern "C" fn $to_css(rule: &$raw_type, result: *mut nsAString) {
+            let global_style_data = &*GLOBAL_STYLE_DATA;
+            let guard = global_style_data.shared_lock.read();
+            let rule = Locked::<$rule_type>::as_arc(&rule);
+            rule.read_with(&guard).to_css(&guard, unsafe { result.as_mut().unwrap() }).unwrap();
+        }
+    }
+}
+
 macro_rules! impl_basic_rule_funcs {
     { ($name:ident, $rule_type:ty, $raw_type:ty),
         getter: $getter:ident,
         debug: $debug:ident,
         to_css: $to_css:ident,
     } => {
         #[no_mangle]
         pub extern "C" fn $getter(rules: ServoCssRulesBorrowed, index: u32,
@@ -796,29 +821,19 @@ macro_rules! impl_basic_rule_funcs {
                 },
                 _ => {
                     unreachable!(concat!(stringify!($getter), "should only be called ",
                                          "on a ", stringify!($name), " rule"));
                 }
             }
         }
 
-        #[no_mangle]
-        pub extern "C" fn $debug(rule: &$raw_type, result: *mut nsACString) {
-            read_locked_arc(rule, |rule: &$rule_type| {
-                write!(unsafe { result.as_mut().unwrap() }, "{:?}", *rule).unwrap();
-            })
-        }
-
-        #[no_mangle]
-        pub extern "C" fn $to_css(rule: &$raw_type, result: *mut nsAString) {
-            let global_style_data = &*GLOBAL_STYLE_DATA;
-            let guard = global_style_data.shared_lock.read();
-            let rule = Locked::<$rule_type>::as_arc(&rule);
-            rule.read_with(&guard).to_css(&guard, unsafe { result.as_mut().unwrap() }).unwrap();
+        impl_basic_rule_funcs_without_getter! { ($rule_type, $raw_type),
+            debug: $debug,
+            to_css: $to_css,
         }
     }
 }
 
 macro_rules! impl_group_rule_funcs {
     { ($name:ident, $rule_type:ty, $raw_type:ty),
       get_rules: $get_rules:ident,
       $($basic:tt)+
@@ -835,16 +850,27 @@ macro_rules! impl_group_rule_funcs {
 }
 
 impl_basic_rule_funcs! { (Style, StyleRule, RawServoStyleRule),
     getter: Servo_CssRules_GetStyleRuleAt,
     debug: Servo_StyleRule_Debug,
     to_css: Servo_StyleRule_GetCssText,
 }
 
+impl_basic_rule_funcs_without_getter! { (Keyframe, RawServoKeyframe),
+    debug: Servo_Keyframe_Debug,
+    to_css: Servo_Keyframe_GetCssText,
+}
+
+impl_basic_rule_funcs! { (Keyframes, KeyframesRule, RawServoKeyframesRule),
+    getter: Servo_CssRules_GetKeyframesRuleAt,
+    debug: Servo_KeyframesRule_Debug,
+    to_css: Servo_KeyframesRule_GetCssText,
+}
+
 impl_group_rule_funcs! { (Media, MediaRule, RawServoMediaRule),
     get_rules: Servo_MediaRule_GetRules,
     getter: Servo_CssRules_GetMediaRuleAt,
     debug: Servo_MediaRule_Debug,
     to_css: Servo_MediaRule_GetCssText,
 }
 
 impl_basic_rule_funcs! { (Namespace, NamespaceRule, RawServoNamespaceRule),
@@ -914,16 +940,109 @@ pub extern "C" fn Servo_StyleRule_SetSty
 #[no_mangle]
 pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: RawServoStyleRuleBorrowed, result: *mut nsAString) {
     read_locked_arc(rule, |rule: &StyleRule| {
         rule.selectors.to_css(unsafe { result.as_mut().unwrap() }).unwrap();
     })
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_Keyframe_GetKeyText(keyframe: RawServoKeyframeBorrowed, result: *mut nsAString) {
+    read_locked_arc(keyframe, |keyframe: &Keyframe| {
+        keyframe.selector.to_css(unsafe { result.as_mut().unwrap() }).unwrap()
+    })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Keyframe_SetKeyText(keyframe: RawServoKeyframeBorrowed, text: *const nsACString) -> bool {
+    let text = unsafe { text.as_ref().unwrap().as_str_unchecked() };
+    if let Ok(selector) = Parser::new(&text).parse_entirely(KeyframeSelector::parse) {
+        write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
+            keyframe.selector = selector;
+        });
+        true
+    } else {
+        false
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Keyframe_GetStyle(keyframe: RawServoKeyframeBorrowed) -> RawServoDeclarationBlockStrong {
+    read_locked_arc(keyframe, |keyframe: &Keyframe| keyframe.block.clone().into_strong())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Keyframe_SetStyle(keyframe: RawServoKeyframeBorrowed,
+                                          declarations: RawServoDeclarationBlockBorrowed) {
+    let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
+    write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
+        keyframe.block = declarations.clone();
+    })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_GetName(rule: RawServoKeyframesRuleBorrowed) -> *mut nsIAtom {
+    read_locked_arc(rule, |rule: &KeyframesRule| rule.name.as_atom().as_ptr())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_SetName(rule: RawServoKeyframesRuleBorrowed, name: *mut nsIAtom) {
+    write_locked_arc(rule, |rule: &mut KeyframesRule| {
+        rule.name = KeyframesName::Ident(CustomIdent(unsafe { Atom::from_addrefed(name) }));
+    })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_GetCount(rule: RawServoKeyframesRuleBorrowed) -> u32 {
+    read_locked_arc(rule, |rule: &KeyframesRule| rule.keyframes.len() as u32)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_GetKeyframe(rule: RawServoKeyframesRuleBorrowed, index: u32)
+                                                  -> RawServoKeyframeStrong {
+    read_locked_arc(rule, |rule: &KeyframesRule| {
+        rule.keyframes[index as usize].clone().into_strong()
+    })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_FindRule(rule: RawServoKeyframesRuleBorrowed,
+                                               key: *const nsACString) -> u32 {
+    let key = unsafe { key.as_ref().unwrap().as_str_unchecked() };
+    let global_style_data = &*GLOBAL_STYLE_DATA;
+    let guard = global_style_data.shared_lock.read();
+    Locked::<KeyframesRule>::as_arc(&rule).read_with(&guard)
+        .find_rule(&guard, key).map(|index| index as u32)
+        .unwrap_or(u32::max_value())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_AppendRule(rule: RawServoKeyframesRuleBorrowed,
+                                                 sheet: RawServoStyleSheetBorrowed,
+                                                 css: *const nsACString) -> bool {
+    let css = unsafe { css.as_ref().unwrap().as_str_unchecked() };
+    let sheet = Stylesheet::as_arc(&sheet);
+    if let Ok(keyframe) = Keyframe::parse(css, sheet) {
+        write_locked_arc(rule, |rule: &mut KeyframesRule| {
+            rule.keyframes.push(keyframe);
+        });
+        true
+    } else {
+        false
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_DeleteRule(rule: RawServoKeyframesRuleBorrowed, index: u32) {
+    write_locked_arc(rule, |rule: &mut KeyframesRule| {
+        rule.keyframes.remove(index as usize);
+    })
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_MediaRule_GetMedia(rule: RawServoMediaRuleBorrowed) -> RawServoMediaListStrong {
     read_locked_arc(rule, |rule: &MediaRule| {
         rule.media_queries.clone().into_strong()
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_NamespaceRule_GetPrefix(rule: RawServoNamespaceRuleBorrowed) -> *mut nsIAtom {
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -241,16 +241,20 @@ fn test_parse_stylesheet() {
                             (PropertyDeclaration::AnimationPlayState(
                                 animation_play_state::SpecifiedValue(
                                     vec![animation_play_state::SingleSpecifiedValue::running])),
                              Importance::Normal),
                         ]))),
                     })),
                 ],
                 vendor_prefix: None,
+                source_location: SourceLocation {
+                    line: 16,
+                    column: 19,
+                },
             })))
 
         ], &stylesheet.shared_lock),
     };
 
     assert_eq!(format!("{:#?}", stylesheet), format!("{:#?}", expected));
 }