Implement parsing and computation for the new properties in css3-animation. (Bug 435442, patch 1) r=bzbarsky
authorL. David Baron <dbaron@dbaron.org>
Mon, 11 Apr 2011 23:18:42 -0700
changeset 67975 0a0314bdf5c62c5589960776c7662d7996640ec4
parent 67974 6bf7649f64a60b95c7d6005fe08cb615f78a723f
child 67976 b579b02a57af56d709f696b8453beb16aad3a210
push idunknown
push userunknown
push dateunknown
reviewersbzbarsky
bugs435442
milestone2.2a1pre
Implement parsing and computation for the new properties in css3-animation. (Bug 435442, patch 1) r=bzbarsky
dom/interfaces/css/nsIDOMCSS2Properties.idl
layout/base/nsStyleConsts.h
layout/style/Declaration.cpp
layout/style/nsCSSKeywordList.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/style/test/test_garbage_at_end_of_declarations.html
layout/style/test/test_transitions_computed_values.html
--- a/dom/interfaces/css/nsIDOMCSS2Properties.idl
+++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl
@@ -45,17 +45,17 @@
  * The nsIDOMCSS2Properties interface is a datatype for additional
  * reflection of data already provided in nsIDOMCSSStyleDeclaration in
  * the Document Object Model.
  *
  * For more information on this interface please see
  * http://www.w3.org/TR/DOM-Level-2-Style
  */
 
-[scriptable, uuid(6b8fda8f-94f6-4d5c-aa77-17c5270f36c7)]
+[scriptable, uuid(cd0e6cfe-7278-49b2-a1d6-c2b87b6eb7e0)]
 interface nsIDOMCSS2Properties : nsISupports
 {
            attribute DOMString        azimuth;
                                         // raises(DOMException) on setting
 
            attribute DOMString        background;
                                         // raises(DOMException) on setting
 
@@ -758,9 +758,36 @@ interface nsIDOMCSS2Properties : nsISupp
            attribute DOMString        pointerEvents;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTabSize;
                                         // raises(DOMException) on setting
 
            attribute DOMString        resize;
                                         // raises(DOMException) on setting
+
+           attribute DOMString        MozAnimationName;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozAnimationDuration;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozAnimationDelay;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozAnimationTimingFunction;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozAnimationDirection;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozAnimationFillMode;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozAnimationIterationCount;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozAnimationPlayState;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozAnimation;
+                                        // raises(DOMException) on setting
 };
--- a/layout/base/nsStyleConsts.h
+++ b/layout/base/nsStyleConsts.h
@@ -237,16 +237,33 @@ static inline mozilla::css::Side operato
 // See nsStyleColor
 #define NS_COLOR_CURRENTCOLOR                   -1
 #define NS_COLOR_MOZ_DEFAULT_COLOR              -2
 #define NS_COLOR_MOZ_DEFAULT_BACKGROUND_COLOR   -3
 #define NS_COLOR_MOZ_HYPERLINKTEXT              -4
 #define NS_COLOR_MOZ_VISITEDHYPERLINKTEXT       -5
 #define NS_COLOR_MOZ_ACTIVEHYPERLINKTEXT        -6
 
+// See nsStyleDisplay
+#define NS_STYLE_ANIMATION_DIRECTION_NORMAL       0
+#define NS_STYLE_ANIMATION_DIRECTION_ALTERNATE    1
+
+// See nsStyleDisplay
+#define NS_STYLE_ANIMATION_FILL_MODE_NONE         0
+#define NS_STYLE_ANIMATION_FILL_MODE_FORWARDS     1
+#define NS_STYLE_ANIMATION_FILL_MODE_BACKWARDS    2
+#define NS_STYLE_ANIMATION_FILL_MODE_BOTH         3
+
+// See nsStyleDisplay
+#define NS_STYLE_ANIMATION_ITERATION_COUNT_INFINITE 0
+
+// See nsStyleDisplay
+#define NS_STYLE_ANIMATION_PLAY_STATE_RUNNING     0
+#define NS_STYLE_ANIMATION_PLAY_STATE_PAUSED      1
+
 // See nsStyleBackground
 #define NS_STYLE_BG_ATTACHMENT_SCROLL     0
 #define NS_STYLE_BG_ATTACHMENT_FIXED      1
 
 // See nsStyleBackground
 // Code depends on these constants having the same values as BG_ORIGIN_*
 #define NS_STYLE_BG_CLIP_BORDER           0
 #define NS_STYLE_BG_CLIP_PADDING          1
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -669,17 +669,66 @@ Declaration::GetValue(nsCSSProperty aPro
         }
         if (pro || dur || tim || del) {
           // Lists not all the same length, can't use shorthand.
           aValue.Truncate();
         }
       }
       break;
     }
+    case eCSSProperty_animation: {
+      const nsCSSProperty* subprops =
+        nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
+      static const size_t numProps = 8;
+      NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
+                        "unexpected number of subproperties");
+      const nsCSSValue* values[numProps];
+      const nsCSSValueList* lists[numProps];
 
+      for (PRUint32 i = 0; i < numProps; ++i) {
+        values[i] = data->ValueFor(subprops[i]);
+        NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
+                          values[i]->GetUnit() == eCSSUnit_ListDep,
+                          nsPrintfCString(32, "bad a-duration unit %d",
+                                          values[i]->GetUnit()).get());
+        lists[i] = values[i]->GetListValue();
+      }
+
+      for (;;) {
+        // We must serialize 'animation-name' last in case it has
+        // a value that conflicts with one of the other keyword properties.
+        NS_ABORT_IF_FALSE(subprops[numProps - 1] ==
+                            eCSSProperty_animation_name,
+                          "animation-name must be last");
+        bool done = false;
+        for (PRUint32 i = 0;;) {
+          lists[i]->mValue.AppendToString(subprops[i], aValue);
+          lists[i] = lists[i]->mNext;
+          if (!lists[i]) {
+            done = true;
+          }
+          if (++i == numProps) {
+            break;
+          }
+          aValue.Append(PRUnichar(' '));
+        }
+        if (done) {
+          break;
+        }
+        aValue.AppendLiteral(", ");
+      }
+      for (PRUint32 i = 0; i < numProps; ++i) {
+        if (lists[i]) {
+          // Lists not all the same length, can't use shorthand.
+          aValue.Truncate();
+          break;
+        }
+      }
+      break;
+    }
     case eCSSProperty_marker: {
       const nsCSSValue &endValue =
         *data->ValueFor(eCSSProperty_marker_end);
       const nsCSSValue &midValue =
         *data->ValueFor(eCSSProperty_marker_mid);
       const nsCSSValue &startValue =
         *data->ValueFor(eCSSProperty_marker_start);
       if (endValue == midValue && midValue == startValue)
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -183,22 +183,24 @@ CSS_KEY(-moz-zoom-out, _moz_zoom_out)
 CSS_KEY(above, above)
 CSS_KEY(absolute, absolute)
 CSS_KEY(active, active)
 CSS_KEY(activeborder, activeborder)
 CSS_KEY(activecaption, activecaption)
 CSS_KEY(alias, alias)
 CSS_KEY(all, all)
 CSS_KEY(all-scroll, all_scroll)
+CSS_KEY(alternate, alternate)
 CSS_KEY(always, always)
 CSS_KEY(appworkspace, appworkspace)
 CSS_KEY(armenian, armenian)
 CSS_KEY(auto, auto)
 CSS_KEY(avoid, avoid)
 CSS_KEY(background, background)
+CSS_KEY(backwards, backwards)
 CSS_KEY(baseline, baseline)
 CSS_KEY(behind, behind)
 CSS_KEY(below, below)
 CSS_KEY(bidi-override, bidi_override)
 CSS_KEY(blink, blink)
 CSS_KEY(block, block)
 CSS_KEY(block-axis, block_axis)
 CSS_KEY(bold, bold)
@@ -274,16 +276,17 @@ CSS_KEY(ew-resize, ew_resize)
 CSS_KEY(far-left, far_left)
 CSS_KEY(far-right, far_right)
 CSS_KEY(farthest-side, farthest_side)
 CSS_KEY(farthest-corner, farthest_corner)
 CSS_KEY(fast, fast)
 CSS_KEY(faster, faster)
 CSS_KEY(fill, fill)
 CSS_KEY(fixed, fixed)
+CSS_KEY(forwards, forwards)
 CSS_KEY(georgian, georgian)
 CSS_KEY(grad, grad)
 CSS_KEY(graytext, graytext)
 CSS_KEY(groove, groove)
 CSS_KEY(hebrew, hebrew)
 CSS_KEY(help, help)
 CSS_KEY(hidden, hidden)
 CSS_KEY(hide, hide)
@@ -298,16 +301,17 @@ CSS_KEY(hz, hz)
 CSS_KEY(icon, icon)
 CSS_KEY(ignore, ignore)
 CSS_KEY(in, in)
 CSS_KEY(interlace, interlace)
 CSS_KEY(inactive, inactive)
 CSS_KEY(inactiveborder, inactiveborder)
 CSS_KEY(inactivecaption, inactivecaption)
 CSS_KEY(inactivecaptiontext, inactivecaptiontext)
+CSS_KEY(infinite, infinite)
 CSS_KEY(infobackground, infobackground)
 CSS_KEY(infotext, infotext)
 CSS_KEY(inherit, inherit)
 CSS_KEY(inline, inline)
 CSS_KEY(inline-axis, inline_axis)
 CSS_KEY(inline-block, inline_block)
 CSS_KEY(inline-table, inline_table)
 CSS_KEY(inset, inset)
@@ -370,16 +374,17 @@ CSS_KEY(nwse-resize, nwse_resize)
 CSS_KEY(oblique, oblique)
 CSS_KEY(once, once)
 CSS_KEY(open-quote, open_quote)
 CSS_KEY(outset, outset)
 CSS_KEY(outside, outside)
 CSS_KEY(overline, overline)
 CSS_KEY(padding-box, padding_box)
 CSS_KEY(painted, painted)
+CSS_KEY(paused, paused)
 CSS_KEY(pc, pc)
 CSS_KEY(physical, physical)
 CSS_KEY(pointer, pointer)
 CSS_KEY(portrait, portrait)
 CSS_KEY(pre, pre)
 CSS_KEY(pre-wrap, pre_wrap)
 CSS_KEY(pre-line, pre_line)
 CSS_KEY(progress, progress)
@@ -397,16 +402,17 @@ CSS_KEY(reverse, reverse)
 CSS_KEY(ridge, ridge)
 CSS_KEY(right, right)
 CSS_KEY(right-side, right_side)
 CSS_KEY(rightwards, rightwards)
 CSS_KEY(rotate, rotate)
 CSS_KEY(round, round)
 CSS_KEY(row-resize, row_resize)
 CSS_KEY(rtl, rtl)
+CSS_KEY(running, running)
 CSS_KEY(s, s)
 CSS_KEY(s-resize, s_resize)
 CSS_KEY(scale, scale)
 CSS_KEY(scalex, scalex)
 CSS_KEY(scaley, scaley)
 CSS_KEY(scroll, scroll)
 CSS_KEY(scrollbar, scrollbar)
 CSS_KEY(scrollbar-small, scrollbar_small)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -102,16 +102,18 @@ namespace css = mozilla::css;
 #define VARIANT_INTEGER         0x000040  // I
 #define VARIANT_ANGLE           0x000080  // G
 #define VARIANT_FREQUENCY       0x000100  // F
 #define VARIANT_TIME            0x000200  // T
 #define VARIANT_STRING          0x000400  // S
 #define VARIANT_COUNTER         0x000800  //
 #define VARIANT_ATTR            0x001000  //
 #define VARIANT_IDENTIFIER      0x002000  // D
+#define VARIANT_IDENTIFIER_NO_INHERIT 0x004000 // like above, but excluding
+                                               // 'inherit' and 'initial'
 #define VARIANT_AUTO            0x010000  // A
 #define VARIANT_INHERIT         0x020000  // H eCSSUnit_Initial, eCSSUnit_Inherit
 #define VARIANT_NONE            0x040000  // O
 #define VARIANT_NORMAL          0x080000  // M
 #define VARIANT_SYSFONT         0x100000  // eCSSUnit_System_Font
 #define VARIANT_GRADIENT        0x200000  // eCSSUnit_Gradient
 #define VARIANT_TIMING_FUNCTION 0x400000  // cubic-bezier() and steps()
 #define VARIANT_ALL             0x800000  //
@@ -506,22 +508,33 @@ protected:
   PRBool ParsePause();
   PRBool ParseQuotes();
   PRBool ParseSize();
   PRBool ParseTextDecoration(nsCSSValue& aValue);
 
   PRBool ParseShadowItem(nsCSSValue& aValue, PRBool aIsBoxShadow);
   PRBool ParseShadowList(nsCSSProperty aProperty);
   PRBool ParseTransitionProperty();
-  PRBool ParseTransition();
   PRBool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
   PRBool ParseTransitionTimingFunctionValueComponent(float& aComponent,
                                                      char aStop,
                                                      PRBool aCheckRange);
   PRBool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
+  enum ParseAnimationOrTransitionShorthandResult {
+    eParseAnimationOrTransitionShorthand_Values,
+    eParseAnimationOrTransitionShorthand_Inherit,
+    eParseAnimationOrTransitionShorthand_Error
+  };
+  ParseAnimationOrTransitionShorthandResult
+    ParseAnimationOrTransitionShorthand(const nsCSSProperty* aProperties,
+                                        const nsCSSValue* aInitialValues,
+                                        nsCSSValue* aValues,
+                                        size_t aNumProperties);
+  PRBool ParseTransition();
+  PRBool ParseAnimation();
 
 #ifdef MOZ_SVG
   PRBool ParsePaint(nsCSSProperty aPropID);
   PRBool ParseDasharray();
   PRBool ParseMarker();
 #endif
 
   // Reused utility parsing routines
@@ -4204,16 +4217,17 @@ CSSParserImpl::TranslateDimension(nsCSSV
 #define VARIANT_ALL_NONNUMERIC \
   VARIANT_KEYWORD | \
   VARIANT_COLOR | \
   VARIANT_URL | \
   VARIANT_STRING | \
   VARIANT_COUNTER | \
   VARIANT_ATTR | \
   VARIANT_IDENTIFIER | \
+  VARIANT_IDENTIFIER_NO_INHERIT | \
   VARIANT_AUTO | \
   VARIANT_INHERIT | \
   VARIANT_NONE | \
   VARIANT_NORMAL | \
   VARIANT_SYSFONT | \
   VARIANT_GRADIENT | \
   VARIANT_TIMING_FUNCTION | \
   VARIANT_ALL | \
@@ -4292,16 +4306,20 @@ CSSParserImpl::ParsePositiveNonZeroVaria
 PRBool
 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
                             PRInt32 aVariantMask,
                             const PRInt32 aKeywordTable[])
 {
   NS_ASSERTION(IsParsingCompoundProperty() ||
                ((~aVariantMask) & (VARIANT_LENGTH|VARIANT_COLOR)),
                "cannot distinguish lengths and colors in quirks mode");
+  NS_ABORT_IF_FALSE(!(aVariantMask & VARIANT_IDENTIFIER) ||
+                    !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
+                    "must not set both VARIANT_IDENTIFIER and "
+                    "VARIANT_IDENTIFIER_NO_INHERIT");
 
   if (!GetToken(PR_TRUE)) {
     return PR_FALSE;
   }
   nsCSSToken* tk = &mToken;
   if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
       (eCSSToken_Ident == tk->mType)) {
     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
@@ -4462,18 +4480,22 @@ CSSParserImpl::ParseVariant(nsCSSValue& 
   }
   if (((aVariantMask & VARIANT_STRING) != 0) &&
       (eCSSToken_String == tk->mType)) {
     nsAutoString  buffer;
     buffer.Append(tk->mIdent);
     aValue.SetStringValue(buffer, eCSSUnit_String);
     return PR_TRUE;
   }
-  if (((aVariantMask & VARIANT_IDENTIFIER) != 0) &&
-      (eCSSToken_Ident == tk->mType)) {
+  if (((aVariantMask &
+        (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
+      (eCSSToken_Ident == tk->mType) &&
+      ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
+       !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
+         tk->mIdent.LowerCaseEqualsLiteral("initial")))) {
     aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
     return PR_TRUE;
   }
   if (((aVariantMask & VARIANT_COUNTER) != 0) &&
       (eCSSToken_Function == tk->mType) &&
       (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
        tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
     return ParseCounter(aValue);
@@ -5376,17 +5398,19 @@ CSSParserImpl::ParsePropertyByFunction(n
     return ParseQuotes();
   case eCSSProperty_size:
     return ParseSize();
   case eCSSProperty__moz_transform:
     return ParseMozTransform();
   case eCSSProperty__moz_transform_origin:
     return ParseMozTransformOrigin();
   case eCSSProperty_transition:
-      return ParseTransition();
+    return ParseTransition();
+  case eCSSProperty_animation:
+    return ParseAnimation();
   case eCSSProperty_transition_property:
     return ParseTransitionProperty();
 
 #ifdef MOZ_SVG
   case eCSSProperty_fill:
   case eCSSProperty_stroke:
     return ParsePaint(aPropID);
   case eCSSProperty_stroke_dasharray:
@@ -8019,16 +8043,99 @@ AppendValueToList(nsCSSValue& aContainer
     NS_ABORT_IF_FALSE(aContainer.GetUnit() == eCSSUnit_List, "not a list");
     entry = new nsCSSValueList;
     aTail->mNext = entry;
   }
   entry->mValue = aValue;
   return entry;
 }
 
+CSSParserImpl::ParseAnimationOrTransitionShorthandResult
+CSSParserImpl::ParseAnimationOrTransitionShorthand(
+                 const nsCSSProperty* aProperties,
+                 const nsCSSValue* aInitialValues,
+                 nsCSSValue* aValues,
+                 size_t aNumProperties)
+{
+  nsCSSValue tempValue;
+  // first see if 'inherit' or '-moz-initial' is specified.  If one is,
+  // it can be the only thing specified, so don't attempt to parse any
+  // additional properties
+  if (ParseVariant(tempValue, VARIANT_INHERIT, nsnull)) {
+    for (PRUint32 i = 0; i < aNumProperties; ++i) {
+      AppendValue(aProperties[i], tempValue);
+    }
+    return eParseAnimationOrTransitionShorthand_Inherit;
+  }
+
+  static const size_t maxNumProperties = 8;
+  NS_ABORT_IF_FALSE(aNumProperties <= maxNumProperties,
+                    "can't handle this many properties");
+  nsCSSValueList *cur[maxNumProperties];
+  PRBool parsedProperty[maxNumProperties];
+
+  for (size_t i = 0; i < aNumProperties; ++i) {
+    cur[i] = nsnull;
+  }
+  PRBool atEOP = PR_FALSE; // at end of property?
+  for (;;) { // loop over comma-separated transitions or animations
+    // whether a particular subproperty was specified for this
+    // transition or animation
+    for (size_t i = 0; i < aNumProperties; ++i) {
+      parsedProperty[i] = PR_FALSE;
+    }
+    for (;;) { // loop over values within a transition or animation
+      PRBool foundProperty = PR_FALSE;
+      // check to see if we're at the end of one full transition or
+      // animation definition (either because we hit a comma or because
+      // we hit the end of the property definition)
+      if (ExpectSymbol(',', PR_TRUE))
+        break;
+      if (CheckEndProperty()) {
+        atEOP = PR_TRUE;
+        break;
+      }
+
+      // else, try to parse the next transition or animation sub-property
+      for (PRUint32 i = 0; !foundProperty && i < aNumProperties; ++i) {
+        if (!parsedProperty[i]) {
+          // if we haven't found this property yet, try to parse it
+          if (ParseSingleValueProperty(tempValue, aProperties[i])) {
+            parsedProperty[i] = PR_TRUE;
+            cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
+            foundProperty = PR_TRUE;
+            break; // out of inner loop; continue looking for next sub-property
+          }
+        }
+      }
+      if (!foundProperty) {
+        // We're not at a ',' or at the end of the property, but we couldn't
+        // parse any of the sub-properties, so the declaration is invalid.
+        return eParseAnimationOrTransitionShorthand_Error;
+      }
+    }
+
+    // We hit the end of the property or the end of one transition
+    // or animation definition, add its components to the list.
+    for (PRUint32 i = 0; i < aNumProperties; ++i) {
+      // If all of the subproperties were not explicitly specified, fill
+      // in the missing ones with initial values.
+      if (!parsedProperty[i]) {
+        cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
+      }
+    }
+
+    if (atEOP)
+      break;
+    // else we just hit a ',' so continue parsing the next compound transition
+  }
+
+  return eParseAnimationOrTransitionShorthand_Values;
+}
+
 PRBool
 CSSParserImpl::ParseTransition()
 {
   static const nsCSSProperty kTransitionProperties[] = {
     eCSSProperty_transition_duration,
     eCSSProperty_transition_timing_function,
     // Must check 'transition-delay' after 'transition-duration', since
     // that's our assumption about what the spec means for the shorthand
@@ -8040,93 +8147,30 @@ CSSParserImpl::ParseTransition()
     // any keyword.
     eCSSProperty_transition_property
   };
   static const PRUint32 numProps = NS_ARRAY_LENGTH(kTransitionProperties);
   // this is a shorthand property that accepts -property, -delay,
   // -duration, and -timing-function with some components missing.
   // there can be multiple transitions, separated with commas
 
-  nsCSSValue tempValue;
-  // first see if 'inherit' or '-moz-initial' is specified.  If one is,
-  // it can be the only thing specified, so don't attempt to parse any
-  // additional properties
-  if (ParseVariant(tempValue, VARIANT_INHERIT, nsnull)) {
-    for (PRUint32 i = 0; i < numProps; ++i) {
-      AppendValue(kTransitionProperties[i], tempValue);
-    }
-    return PR_TRUE;
-  }
+  nsCSSValue initialValues[numProps];
+  initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
+  initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
+                               eCSSUnit_Enumerated);
+  initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
+  initialValues[3].SetAllValue();
 
   nsCSSValue values[numProps];
-  nsCSSValueList *cur[numProps] = { nsnull, nsnull, nsnull, nsnull };
-  PRBool atEOP = PR_FALSE; // at end of property?
-  for (;;) { // loop over comma-separated transitions
-    // whether a particular subproperty was specified for this transition
-    PRBool parsedProperty[numProps] =
-      { PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE };
-    for (;;) { // loop over values within a transition
-      PRBool foundProperty = PR_FALSE;
-      // check to see if we're at the end of one full transition definition
-      // (either because we hit a comma or because we hit the end of the
-      // property definition)
-      if (ExpectSymbol(',', PR_TRUE))
-        break;
-      if (CheckEndProperty()) {
-        atEOP = PR_TRUE;
-        break;
-      }
-
-      // else, try to parse the next transition sub-property
-      for (PRUint32 i = 0; !foundProperty && i < numProps; ++i) {
-        if (!parsedProperty[i]) {
-          // if we haven't found this property yet, try to parse it
-          if (ParseSingleValueProperty(tempValue, kTransitionProperties[i])) {
-            parsedProperty[i] = PR_TRUE;
-            cur[i] = AppendValueToList(values[i], cur[i], tempValue);
-            foundProperty = PR_TRUE;
-            break; // out of inner loop; continue looking for next sub-property
-          }
-        }
-      }
-      if (!foundProperty) {
-        // We're not at a ',' or at the end of the property, but we couldn't
-        // parse any of the sub-properties, so the declaration is invalid.
-        return PR_FALSE;
-      }
-    }
-
-    // We hit the end of the property or the end of one transition
-    // definition, add its components to the list.
-    for (PRUint32 i = 0; i < numProps; ++i) {
-      // If all of the subproperties were not explicitly specified, fill
-      // in the missing ones with initial values.
-      if (!parsedProperty[i]) {
-        switch (kTransitionProperties[i]) {
-          case eCSSProperty_transition_property:
-            tempValue.SetAllValue();
-            break;
-          case eCSSProperty_transition_duration:
-          case eCSSProperty_transition_delay:
-            tempValue.SetFloatValue(0.0, eCSSUnit_Seconds);
-            break;
-          case eCSSProperty_transition_timing_function:
-            tempValue.SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
-                                  eCSSUnit_Enumerated);
-            break;
-          default:
-            NS_ABORT_IF_FALSE(PR_FALSE, "Invalid transition property");
-        }
-        cur[i] = AppendValueToList(values[i], cur[i], tempValue);
-      }
-    }
-
-    if (atEOP)
-      break;
-    // else we just hit a ',' so continue parsing the next compound transition
+
+  ParseAnimationOrTransitionShorthandResult spres =
+    ParseAnimationOrTransitionShorthand(kTransitionProperties,
+                                        initialValues, values, numProps);
+  if (spres != eParseAnimationOrTransitionShorthand_Values) {
+    return spres != eParseAnimationOrTransitionShorthand_Error;
   }
 
   // Make two checks on the list for 'transition-property':
   //   + If there is more than one item, then none of the items can be
   //     'none' or 'all'.
   //   + None of the items can be 'inherit' or 'initial' (this is the case,
   //     like with counter-reset &c., where CSS 2.1 specifies 'initial', so
   //     we should check it without the -moz- prefix).
@@ -8164,16 +8208,69 @@ CSSParserImpl::ParseTransition()
   // Save all parsed transition sub-properties in mTempData
   for (PRUint32 i = 0; i < numProps; ++i) {
     AppendValue(kTransitionProperties[i], values[i]);
   }
   return PR_TRUE;
 }
 
 PRBool
+CSSParserImpl::ParseAnimation()
+{
+  static const nsCSSProperty kAnimationProperties[] = {
+    eCSSProperty_animation_duration,
+    eCSSProperty_animation_timing_function,
+    // Must check 'animation-delay' after 'animation-duration', since
+    // that's our assumption about what the spec means for the shorthand
+    // syntax (the first time given is the duration, and the second
+    // given is the delay).
+    eCSSProperty_animation_delay,
+    eCSSProperty_animation_direction,
+    eCSSProperty_animation_fill_mode,
+    eCSSProperty_animation_iteration_count,
+    eCSSProperty_animation_play_state,
+    // Must check 'animation-name' after 'animation-timing-function',
+    // 'animation-direction', 'animation-fill-mode',
+    // 'animation-iteration-count', and 'animation-play-state' since
+    // 'animation-property' accepts any keyword.
+    eCSSProperty_animation_name
+  };
+  static const PRUint32 numProps = NS_ARRAY_LENGTH(kAnimationProperties);
+  // this is a shorthand property that accepts -property, -delay,
+  // -duration, and -timing-function with some components missing.
+  // there can be multiple animations, separated with commas
+
+  nsCSSValue initialValues[numProps];
+  initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
+  initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
+                               eCSSUnit_Enumerated);
+  initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
+  initialValues[3].SetIntValue(NS_STYLE_ANIMATION_DIRECTION_NORMAL, eCSSUnit_Enumerated);
+  initialValues[4].SetIntValue(NS_STYLE_ANIMATION_FILL_MODE_NONE, eCSSUnit_Enumerated);
+  initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
+  initialValues[6].SetIntValue(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING, eCSSUnit_Enumerated);
+  initialValues[7].SetNoneValue();
+
+  nsCSSValue values[numProps];
+
+  ParseAnimationOrTransitionShorthandResult spres =
+    ParseAnimationOrTransitionShorthand(kAnimationProperties,
+                                        initialValues, values, numProps);
+  if (spres != eParseAnimationOrTransitionShorthand_Values) {
+    return spres != eParseAnimationOrTransitionShorthand_Error;
+  }
+
+  // Save all parsed animation sub-properties in mTempData
+  for (PRUint32 i = 0; i < numProps; ++i) {
+    AppendValue(kAnimationProperties[i], values[i]);
+  }
+  return PR_TRUE;
+}
+
+PRBool
 CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, PRBool aIsBoxShadow)
 {
   // A shadow list item is an array, with entries in this sequence:
   enum {
     IndexX,
     IndexY,
     IndexRadius,
     IndexSpread,  // only for box-shadow
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -359,16 +359,106 @@ CSS_PROP_FONT(
     CSS_PROP_DOMPROP_PREFIXED(SystemFont),
     CSS_PROPERTY_PARSE_INACCESSIBLE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE,
     0,
     kFontKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 #endif
+CSS_PROP_SHORTHAND(
+    -moz-animation,
+    animation,
+    CSS_PROP_DOMPROP_PREFIXED(Animation),
+    CSS_PROPERTY_PARSE_FUNCTION)
+CSS_PROP_DISPLAY(
+    -moz-animation-delay,
+    animation_delay,
+    CSS_PROP_DOMPROP_PREFIXED(AnimationDelay),
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    VARIANT_TIME, // used by list parsing
+    nsnull,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_DISPLAY(
+    -moz-animation-direction,
+    animation_direction,
+    CSS_PROP_DOMPROP_PREFIXED(AnimationDirection),
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    VARIANT_KEYWORD, // used by list parsing
+    kAnimationDirectionKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_DISPLAY(
+    -moz-animation-duration,
+    animation_duration,
+    CSS_PROP_DOMPROP_PREFIXED(AnimationDuration),
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    VARIANT_TIME, // used by list parsing
+    nsnull,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_DISPLAY(
+    -moz-animation-fill-mode,
+    animation_fill_mode,
+    CSS_PROP_DOMPROP_PREFIXED(AnimationFillMode),
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    VARIANT_KEYWORD, // used by list parsing
+    kAnimationFillModeKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_DISPLAY(
+    -moz-animation-iteration-count,
+    animation_iteration_count,
+    CSS_PROP_DOMPROP_PREFIXED(AnimationIterationCount),
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        // nonnegative per
+        // http://lists.w3.org/Archives/Public/www-style/2011Mar/0355.html
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    VARIANT_KEYWORD | VARIANT_NUMBER, // used by list parsing
+    kAnimationIterationCountKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_DISPLAY(
+    -moz-animation-name,
+    animation_name,
+    CSS_PROP_DOMPROP_PREFIXED(AnimationName),
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    // FIXME: The spec should say something about 'inherit' and 'initial'
+    // not being allowed.
+    VARIANT_NONE | VARIANT_IDENTIFIER_NO_INHERIT, // used by list parsing
+    nsnull,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_DISPLAY(
+    -moz-animation-play-state,
+    animation_play_state,
+    CSS_PROP_DOMPROP_PREFIXED(AnimationPlayState),
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    VARIANT_KEYWORD, // used by list parsing
+    kAnimationPlayStateKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_DISPLAY(
+    -moz-animation-timing-function,
+    animation_timing_function,
+    CSS_PROP_DOMPROP_PREFIXED(AnimationTimingFunction),
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    VARIANT_KEYWORD | VARIANT_TIMING_FUNCTION, // used by list parsing
+    kTransitionTimingFunctionKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
 CSS_PROP_BACKENDONLY(
     azimuth,
     azimuth,
     Azimuth,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     0,
     kAzimuthKTable)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -440,16 +440,41 @@ nsCSSProps::OtherNameFor(nsCSSProperty a
     default:
       NS_ABORT_IF_FALSE(PR_FALSE, "bad caller");
   }
   return eCSSProperty_UNKNOWN;
 }
 
 /***************************************************************************/
 
+const PRInt32 nsCSSProps::kAnimationDirectionKTable[] = {
+  eCSSKeyword_normal, NS_STYLE_ANIMATION_DIRECTION_NORMAL,
+  eCSSKeyword_alternate, NS_STYLE_ANIMATION_DIRECTION_ALTERNATE,
+  eCSSKeyword_UNKNOWN,-1
+};
+
+const PRInt32 nsCSSProps::kAnimationFillModeKTable[] = {
+  eCSSKeyword_none, NS_STYLE_ANIMATION_FILL_MODE_NONE,
+  eCSSKeyword_forwards, NS_STYLE_ANIMATION_FILL_MODE_FORWARDS,
+  eCSSKeyword_backwards, NS_STYLE_ANIMATION_FILL_MODE_BACKWARDS,
+  eCSSKeyword_both, NS_STYLE_ANIMATION_FILL_MODE_BOTH,
+  eCSSKeyword_UNKNOWN,-1
+};
+
+const PRInt32 nsCSSProps::kAnimationIterationCountKTable[] = {
+  eCSSKeyword_infinite, NS_STYLE_ANIMATION_ITERATION_COUNT_INFINITE,
+  eCSSKeyword_UNKNOWN,-1
+};
+
+const PRInt32 nsCSSProps::kAnimationPlayStateKTable[] = {
+  eCSSKeyword_running, NS_STYLE_ANIMATION_PLAY_STATE_RUNNING,
+  eCSSKeyword_paused, NS_STYLE_ANIMATION_PLAY_STATE_PAUSED,
+  eCSSKeyword_UNKNOWN,-1
+};
+
 const PRInt32 nsCSSProps::kAppearanceKTable[] = {
   eCSSKeyword_none,                   NS_THEME_NONE,
   eCSSKeyword_button,                 NS_THEME_BUTTON,
   eCSSKeyword_radio,                  NS_THEME_RADIO,
   eCSSKeyword_checkbox,               NS_THEME_CHECKBOX,
   eCSSKeyword_button_bevel,           NS_THEME_BUTTON_BEVEL,
   eCSSKeyword_toolbox,                NS_THEME_TOOLBOX,
   eCSSKeyword_toolbar,                NS_THEME_TOOLBAR,
@@ -1607,16 +1632,31 @@ const PRUint32 nsCSSProps::kFlagsTable[e
   flags_,
 #include "nsCSSPropList.h"
 #undef CSS_PROP
 #define CSS_PROP_SHORTHAND(name_, id_, method_, flags_) flags_,
 #include "nsCSSPropList.h"
 #undef CSS_PROP_SHORTHAND
 };
 
+static const nsCSSProperty gAnimationSubpropTable[] = {
+  eCSSProperty_animation_duration,
+  eCSSProperty_animation_timing_function,
+  eCSSProperty_animation_delay,
+  eCSSProperty_animation_direction,
+  eCSSProperty_animation_fill_mode,
+  eCSSProperty_animation_iteration_count,
+  eCSSProperty_animation_play_state,
+  // List animation-name last so we serialize it last, in case it has
+  // a value that conflicts with one of the other properties.  (See
+  // how Declaration::GetValue serializes 'animation'.
+  eCSSProperty_animation_name,
+  eCSSProperty_UNKNOWN
+};
+
 static const nsCSSProperty gBorderRadiusSubpropTable[] = {
   // Code relies on these being in topleft-topright-bottomright-bottomleft
   // order.
   eCSSProperty_border_top_left_radius,
   eCSSProperty_border_top_right_radius,
   eCSSProperty_border_bottom_right_radius,
   eCSSProperty_border_bottom_left_radius,
   eCSSProperty_UNKNOWN
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -324,16 +324,21 @@ public:
 
 public:
 
 #define CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(iter_, prop_)                    \
   for (const nsCSSProperty* iter_ = nsCSSProps::SubpropertyEntryFor(prop_);   \
        *iter_ != eCSSProperty_UNKNOWN; ++iter_)
 
   // Keyword/Enum value tables
+  static const PRInt32 kAnimationDirectionKTable[];
+  static const PRInt32 kAnimationFillModeKTable[];
+  static const PRInt32 kAnimationIterationCountKTable[];
+  static const PRInt32 kAnimationPlayStateKTable[];
+  static const PRInt32 kAnimationTimingFunctionKTable[];
   static const PRInt32 kAppearanceKTable[];
   static const PRInt32 kAzimuthKTable[];
   static const PRInt32 kBackgroundAttachmentKTable[];
   static const PRInt32 kBackgroundInlinePolicyKTable[];
   static const PRInt32 kBackgroundOriginKTable[];
   static const PRInt32 kBackgroundPositionKTable[];
   static const PRInt32 kBackgroundRepeatKTable[];
   static const PRInt32 kBackgroundSizeKTable[];
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -3924,16 +3924,195 @@ nsComputedDOMStyle::DoGetTransitionTimin
   do {
     AppendTimingFunction(valueList,
                          display->mTransitions[i].GetTimingFunction());
   } while (++i < display->mTransitionTimingFunctionCount);
 
   return valueList;
 }
 
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetAnimationName()
+{
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
+
+  NS_ABORT_IF_FALSE(display->mAnimationNameCount > 0,
+                    "first item must be explicit");
+  PRUint32 i = 0;
+  do {
+    const nsAnimation *animation = &display->mAnimations[i];
+    nsROCSSPrimitiveValue* property = GetROCSSPrimitiveValue();
+    valueList->AppendCSSValue(property);
+
+    const nsString& name = animation->GetName();
+    if (name.IsEmpty()) {
+      property->SetIdent(eCSSKeyword_none);
+    } else {
+      nsAutoString escaped;
+      nsStyleUtil::AppendEscapedCSSIdent(animation->GetName(), escaped);
+      property->SetString(escaped); // really want SetIdent
+    }
+  } while (++i < display->mAnimationNameCount);
+
+  return valueList;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetAnimationDelay()
+{
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
+
+  NS_ABORT_IF_FALSE(display->mAnimationDelayCount > 0,
+                    "first item must be explicit");
+  PRUint32 i = 0;
+  do {
+    const nsAnimation *animation = &display->mAnimations[i];
+    nsROCSSPrimitiveValue* delay = GetROCSSPrimitiveValue();
+    valueList->AppendCSSValue(delay);
+    delay->SetTime((float)animation->GetDelay() / (float)PR_MSEC_PER_SEC);
+  } while (++i < display->mAnimationDelayCount);
+
+  return valueList;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetAnimationDuration()
+{
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
+
+  NS_ABORT_IF_FALSE(display->mAnimationDurationCount > 0,
+                    "first item must be explicit");
+  PRUint32 i = 0;
+  do {
+    const nsAnimation *animation = &display->mAnimations[i];
+    nsROCSSPrimitiveValue* duration = GetROCSSPrimitiveValue();
+    valueList->AppendCSSValue(duration);
+
+    duration->SetTime((float)animation->GetDuration() / (float)PR_MSEC_PER_SEC);
+  } while (++i < display->mAnimationDurationCount);
+
+  return valueList;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetAnimationTimingFunction()
+{
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
+
+  NS_ABORT_IF_FALSE(display->mAnimationTimingFunctionCount > 0,
+                    "first item must be explicit");
+  PRUint32 i = 0;
+  do {
+    AppendTimingFunction(valueList,
+                         display->mAnimations[i].GetTimingFunction());
+  } while (++i < display->mAnimationTimingFunctionCount);
+
+  return valueList;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetAnimationDirection()
+{
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
+
+  NS_ABORT_IF_FALSE(display->mAnimationDirectionCount > 0,
+                    "first item must be explicit");
+  PRUint32 i = 0;
+  do {
+    const nsAnimation *animation = &display->mAnimations[i];
+    nsROCSSPrimitiveValue* direction = GetROCSSPrimitiveValue();
+    valueList->AppendCSSValue(direction);
+    direction->SetIdent(
+      nsCSSProps::ValueToKeywordEnum(animation->GetDirection(),
+                                     nsCSSProps::kAnimationDirectionKTable));
+  } while (++i < display->mAnimationDirectionCount);
+
+  return valueList;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetAnimationFillMode()
+{
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
+
+  NS_ABORT_IF_FALSE(display->mAnimationFillModeCount > 0,
+                    "first item must be explicit");
+  PRUint32 i = 0;
+  do {
+    const nsAnimation *animation = &display->mAnimations[i];
+    nsROCSSPrimitiveValue* fillMode = GetROCSSPrimitiveValue();
+    valueList->AppendCSSValue(fillMode);
+    fillMode->SetIdent(
+      nsCSSProps::ValueToKeywordEnum(animation->GetFillMode(),
+                                     nsCSSProps::kAnimationFillModeKTable));
+  } while (++i < display->mAnimationFillModeCount);
+
+  return valueList;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetAnimationIterationCount()
+{
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
+
+  NS_ABORT_IF_FALSE(display->mAnimationIterationCountCount > 0,
+                    "first item must be explicit");
+  PRUint32 i = 0;
+  do {
+    const nsAnimation *animation = &display->mAnimations[i];
+    nsROCSSPrimitiveValue* iterationCount = GetROCSSPrimitiveValue();
+    valueList->AppendCSSValue(iterationCount);
+
+    float f = animation->GetIterationCount();
+    if (f == NS_IEEEPositiveInfinity()) {
+      iterationCount->SetIdent(eCSSKeyword_infinite);
+    } else {
+      iterationCount->SetNumber(f);
+    }
+  } while (++i < display->mAnimationIterationCountCount);
+
+  return valueList;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetAnimationPlayState()
+{
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
+
+  NS_ABORT_IF_FALSE(display->mAnimationPlayStateCount > 0,
+                    "first item must be explicit");
+  PRUint32 i = 0;
+  do {
+    const nsAnimation *animation = &display->mAnimations[i];
+    nsROCSSPrimitiveValue* playState = GetROCSSPrimitiveValue();
+    valueList->AppendCSSValue(playState);
+    playState->SetIdent(
+      nsCSSProps::ValueToKeywordEnum(animation->GetPlayState(),
+                                     nsCSSProps::kAnimationPlayStateKTable));
+  } while (++i < display->mAnimationPlayStateCount);
+
+  return valueList;
+}
+
 #define COMPUTED_STYLE_MAP_ENTRY(_prop, _method)              \
   { eCSSProperty_##_prop, &nsComputedDOMStyle::DoGet##_method, PR_FALSE }
 #define COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_prop, _method)       \
   { eCSSProperty_##_prop, &nsComputedDOMStyle::DoGet##_method, PR_TRUE }
 
 const nsComputedDOMStyle::ComputedStyleMapEntry*
 nsComputedDOMStyle::GetQueryablePropertyMap(PRUint32* aLength)
 {
@@ -4076,16 +4255,24 @@ nsComputedDOMStyle::GetQueryableProperty
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(width,                  Width),
     COMPUTED_STYLE_MAP_ENTRY(word_spacing,                  WordSpacing),
     COMPUTED_STYLE_MAP_ENTRY(z_index,                       ZIndex),
 
     /* ******************************* *\
      * Implementations of -moz- styles *
     \* ******************************* */
 
+    COMPUTED_STYLE_MAP_ENTRY(animation_delay,               AnimationDelay),
+    COMPUTED_STYLE_MAP_ENTRY(animation_direction,           AnimationDirection),
+    COMPUTED_STYLE_MAP_ENTRY(animation_duration,            AnimationDuration),
+    COMPUTED_STYLE_MAP_ENTRY(animation_fill_mode,           AnimationFillMode),
+    COMPUTED_STYLE_MAP_ENTRY(animation_iteration_count,     AnimationIterationCount),
+    COMPUTED_STYLE_MAP_ENTRY(animation_name,                AnimationName),
+    COMPUTED_STYLE_MAP_ENTRY(animation_play_state,          AnimationPlayState),
+    COMPUTED_STYLE_MAP_ENTRY(animation_timing_function,     AnimationTimingFunction),
     COMPUTED_STYLE_MAP_ENTRY(appearance,                    Appearance),
     COMPUTED_STYLE_MAP_ENTRY(background_clip,               BackgroundClip),
     COMPUTED_STYLE_MAP_ENTRY(_moz_background_inline_policy, BackgroundInlinePolicy),
     COMPUTED_STYLE_MAP_ENTRY(background_origin,             BackgroundOrigin),
     COMPUTED_STYLE_MAP_ENTRY(background_size,               MozBackgroundSize),
     COMPUTED_STYLE_MAP_ENTRY(binding,                       Binding),
     COMPUTED_STYLE_MAP_ENTRY(border_bottom_colors,          BorderBottomColors),
     COMPUTED_STYLE_MAP_ENTRY(border_image,                  BorderImage),
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -363,16 +363,26 @@ private:
   nsIDOMCSSValue* DoGetColumnRuleColor();
 
   /* CSS Transitions */
   nsIDOMCSSValue* DoGetTransitionProperty();
   nsIDOMCSSValue* DoGetTransitionDuration();
   nsIDOMCSSValue* DoGetTransitionDelay();
   nsIDOMCSSValue* DoGetTransitionTimingFunction();
 
+  /* CSS Animations */
+  nsIDOMCSSValue* DoGetAnimationName();
+  nsIDOMCSSValue* DoGetAnimationDuration();
+  nsIDOMCSSValue* DoGetAnimationDelay();
+  nsIDOMCSSValue* DoGetAnimationTimingFunction();
+  nsIDOMCSSValue* DoGetAnimationDirection();
+  nsIDOMCSSValue* DoGetAnimationFillMode();
+  nsIDOMCSSValue* DoGetAnimationIterationCount();
+  nsIDOMCSSValue* DoGetAnimationPlayState();
+
   /* SVG properties */
   nsIDOMCSSValue* DoGetFill();
   nsIDOMCSSValue* DoGetStroke();
   nsIDOMCSSValue* DoGetMarkerEnd();
   nsIDOMCSSValue* DoGetMarkerMid();
   nsIDOMCSSValue* DoGetMarkerStart();
   nsIDOMCSSValue* DoGetStrokeDasharray();
 
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3577,17 +3577,18 @@ nsRuleNode::ComputeUIResetData(void* aSt
   SetDiscrete(*aRuleData->ValueForWindowShadow(),
               ui->mWindowShadow, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentUI->mWindowShadow,
               NS_STYLE_WINDOW_SHADOW_DEFAULT, 0, 0, 0, 0);
 
   COMPUTE_END_RESET(UIReset, ui)
 }
 
-// Information about each transition property that is constant.
+// Information about each transition or animation property that is
+// constant.
 struct TransitionPropInfo {
   nsCSSProperty property;
   // Location of the count of the property's computed value.
   PRUint32 nsStyleDisplay::* sdCount;
 };
 
 // Each property's index in this array must match its index in the
 // mutable array |transitionPropData| below.
@@ -3597,18 +3598,39 @@ static const TransitionPropInfo transiti
   { eCSSProperty_transition_duration,
     &nsStyleDisplay::mTransitionDurationCount },
   { eCSSProperty_transition_property,
     &nsStyleDisplay::mTransitionPropertyCount },
   { eCSSProperty_transition_timing_function,
     &nsStyleDisplay::mTransitionTimingFunctionCount },
 };
 
-// Information about each transition property that changes during
-// ComputeDisplayData.
+// Each property's index in this array must match its index in the
+// mutable array |animationPropData| below.
+static const TransitionPropInfo animationPropInfo[8] = {
+  { eCSSProperty_animation_delay,
+    &nsStyleDisplay::mAnimationDelayCount },
+  { eCSSProperty_animation_duration,
+    &nsStyleDisplay::mAnimationDurationCount },
+  { eCSSProperty_animation_name,
+    &nsStyleDisplay::mAnimationNameCount },
+  { eCSSProperty_animation_timing_function,
+    &nsStyleDisplay::mAnimationTimingFunctionCount },
+  { eCSSProperty_animation_direction,
+    &nsStyleDisplay::mAnimationDirectionCount },
+  { eCSSProperty_animation_fill_mode,
+    &nsStyleDisplay::mAnimationFillModeCount },
+  { eCSSProperty_animation_play_state,
+    &nsStyleDisplay::mAnimationPlayStateCount },
+  { eCSSProperty_animation_iteration_count,
+    &nsStyleDisplay::mAnimationIterationCountCount },
+};
+
+// Information about each transition or animation property that changes
+// during ComputeDisplayData.
 struct TransitionPropData {
   const nsCSSValueList *list;
   nsCSSUnit unit;
   PRUint32 num;
 };
 
 const void*
 nsRuleNode::ComputeDisplayData(void* aStartStruct,
@@ -3669,17 +3691,16 @@ nsRuleNode::ComputeDisplayData(void* aSt
     // cache whether any of the properties are specified as 'inherit' so
     // we can use it below
 
     const nsCSSValue& value = *aRuleData->ValueFor(i.property);
     d.unit = value.GetUnit();
     d.list = (value.GetUnit() == eCSSUnit_List ||
               value.GetUnit() == eCSSUnit_ListDep)
       ? value.GetListValue() : nsnull;
-    d.num = 0;
 
     // General algorithm to determine how many total transitions we need
     // to build.  For each property:
     //  - if there is no value specified in for the property in
     //    displayData, use the values from the start struct, but only if
     //    they were explicitly specified
     //  - if there is a value specified for the property in displayData:
     //    - if the value is 'inherit', count the number of values for
@@ -3873,16 +3894,334 @@ nsRuleNode::ComputeDisplayData(void* aSt
       // until we're out of transitions to populate
       if (d.list) {
         d.list = d.list->mNext ? d.list->mNext :
           aRuleData->ValueFor(info.property)->GetListValue();
       }
     }
   }
 
+  // Each property's index in this array must match its index in the
+  // const array |animationPropInfo| above.
+  TransitionPropData animationPropData[8];
+  TransitionPropData& animDelay = animationPropData[0];
+  TransitionPropData& animDuration = animationPropData[1];
+  TransitionPropData& animName = animationPropData[2];
+  TransitionPropData& animTimingFunction = animationPropData[3];
+  TransitionPropData& animDirection = animationPropData[4];
+  TransitionPropData& animFillMode = animationPropData[5];
+  TransitionPropData& animPlayState = animationPropData[6];
+  TransitionPropData& animIterationCount = animationPropData[7];
+
+#define FOR_ALL_ANIMATION_PROPS(var_) \
+    for (PRUint32 var_ = 0; var_ < 8; ++var_)
+
+  // CSS Animations.  See transitions, above.
+
+  PRUint32 numAnimations = 0;
+  FOR_ALL_ANIMATION_PROPS(p) {
+    const TransitionPropInfo& i = animationPropInfo[p];
+    TransitionPropData& d = animationPropData[p];
+
+    // cache whether any of the properties are specified as 'inherit' so
+    // we can use it below
+
+    const nsCSSValue& value = *aRuleData->ValueFor(i.property);
+    d.unit = value.GetUnit();
+    d.list = (value.GetUnit() == eCSSUnit_List ||
+              value.GetUnit() == eCSSUnit_ListDep)
+      ? value.GetListValue() : nsnull;
+
+    // General algorithm to determine how many total animations we need
+    // to build.  For each property:
+    //  - if there is no value specified in for the property in
+    //    displayData, use the values from the start struct, but only if
+    //    they were explicitly specified
+    //  - if there is a value specified for the property in displayData:
+    //    - if the value is 'inherit', count the number of values for
+    //      that property are specified by the parent, but only those
+    //      that were explicitly specified
+    //    - otherwise, count the number of values specified in displayData
+
+
+    // calculate number of elements
+    if (d.unit == eCSSUnit_Inherit) {
+      d.num = parentDisplay->*(i.sdCount);
+      canStoreInRuleTree = PR_FALSE;
+    } else if (d.list) {
+      d.num = ListLength(d.list);
+    } else {
+      d.num = display->*(i.sdCount);
+    }
+    if (d.num > numAnimations)
+      numAnimations = d.num;
+  }
+
+  if (!display->mAnimations.SetLength(numAnimations)) {
+    NS_WARNING("failed to allocate animations array");
+    display->mAnimations.SetLength(1);
+    NS_ABORT_IF_FALSE(display->mAnimations.Length() == 1,
+                      "could not allocate using auto array buffer");
+    numAnimations = 1;
+    FOR_ALL_ANIMATION_PROPS(p) {
+      TransitionPropData& d = animationPropData[p];
+
+      d.num = 1;
+    }
+  }
+
+  FOR_ALL_ANIMATION_PROPS(p) {
+    const TransitionPropInfo& i = animationPropInfo[p];
+    TransitionPropData& d = animationPropData[p];
+
+    display->*(i.sdCount) = d.num;
+  }
+
+  // Fill in the animations we just allocated with the appropriate values.
+  for (PRUint32 i = 0; i < numAnimations; ++i) {
+    nsAnimation *animation = &display->mAnimations[i];
+
+    if (i >= animDelay.num) {
+      animation->SetDelay(display->mAnimations[i % animDelay.num].GetDelay());
+    } else if (animDelay.unit == eCSSUnit_Inherit) {
+      // FIXME (Bug 522599) (for all animation properties): write a test that
+      // detects when this was wrong for i >= animDelay.num if parent had
+      // count for this property not equal to length
+      NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDelayCount,
+                        "animDelay.num computed incorrectly");
+      NS_ABORT_IF_FALSE(!canStoreInRuleTree,
+                        "should have made canStoreInRuleTree false above");
+      animation->SetDelay(parentDisplay->mAnimations[i].GetDelay());
+    } else if (animDelay.unit == eCSSUnit_Initial) {
+      animation->SetDelay(0.0);
+    } else if (animDelay.list) {
+      switch (animDelay.list->mValue.GetUnit()) {
+        case eCSSUnit_Seconds:
+          animation->SetDelay(PR_MSEC_PER_SEC *
+                              animDelay.list->mValue.GetFloatValue());
+          break;
+        case eCSSUnit_Milliseconds:
+          animation->SetDelay(animDelay.list->mValue.GetFloatValue());
+          break;
+        default:
+          NS_NOTREACHED("Invalid delay unit");
+      }
+    }
+
+    if (i >= animDuration.num) {
+      animation->SetDuration(
+        display->mAnimations[i % animDuration.num].GetDuration());
+    } else if (animDuration.unit == eCSSUnit_Inherit) {
+      NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDurationCount,
+                        "animDuration.num computed incorrectly");
+      NS_ABORT_IF_FALSE(!canStoreInRuleTree,
+                        "should have made canStoreInRuleTree false above");
+      animation->SetDuration(parentDisplay->mAnimations[i].GetDuration());
+    } else if (animDuration.unit == eCSSUnit_Initial) {
+      animation->SetDuration(0.0);
+    } else if (animDuration.list) {
+      switch (animDuration.list->mValue.GetUnit()) {
+        case eCSSUnit_Seconds:
+          animation->SetDuration(PR_MSEC_PER_SEC *
+                                 animDuration.list->mValue.GetFloatValue());
+          break;
+        case eCSSUnit_Milliseconds:
+          animation->SetDuration(animDuration.list->mValue.GetFloatValue());
+          break;
+        default:
+          NS_NOTREACHED("Invalid duration unit");
+      }
+    }
+
+    if (i >= animName.num) {
+      animation->SetName(display->mAnimations[i % animName.num].GetName());
+    } else if (animName.unit == eCSSUnit_Inherit) {
+      NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationNameCount,
+                        "animName.num computed incorrectly");
+      NS_ABORT_IF_FALSE(!canStoreInRuleTree,
+                        "should have made canStoreInRuleTree false above");
+      animation->SetName(parentDisplay->mAnimations[i].GetName());
+    } else if (animName.unit == eCSSUnit_Initial) {
+      animation->SetName(EmptyString());
+    } else if (animName.list) {
+      switch (animName.list->mValue.GetUnit()) {
+        case eCSSUnit_Ident: {
+          nsDependentString
+            nameStr(animName.list->mValue.GetStringBufferValue());
+          animation->SetName(nameStr);
+          break;
+        }
+        case eCSSUnit_None: {
+          animation->SetName(EmptyString());
+          break;
+        }
+        default:
+          NS_ABORT_IF_FALSE(PR_FALSE,
+            nsPrintfCString(64, "Invalid animation-name unit %d",
+                                animName.list->mValue.GetUnit()).get());
+      }
+    }
+
+    if (i >= animTimingFunction.num) {
+      animation->SetTimingFunction(
+        display->mAnimations[i % animTimingFunction.num].GetTimingFunction());
+    } else if (animTimingFunction.unit == eCSSUnit_Inherit) {
+      NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationTimingFunctionCount,
+                        "animTimingFunction.num computed incorrectly");
+      NS_ABORT_IF_FALSE(!canStoreInRuleTree,
+                        "should have made canStoreInRuleTree false above");
+      animation->SetTimingFunction(
+        parentDisplay->mAnimations[i].GetTimingFunction());
+    } else if (animTimingFunction.unit == eCSSUnit_Initial) {
+      animation->SetTimingFunction(
+        nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
+    } else if (animTimingFunction.list) {
+      switch (animTimingFunction.list->mValue.GetUnit()) {
+        case eCSSUnit_Enumerated:
+          animation->SetTimingFunction(
+            nsTimingFunction(animTimingFunction.list->mValue.GetIntValue()));
+          break;
+        case eCSSUnit_Cubic_Bezier:
+          {
+            nsCSSValue::Array* array =
+              animTimingFunction.list->mValue.GetArrayValue();
+            NS_ASSERTION(array && array->Count() == 4,
+                         "Need 4 control points");
+            animation->SetTimingFunction(
+              nsTimingFunction(array->Item(0).GetFloatValue(),
+                               array->Item(1).GetFloatValue(),
+                               array->Item(2).GetFloatValue(),
+                               array->Item(3).GetFloatValue()));
+          }
+          break;
+        case eCSSUnit_Steps:
+          {
+            nsCSSValue::Array* array =
+              animTimingFunction.list->mValue.GetArrayValue();
+            NS_ASSERTION(array && array->Count() == 2,
+                         "Need 2 items");
+            NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer,
+                         "unexpected first value");
+            NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Enumerated &&
+                         (array->Item(1).GetIntValue() ==
+                           NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START ||
+                          array->Item(1).GetIntValue() ==
+                           NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END),
+                         "unexpected second value");
+            animation->SetTimingFunction(
+              nsTimingFunction((
+                array->Item(1).GetIntValue() ==
+                  NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END)
+                  ? nsTimingFunction::StepEnd : nsTimingFunction::StepStart,
+                array->Item(0).GetIntValue()));
+          }
+          break;
+        default:
+          NS_NOTREACHED("Invalid animation property unit");
+      }
+    }
+
+    if (i >= animDirection.num) {
+      animation->SetDirection(display->mAnimations[i % animDirection.num].GetDirection());
+    } else if (animDirection.unit == eCSSUnit_Inherit) {
+      NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDirectionCount,
+                        "animDirection.num computed incorrectly");
+      NS_ABORT_IF_FALSE(!canStoreInRuleTree,
+                        "should have made canStoreInRuleTree false above");
+      animation->SetDirection(parentDisplay->mAnimations[i].GetDirection());
+    } else if (animDirection.unit == eCSSUnit_Initial) {
+      animation->SetDirection(NS_STYLE_ANIMATION_DIRECTION_NORMAL);
+    } else if (animDirection.list) {
+      NS_ABORT_IF_FALSE(animDirection.list->mValue.GetUnit() == eCSSUnit_Enumerated,
+                        nsPrintfCString(64,
+                                        "Invalid animation-direction unit %d",
+                                        animDirection.list->mValue.GetUnit()).get());
+
+      animation->SetDirection(animDirection.list->mValue.GetIntValue());
+    }
+
+    if (i >= animFillMode.num) {
+      animation->SetFillMode(display->mAnimations[i % animFillMode.num].GetFillMode());
+    } else if (animFillMode.unit == eCSSUnit_Inherit) {
+      NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationFillModeCount,
+                        "animFillMode.num computed incorrectly");
+      NS_ABORT_IF_FALSE(!canStoreInRuleTree,
+                        "should have made canStoreInRuleTree false above");
+      animation->SetFillMode(parentDisplay->mAnimations[i].GetFillMode());
+    } else if (animFillMode.unit == eCSSUnit_Initial) {
+      animation->SetFillMode(NS_STYLE_ANIMATION_FILL_MODE_NONE);
+    } else if (animFillMode.list) {
+      NS_ABORT_IF_FALSE(animFillMode.list->mValue.GetUnit() == eCSSUnit_Enumerated,
+                        nsPrintfCString(64,
+                                        "Invalid animation-fill-mode unit %d",
+                                        animFillMode.list->mValue.GetUnit()).get());
+
+      animation->SetFillMode(animFillMode.list->mValue.GetIntValue());
+    }
+
+    if (i >= animPlayState.num) {
+      animation->SetPlayState(display->mAnimations[i % animPlayState.num].GetPlayState());
+    } else if (animPlayState.unit == eCSSUnit_Inherit) {
+      NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationPlayStateCount,
+                        "animPlayState.num computed incorrectly");
+      NS_ABORT_IF_FALSE(!canStoreInRuleTree,
+                        "should have made canStoreInRuleTree false above");
+      animation->SetPlayState(parentDisplay->mAnimations[i].GetPlayState());
+    } else if (animPlayState.unit == eCSSUnit_Initial) {
+      animation->SetPlayState(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING);
+    } else if (animPlayState.list) {
+      NS_ABORT_IF_FALSE(animPlayState.list->mValue.GetUnit() == eCSSUnit_Enumerated,
+                        nsPrintfCString(64,
+                                        "Invalid animation-play-state unit %d",
+                                        animPlayState.list->mValue.GetUnit()).get());
+
+      animation->SetPlayState(animPlayState.list->mValue.GetIntValue());
+    }
+
+    if (i >= animIterationCount.num) {
+      animation->SetIterationCount(display->mAnimations[i % animIterationCount.num].GetIterationCount());
+    } else if (animIterationCount.unit == eCSSUnit_Inherit) {
+      NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationIterationCountCount,
+                        "animIterationCount.num computed incorrectly");
+      NS_ABORT_IF_FALSE(!canStoreInRuleTree,
+                        "should have made canStoreInRuleTree false above");
+      animation->SetIterationCount(parentDisplay->mAnimations[i].GetIterationCount());
+    } else if (animIterationCount.unit == eCSSUnit_Initial) {
+      animation->SetIterationCount(1.0f);
+    } else if (animIterationCount.list) {
+      switch(animIterationCount.list->mValue.GetUnit()) {
+        case eCSSUnit_Enumerated:
+          NS_ABORT_IF_FALSE(animIterationCount.list->mValue.GetIntValue() ==
+                              NS_STYLE_ANIMATION_ITERATION_COUNT_INFINITE,
+                            "unexpected value");
+          animation->SetIterationCount(NS_IEEEPositiveInfinity());
+          break;
+        case eCSSUnit_Number:
+          animation->SetIterationCount(
+            animIterationCount.list->mValue.GetFloatValue());
+          break;
+        default:
+          NS_ABORT_IF_FALSE(PR_FALSE,
+                            "unexpected animation-iteration-count unit");
+      }
+    }
+
+    FOR_ALL_ANIMATION_PROPS(p) {
+      const TransitionPropInfo& info = animationPropInfo[p];
+      TransitionPropData& d = animationPropData[p];
+
+      // if we're at the end of the list, start at the beginning and repeat
+      // until we're out of animations to populate
+      if (d.list) {
+        d.list = d.list->mNext ? d.list->mNext :
+          aRuleData->ValueFor(info.property)->GetListValue();
+      }
+    }
+  }
+
   // opacity: factor, inherit, initial
   SetFactor(*aRuleData->ValueForOpacity(), display->mOpacity, canStoreInRuleTree,
             parentDisplay->mOpacity, 1.0f, SETFCT_OPACITY);
 
   // display: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForDisplay(), display->mDisplay, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentDisplay->mDisplay,
               NS_STYLE_DISPLAY_INLINE, 0, 0, 0, 0);
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1978,16 +1978,41 @@ void nsTransition::SetUnknownProperty(co
 {
   NS_ASSERTION(nsCSSProps::LookupProperty(aUnknownProperty) ==
                  eCSSProperty_UNKNOWN,
                "should be unknown property");
   mProperty = eCSSProperty_UNKNOWN;
   mUnknownProperty = do_GetAtom(aUnknownProperty);
 }
 
+nsAnimation::nsAnimation(const nsAnimation& aCopy)
+  : mTimingFunction(aCopy.mTimingFunction)
+  , mDuration(aCopy.mDuration)
+  , mDelay(aCopy.mDelay)
+  , mName(aCopy.mName)
+  , mDirection(aCopy.mDirection)
+  , mFillMode(aCopy.mFillMode)
+  , mPlayState(aCopy.mPlayState)
+  , mIterationCount(aCopy.mIterationCount)
+{
+}
+
+void
+nsAnimation::SetInitialValues()
+{
+  mTimingFunction = nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE);
+  mDuration = 0.0;
+  mDelay = 0.0;
+  mName = EmptyString();
+  mDirection = NS_STYLE_ANIMATION_DIRECTION_NORMAL;
+  mFillMode = NS_STYLE_ANIMATION_FILL_MODE_NONE;
+  mPlayState = NS_STYLE_ANIMATION_PLAY_STATE_RUNNING;
+  mIterationCount = 1.0f;
+}
+
 nsStyleDisplay::nsStyleDisplay()
 {
   MOZ_COUNT_CTOR(nsStyleDisplay);
   mAppearance = NS_THEME_NONE;
   mDisplay = NS_STYLE_DISPLAY_INLINE;
   mOriginalDisplay = NS_STYLE_DISPLAY_NONE;
   mPosition = NS_STYLE_POSITION_STATIC;
   mFloats = NS_STYLE_FLOAT_NONE;
@@ -1998,32 +2023,55 @@ nsStyleDisplay::nsStyleDisplay()
   mOverflowY = NS_STYLE_OVERFLOW_VISIBLE;
   mResize = NS_STYLE_RESIZE_NONE;
   mClipFlags = NS_STYLE_CLIP_AUTO;
   mClip.SetRect(0,0,0,0);
   mOpacity = 1.0f;
   mSpecifiedTransform = nsnull;
   mTransformOrigin[0].SetPercentValue(0.5f); // Transform is centered on origin
   mTransformOrigin[1].SetPercentValue(0.5f); 
+
   mTransitions.AppendElement();
   NS_ABORT_IF_FALSE(mTransitions.Length() == 1,
                     "appending within auto buffer should never fail");
   mTransitions[0].SetInitialValues();
   mTransitionTimingFunctionCount = 1;
   mTransitionDurationCount = 1;
   mTransitionDelayCount = 1;
   mTransitionPropertyCount = 1;
+
+  mAnimations.AppendElement();
+  NS_ABORT_IF_FALSE(mAnimations.Length() == 1,
+                    "appending within auto buffer should never fail");
+  mAnimations[0].SetInitialValues();
+  mAnimationTimingFunctionCount = 1;
+  mAnimationDurationCount = 1;
+  mAnimationDelayCount = 1;
+  mAnimationNameCount = 1;
+  mAnimationDirectionCount = 1;
+  mAnimationFillModeCount = 1;
+  mAnimationPlayStateCount = 1;
+  mAnimationIterationCountCount = 1;
 }
 
 nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource)
   : mTransitions(aSource.mTransitions)
   , mTransitionTimingFunctionCount(aSource.mTransitionTimingFunctionCount)
   , mTransitionDurationCount(aSource.mTransitionDurationCount)
   , mTransitionDelayCount(aSource.mTransitionDelayCount)
   , mTransitionPropertyCount(aSource.mTransitionPropertyCount)
+  , mAnimations(aSource.mAnimations)
+  , mAnimationTimingFunctionCount(aSource.mAnimationTimingFunctionCount)
+  , mAnimationDurationCount(aSource.mAnimationDurationCount)
+  , mAnimationDelayCount(aSource.mAnimationDelayCount)
+  , mAnimationNameCount(aSource.mAnimationNameCount)
+  , mAnimationDirectionCount(aSource.mAnimationDirectionCount)
+  , mAnimationFillModeCount(aSource.mAnimationFillModeCount)
+  , mAnimationPlayStateCount(aSource.mAnimationPlayStateCount)
+  , mAnimationIterationCountCount(aSource.mAnimationIterationCountCount)
 {
   MOZ_COUNT_CTOR(nsStyleDisplay);
   mAppearance = aSource.mAppearance;
   mDisplay = aSource.mDisplay;
   mOriginalDisplay = aSource.mOriginalDisplay;
   mBinding = aSource.mBinding;
   mPosition = aSource.mPosition;
   mFloats = aSource.mFloats;
@@ -2110,17 +2158,21 @@ nsChangeHint nsStyleDisplay::CalcDiffere
   // transition-duration, transition-delay, and transition-timing-function
   // properties is to do nothing.  In other words, the transition
   // property that matters is what it is when the transition begins, and
   // we don't stop a transition later because the transition property
   // changed.
   // We do handle changes to transition-property, but we don't need to
   // bother with anything here, since the transition manager is notified
   // of any style context change anyway.
-  
+
+  // Note: Likewise, for animation-*, the animation manager gets
+  // notified about every new style context constructed, and it uses
+  // that opportunity to handle dynamic changes appropriately.
+
   return hint;
 }
 
 #ifdef DEBUG
 /* static */
 nsChangeHint nsStyleDisplay::MaxDifference()
 {
   // All the parts of FRAMECHANGE are present above in CalcDifference.
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1381,17 +1381,16 @@ private:
 struct nsTransition {
   nsTransition() { /* leaves uninitialized; see also SetInitialValues */ }
   explicit nsTransition(const nsTransition& aCopy);
 
   void SetInitialValues();
 
   // Delay and Duration are in milliseconds
 
-  nsTimingFunction& GetTimingFunction() { return mTimingFunction; }
   const nsTimingFunction& GetTimingFunction() const { return mTimingFunction; }
   float GetDelay() const { return mDelay; }
   float GetDuration() const { return mDuration; }
   nsCSSProperty GetProperty() const { return mProperty; }
   nsIAtom* GetUnknownProperty() const { return mUnknownProperty; }
 
   void SetTimingFunction(const nsTimingFunction& aTimingFunction)
     { mTimingFunction = aTimingFunction; }
@@ -1413,16 +1412,55 @@ private:
   nsTimingFunction mTimingFunction;
   float mDuration;
   float mDelay;
   nsCSSProperty mProperty;
   nsCOMPtr<nsIAtom> mUnknownProperty; // used when mProperty is
                                       // eCSSProperty_UNKNOWN
 };
 
+struct nsAnimation {
+  nsAnimation() { /* leaves uninitialized; see also SetInitialValues */ }
+  explicit nsAnimation(const nsAnimation& aCopy);
+
+  void SetInitialValues();
+
+  // Delay and Duration are in milliseconds
+
+  const nsTimingFunction& GetTimingFunction() const { return mTimingFunction; }
+  float GetDelay() const { return mDelay; }
+  float GetDuration() const { return mDuration; }
+  const nsString& GetName() const { return mName; }
+  PRUint8 GetDirection() const { return mDirection; }
+  PRUint8 GetFillMode() const { return mFillMode; }
+  PRUint8 GetPlayState() const { return mPlayState; }
+  float GetIterationCount() const { return mIterationCount; }
+
+  void SetTimingFunction(const nsTimingFunction& aTimingFunction)
+    { mTimingFunction = aTimingFunction; }
+  void SetDelay(float aDelay) { mDelay = aDelay; }
+  void SetDuration(float aDuration) { mDuration = aDuration; }
+  void SetName(const nsSubstring& aName) { mName = aName; }
+  void SetDirection(PRUint8 aDirection) { mDirection = aDirection; }
+  void SetFillMode(PRUint8 aFillMode) { mFillMode = aFillMode; }
+  void SetPlayState(PRUint8 aPlayState) { mPlayState = aPlayState; }
+  void SetIterationCount(float aIterationCount)
+    { mIterationCount = aIterationCount; }
+
+private:
+  nsTimingFunction mTimingFunction;
+  float mDuration;
+  float mDelay;
+  nsString mName; // empty string for 'none'
+  PRUint8 mDirection;
+  PRUint8 mFillMode;
+  PRUint8 mPlayState;
+  float mIterationCount; // NS_IEEEPositiveInfinity() means infinite
+};
+
 struct nsStyleDisplay {
   nsStyleDisplay();
   nsStyleDisplay(const nsStyleDisplay& aOther);
   ~nsStyleDisplay() {
     MOZ_COUNT_DTOR(nsStyleDisplay);
   }
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
@@ -1468,16 +1506,28 @@ struct nsStyleDisplay {
   nsAutoTArray<nsTransition, 1> mTransitions; // [reset]
   // The number of elements in mTransitions that are not from repeating
   // a list due to another property being longer.
   PRUint32 mTransitionTimingFunctionCount,
            mTransitionDurationCount,
            mTransitionDelayCount,
            mTransitionPropertyCount;
 
+  nsAutoTArray<nsAnimation, 1> mAnimations; // [reset]
+  // The number of elements in mAnimations that are not from repeating
+  // a list due to another property being longer.
+  PRUint32 mAnimationTimingFunctionCount,
+           mAnimationDurationCount,
+           mAnimationDelayCount,
+           mAnimationNameCount,
+           mAnimationDirectionCount,
+           mAnimationFillModeCount,
+           mAnimationPlayStateCount,
+           mAnimationIterationCountCount;
+
   PRBool IsBlockInside() const {
     return NS_STYLE_DISPLAY_BLOCK == mDisplay ||
            NS_STYLE_DISPLAY_LIST_ITEM == mDisplay ||
            NS_STYLE_DISPLAY_INLINE_BLOCK == mDisplay;
     // Should TABLE_CELL and TABLE_CAPTION go here?  They have
     // block frames nested inside of them.
     // (But please audit all callers before changing.)
   }
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -70,16 +70,91 @@ function initial_font_family_is_sans_ser
 	// 'sans-serif'.
 	var div = document.createElement("div");
 	div.setAttribute("style", "font: -moz-initial");
 	return getComputedStyle(div, "").fontFamily == "sans-serif";
 }
 var gInitialFontFamilyIsSansSerif = initial_font_family_is_sans_serif();
 
 var gCSSProperties = {
+	"-moz-animation": {
+		domProp: "MozAnimation",
+		inherited: false,
+		type: CSS_TYPE_TRUE_SHORTHAND,
+		subproperties: [ "-moz-animation-name", "-moz-animation-duration", "-moz-animation-timing-function", "-moz-animation-delay", "-moz-animation-direction", "-moz-animation-fill-mode", "-moz-animation-iteration-count", "-moz-animation-play-state" ],
+		initial_values: [ "none none 0s 0s ease normal running 1.0", "none", "0s", "ease", "normal", "running", "1.0" ],
+		other_values: [ "bounce 1s linear 2s", "bounce 1s 2s linear", "bounce linear 1s 2s", "linear bounce 1s 2s", "linear 1s bounce 2s", "linear 1s 2s bounce", "1s bounce linear 2s", "1s bounce 2s linear", "1s 2s bounce linear", "1s linear bounce 2s", "1s linear 2s bounce", "1s 2s linear bounce", "bounce linear 1s", "bounce 1s linear", "linear bounce 1s", "linear 1s bounce", "1s bounce linear", "1s linear bounce", "1s 2s bounce", "1s bounce 2s", "bounce 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "bounce 1s", "1s bounce", "linear 1s", "1s linear", "1s 2s", "2s 1s", "bounce", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s bounce, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32bounce linear 2s", "1s -bounce linear 2s", "1s -\\32bounce linear 2s", "1s \\32 0bounce linear 2s", "1s -\\32 0bounce linear 2s", "1s \\2bounce linear 2s", "1s -\\2bounce linear 2s", "2s, 1s bounce", "1s bounce, 2s", "2s all, 1s bounce", "1s bounce, 2s all", "1s bounce, 2s none", "2s none, 1s bounce", "2s bounce, 1s all", "2s all, 1s bounce" ],
+		invalid_values: [  "2s inherit", "inherit 2s", "2s bounce, 1s inherit", "2s inherit, 1s bounce", "2s initial" ]
+	},
+	"-moz-animation-delay": {
+		domProp: "MozAnimationDelay",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "0s", "0ms" ],
+		other_values: [ "1s", "250ms", "-100ms", "-1s", "1s, 250ms, 2.3s"],
+		invalid_values: [ "0", "0px" ]
+	},
+	"-moz-animation-direction": {
+		domProp: "MozAnimationDirection",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "normal" ],
+		other_values: [ "alternate", "normal, alternate", "alternate, normal", "normal, normal", "normal, normal, normal" ],
+		invalid_values: [ "normal normal", "inherit, normal" ]
+	},
+	"-moz-animation-duration": {
+		domProp: "MozAnimationDuration",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "0s", "0ms" ],
+		other_values: [ "1s", "250ms", "-1ms", "-2s", "1s, 250ms, 2.3s"],
+		invalid_values: [ "0", "0px" ]
+	},
+	"-moz-animation-fill-mode": {
+		domProp: "MozAnimationFillMode",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "none" ],
+		other_values: [ "forwards", "backwards", "both", "none, none", "forwards, backwards", "forwards, none", "none, both" ],
+		invalid_values: [ "all"]
+	},
+	"-moz-animation-iteration-count": {
+		domProp: "MozAnimationIterationCount",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "1" ],
+		other_values: [ "infinite", "0", "0.5", "7.75", "-0.0", "1, 2, 3", "infinite, 2", "1, infinite" ],
+		// negatives forbidden per
+		// http://lists.w3.org/Archives/Public/www-style/2011Mar/0355.html
+		invalid_values: [ "none", "-1", "-0.5", "-1, infinite", "infinite, -3" ]
+	},
+	"-moz-animation-name": {
+		domProp: "MozAnimationName",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "none" ],
+		other_values: [ "all", "ball", "mall", "color", "bounce, bubble, opacity", "foobar", "auto", "\\32bounce", "-bounce", "-\\32bounce", "\\32 0bounce", "-\\32 0bounce", "\\2bounce", "-\\2bounce" ],
+		invalid_values: [ "bounce, initial", "initial, bounce", "bounce, inherit", "inherit, bounce" ]
+	},
+	"-moz-animation-play-state": {
+		domProp: "MozAnimationPlayState",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "running" ],
+		other_values: [ "paused", "running, running", "paused, running", "paused, paused", "running, paused", "paused, running, running, running, paused, running" ],
+		invalid_values: [ "0" ]
+	},
+	"-moz-animation-timing-function": {
+		domProp: "MozAnimationTimingFunction",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "ease", "cubic-bezier(0.25, 0.1, 0.25, 1.0)" ],
+		other_values: [ "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
+		invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)" ]
+	},
 	"-moz-appearance": {
 		domProp: "MozAppearance",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none" ],
 		other_values: [ "radio", "menulist" ],
 		invalid_values: []
 	},
@@ -2603,17 +2678,17 @@ var gCSSProperties = {
 		],
 		invalid_values: []
 	},
 	"-moz-transition": {
 		domProp: "MozTransition",
 		inherited: false,
 		type: CSS_TYPE_TRUE_SHORTHAND,
 		subproperties: [ "-moz-transition-property", "-moz-transition-duration", "-moz-transition-timing-function", "-moz-transition-delay" ],
-		initial_values: [ "all 0s ease 0s" ],
+		initial_values: [ "all 0s ease 0s", "all", "0s", "0s 0s", "ease" ],
 		other_values: [ "width 1s linear 2s", "width 1s 2s linear", "width linear 1s 2s", "linear width 1s 2s", "linear 1s width 2s", "linear 1s 2s width", "1s width linear 2s", "1s width 2s linear", "1s 2s width linear", "1s linear width 2s", "1s linear 2s width", "1s 2s linear width", "width linear 1s", "width 1s linear", "linear width 1s", "linear 1s width", "1s width linear", "1s linear width", "1s 2s width", "1s width 2s", "width 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "width 1s", "1s width", "linear 1s", "1s linear", "1s 2s", "2s 1s", "width", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s width, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32width linear 2s", "1s -width linear 2s", "1s -\\32width linear 2s", "1s \\32 0width linear 2s", "1s -\\32 0width linear 2s", "1s \\2width linear 2s", "1s -\\2width linear 2s" ],
 		invalid_values: [ "2s, 1s width", "1s width, 2s", "2s all, 1s width", "1s width, 2s all", "1s width, 2s none", "2s none, 1s width", "2s inherit", "inherit 2s", "2s width, 1s inherit", "2s inherit, 1s width", "2s initial", "2s all, 1s width", "2s width, 1s all" ]
 	},
 	"-moz-transition-delay": {
 		domProp: "MozTransitionDelay",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "0s", "0ms" ],
--- a/layout/style/test/test_garbage_at_end_of_declarations.html
+++ b/layout/style/test/test_garbage_at_end_of_declarations.html
@@ -51,22 +51,32 @@ var gAllowsExtra = {
   "font": { "caption": true, "icon": true, "menu": true, "message-box": true,
             "small-caption": true, "status-bar": true },
   "voice-family": {},
 };
 
 /* These are the reverse of the above list; they're the unusual values
    that do allow extra keywords afterwards */
 var gAllowsExtraUnusual = {
-  "-moz-transition": { "1s 2s linear": true, "1s linear 2s": true,
+  "-moz-transition": { "all": true, "0s": true, "0s 0s": true, "ease": true,
+                       "1s 2s linear": true, "1s linear 2s": true,
                        "linear 1s 2s": true, "linear 1s": true,
                        "1s linear": true, "1s 2s": true, "2s 1s": true,
                        "linear": true, "1s": true, "2s": true,
                        "ease-in-out": true, "2s ease-in": true,
                        "ease-out 2s": true },
+  "-moz-animation": { "none": true, "0s": true, "ease": true,
+                      "normal": true, "running": true, "1.0": true,
+                      "1s 2s linear": true, "1s linear 2s": true,
+                      "linear 1s 2s": true, "linear 1s": true,
+                      "1s linear": true, "1s 2s": true, "2s 1s": true,
+                      "linear": true, "1s": true, "2s": true,
+                      "ease-in-out": true, "2s ease-in": true,
+                      "ease-out 2s": true, "1s bounce, 2s": true,
+                      "1s bounce, 2s none": true },
 };
 
 function test_property(property)
 {
   var info = gCSSProperties[property];
 
   function test_value(value) {
     if (property in gAllowsExtra &&
--- a/layout/style/test/test_transitions_computed_values.html
+++ b/layout/style/test/test_transitions_computed_values.html
@@ -62,12 +62,53 @@ is(cs.MozTransitionDuration, "5s, 4s",
    "computed style match with longer property");
 is(cs.MozTransitionProperty,
    "margin-left, margin-right, margin-top, margin-bottom",
    "longer property computed correctly");
 p.style.MozTransitionDuration = "";
 c.style.MozTransitionDuration = "";
 c.style.MozTransitionProperty = "";
 
+// And do the same pair of tests for animations:
+
+p.style.MozAnimationName = "bounce, roll";
+c.style.MozAnimationName = "inherit";
+is(cs.MozAnimationName, "bounce, roll",
+   "computed style match with no other properties");
+c.style.MozAnimationDuration = "5s";
+is(cs.MozAnimationName, "bounce, roll",
+   "computed style match with shorter property");
+is(cs.MozAnimationDuration, "5s",
+   "shorter property not extended");
+c.style.MozAnimationDuration = "5s, 4s, 3s, 2000ms";
+is(cs.MozAnimationName, "bounce, roll",
+   "computed style match with longer property");
+is(cs.MozAnimationDuration, "5s, 4s, 3s, 2s",
+   "longer property computed correctly");
+p.style.MozAnimationName = "";
+c.style.MozAnimationName = "";
+c.style.MozAnimationDuration = "";
+
+// and repeat the above set of tests with name and duration swapped
+p.style.MozAnimationDuration = "5s, 4s";
+c.style.MozAnimationDuration = "inherit";
+is(cs.MozAnimationDuration, "5s, 4s",
+   "computed style match with no other properties");
+c.style.MozAnimationName = "bounce";
+is(cs.MozAnimationDuration, "5s, 4s",
+   "computed style match with shorter property");
+is(cs.MozAnimationName, "bounce",
+   "shorter property not extended");
+c.style.MozAnimationName =
+  "bounce, roll, wiggle, spin";
+is(cs.MozAnimationDuration, "5s, 4s",
+   "computed style match with longer property");
+is(cs.MozAnimationName,
+   "bounce, roll, wiggle, spin",
+   "longer property computed correctly");
+p.style.MozAnimationDuration = "";
+c.style.MozAnimationDuration = "";
+c.style.MozAnimationName = "";
+
 </script>
 </pre>
 </body>
 </html>