Bug 1277433 - Part 1: Use discrete animation for appropriate CSS Alignment properties. r=dholbert,heycam
authorDaisuke Akatsuka <daisuke@mozilla-japan.org>
Tue, 16 Aug 2016 14:29:21 +0900
changeset 309674 f77b7188d3b229e9bb68c28e3f429ec4276414d1
parent 309673 714f26001d3e3e47190f3c91f62dbe9137418811
child 309675 6fb73a2de6e8ff5d1dbf66d79617c66026ced148
push id80665
push userkwierso@gmail.com
push dateWed, 17 Aug 2016 05:07:09 +0000
treeherdermozilla-inbound@5aa02060f7c4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert, heycam
bugs1277433
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1277433 - Part 1: Use discrete animation for appropriate CSS Alignment properties. r=dholbert,heycam MozReview-Commit-ID: BHtzuwJOuB5
dom/animation/AnimValuesStyleRule.cpp
dom/animation/AnimValuesStyleRule.h
dom/base/nsMappedAttributes.cpp
dom/base/nsMappedAttributes.h
dom/html/HTMLBodyElement.cpp
dom/html/HTMLBodyElement.h
layout/style/Declaration.cpp
layout/style/Declaration.h
layout/style/StyleAnimationValue.cpp
layout/style/StyleAnimationValue.h
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.h
layout/style/nsHTMLStyleSheet.cpp
layout/style/nsHTMLStyleSheet.h
layout/style/nsIStyleRule.h
layout/style/nsRuleNode.cpp
layout/style/nsRuleNode.h
layout/style/nsStyleSet.cpp
layout/style/nsStyleSet.h
layout/style/nsTransitionManager.cpp
--- a/dom/animation/AnimValuesStyleRule.cpp
+++ b/dom/animation/AnimValuesStyleRule.cpp
@@ -51,16 +51,24 @@ AnimValuesStyleRule::MapRuleInfoInto(nsR
 }
 
 bool
 AnimValuesStyleRule::MightMapInheritedStyleData()
 {
   return mStyleBits & NS_STYLE_INHERITED_STRUCT_MASK;
 }
 
+bool
+AnimValuesStyleRule::GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                                   nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
+
 #ifdef DEBUG
 void
 AnimValuesStyleRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString str;
   for (int32_t index = aIndent; --index >= 0; ) {
     str.AppendLiteral("  ");
   }
--- a/dom/animation/AnimValuesStyleRule.h
+++ b/dom/animation/AnimValuesStyleRule.h
@@ -27,16 +27,18 @@ public:
     : mStyleBits(0) {}
 
   // nsISupports implementation
   NS_DECL_ISUPPORTS
 
   // nsIStyleRule implementation
   void MapRuleInfoInto(nsRuleData* aRuleData) override;
   bool MightMapInheritedStyleData() override;
+  bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                     nsCSSValue* aValue) override;
 #ifdef DEBUG
   void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
   void AddValue(nsCSSProperty aProperty, const StyleAnimationValue &aStartValue)
   {
     PropertyStyleAnimationValuePair pair = { aProperty, aStartValue };
     mPropertyValuePairs.AppendElement(pair);
--- a/dom/base/nsMappedAttributes.cpp
+++ b/dom/base/nsMappedAttributes.cpp
@@ -187,16 +187,24 @@ nsMappedAttributes::MapRuleInfoInto(nsRu
 /* virtual */ bool
 nsMappedAttributes::MightMapInheritedStyleData()
 {
   // Just assume that we do, rather than adding checks to all of the different
   // kinds of attribute mapping functions we have.
   return true;
 }
 
+/* virtual */ bool
+nsMappedAttributes::GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                                  nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
+
 #ifdef DEBUG
 /* virtual */ void
 nsMappedAttributes::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString str;
   nsAutoString tmp;
   uint32_t i;
 
--- a/dom/base/nsMappedAttributes.h
+++ b/dom/base/nsMappedAttributes.h
@@ -70,16 +70,18 @@ public:
   void RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue);
   const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const;
   int32_t IndexOfAttr(nsIAtom* aLocalName) const;
   
 
   // nsIStyleRule 
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
   virtual bool MightMapInheritedStyleData() override;
+  virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                             nsCSSValue* aValue) override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 private:
   nsMappedAttributes(const nsMappedAttributes& aCopy);
--- a/dom/html/HTMLBodyElement.cpp
+++ b/dom/html/HTMLBodyElement.cpp
@@ -169,16 +169,24 @@ BodyRule::MapRuleInfoInto(nsRuleData* aD
 }
 
 /* virtual */ bool
 BodyRule::MightMapInheritedStyleData()
 {
   return false;
 }
 
+/* virtual */ bool
+BodyRule::GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                        nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
+
 #ifdef DEBUG
 /* virtual */ void
 BodyRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString indent;
   for (int32_t index = aIndent; --index >= 0; ) {
     indent.AppendLiteral("  ");
   }
--- a/dom/html/HTMLBodyElement.h
+++ b/dom/html/HTMLBodyElement.h
@@ -24,16 +24,18 @@ class BodyRule: public nsIStyleRule
 public:
   explicit BodyRule(HTMLBodyElement* aPart);
 
   NS_DECL_ISUPPORTS
 
   // nsIStyleRule interface
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
   virtual bool MightMapInheritedStyleData() override;
+  virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                             nsCSSValue* aValue) override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
   HTMLBodyElement*  mPart;  // not ref-counted, cleared by content 
 };
 
 class HTMLBodyElement final : public nsGenericHTMLElement,
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -30,16 +30,24 @@ ImportantStyleData::MapRuleInfoInto(nsRu
 }
 
 /* virtual */ bool
 ImportantStyleData::MightMapInheritedStyleData()
 {
   return Declaration()->MapsImportantInheritedStyleData();
 }
 
+/* virtual */ bool
+ImportantStyleData::GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                                  nsCSSValue* aValue)
+{
+  return Declaration()->GetDiscretelyAnimatedCSSValue(aProperty, aValue);
+}
+
+
 #ifdef DEBUG
 /* virtual */ void
 ImportantStyleData::List(FILE* out, int32_t aIndent) const
 {
   // Indent
   nsAutoCString str;
   for (int32_t index = aIndent; --index >= 0; ) {
     str.AppendLiteral("  ");
@@ -106,16 +114,31 @@ Declaration::MightMapInheritedStyleData(
 {
   MOZ_ASSERT(mData, "must call only while compressed");
   if (mVariables && mVariables->Count() != 0) {
     return true;
   }
   return mData->HasInheritedStyleData();
 }
 
+/* virtual */ bool
+Declaration::GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                           nsCSSValue* aValue)
+{
+  nsCSSCompressedDataBlock* data = GetValueIsImportant(aProperty)
+                                   ? mImportantData : mData;
+  const nsCSSValue* value = data->ValueFor(aProperty);
+  if (!value) {
+    return false;
+  }
+  *aValue = *value;
+  return true;
+}
+
+
 bool
 Declaration::MapsImportantInheritedStyleData() const
 {
   MOZ_ASSERT(mData, "must call only while compressed");
   MOZ_ASSERT(HasImportantData(), "must only be called for Declarations with "
                                  "important data");
   if (mImportantVariables && mImportantVariables->Count() != 0) {
     return true;
--- a/layout/style/Declaration.h
+++ b/layout/style/Declaration.h
@@ -55,16 +55,18 @@ public:
 
   NS_DECL_ISUPPORTS
 
   inline ::mozilla::css::Declaration* Declaration();
 
   // nsIStyleRule interface
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
   virtual bool MightMapInheritedStyleData() override;
+  virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                             nsCSSValue* aValue) override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
 private:
   ImportantStyleData() {}
   ~ImportantStyleData() {}
 
@@ -98,16 +100,18 @@ public:
 private:
   ~Declaration();
 
 public:
 
   // nsIStyleRule implementation
   virtual void MapRuleInfoInto(nsRuleData *aRuleData) override;
   virtual bool MightMapInheritedStyleData() override;
+  virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                             nsCSSValue* aValue) override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
   /**
    * |ValueAppended| must be called to maintain this declaration's
    * |mOrder| whenever a property is parsed into an expanded data block
    * for this declaration.  aProperty must not be a shorthand.
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -494,16 +494,17 @@ StyleAnimationValue::ComputeDistance(nsC
   switch (commonUnit) {
     case eUnit_Null:
     case eUnit_Auto:
     case eUnit_None:
     case eUnit_Normal:
     case eUnit_UnparsedString:
     case eUnit_URL:
     case eUnit_CurrentColor:
+    case eUnit_DiscreteCSSValue:
       return false;
 
     case eUnit_Enumerated:
       switch (aProperty) {
         case eCSSProperty_font_stretch: {
           // just like eUnit_Integer.
           int32_t startInt = aStartValue.GetIntValue();
           int32_t endInt = aEndValue.GetIntValue();
@@ -2264,16 +2265,17 @@ StyleAnimationValue::AddWeighted(nsCSSPr
   switch (commonUnit) {
     case eUnit_Null:
     case eUnit_Auto:
     case eUnit_None:
     case eUnit_Normal:
     case eUnit_UnparsedString:
     case eUnit_URL:
     case eUnit_CurrentColor:
+    case eUnit_DiscreteCSSValue:
       return false;
 
     case eUnit_Enumerated:
       switch (aProperty) {
         case eCSSProperty_font_stretch: {
           // Animate just like eUnit_Integer.
           int32_t result = floor(aCoeff1 * double(aValue1.GetIntValue()) +
                                  aCoeff2 * double(aValue2.GetIntValue()));
@@ -3056,24 +3058,26 @@ StyleAnimationValue::UncomputeValue(nsCS
       // colors can be alone, or part of a paint server
       aSpecifiedValue.SetColorValue(aComputedValue.GetColorValue());
       break;
     case eUnit_CurrentColor:
       aSpecifiedValue.SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
       break;
     case eUnit_Calc:
     case eUnit_ObjectPosition:
-    case eUnit_URL: {
+    case eUnit_URL:
+    case eUnit_DiscreteCSSValue: {
       nsCSSValue* val = aComputedValue.GetCSSValueValue();
       // Sanity-check that the underlying unit in the nsCSSValue is what we
       // expect for our StyleAnimationValue::Unit:
       MOZ_ASSERT((unit == eUnit_Calc && val->GetUnit() == eCSSUnit_Calc) ||
                  (unit == eUnit_ObjectPosition &&
                   val->GetUnit() == eCSSUnit_Array) ||
-                 (unit == eUnit_URL && val->GetUnit() == eCSSUnit_URL),
+                 (unit == eUnit_URL && val->GetUnit() == eCSSUnit_URL) ||
+                 unit == eUnit_DiscreteCSSValue,
                  "unexpected unit");
       aSpecifiedValue = *val;
       break;
     }
     case eUnit_CSSValuePair: {
       // Rule node processing expects pair values to be collapsed to a
       // single value if both halves would be equal, for most but not
       // all properties.  At present, all animatable properties that
@@ -3592,18 +3596,21 @@ StyleAnimationValue::ExtractComputedValu
                                           StyleAnimationValue& aComputedValue)
 {
   MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands,
              "bad property");
   const void* styleStruct =
     aStyleContext->StyleData(nsCSSProps::kSIDTable[aProperty]);
   ptrdiff_t ssOffset = nsCSSProps::kStyleStructOffsetTable[aProperty];
   nsStyleAnimType animType = nsCSSProps::kAnimTypeTable[aProperty];
-  MOZ_ASSERT(0 <= ssOffset || animType == eStyleAnimType_Custom,
-             "must be dealing with animatable property");
+  MOZ_ASSERT(0 <= ssOffset ||
+             animType == eStyleAnimType_Custom ||
+             animType == eStyleAnimType_Discrete,
+             "all animation types other than Custom and Discrete must " \
+             "specify a style struct offset to extract values from");
   switch (animType) {
     case eStyleAnimType_Custom:
       switch (aProperty) {
         // For border-width, ignore the border-image business (which
         // only exists until we update our implementation to the current
         // spec) and use GetComputedBorder
 
         #define BORDER_WIDTH_CASE(prop_, side_)                               \
@@ -4208,16 +4215,24 @@ StyleAnimationValue::ExtractComputedValu
       nsCSSValueList **resultTail = getter_Transfers(result);
       for (uint32_t i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
         AppendCSSShadowValue(shadowArray->ShadowAt(i), resultTail);
       }
       aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
                                                   eUnit_Shadow);
       return true;
     }
+    case eStyleAnimType_Discrete: {
+      auto cssValue = MakeUnique<nsCSSValue>(eCSSUnit_Unset);
+      aStyleContext->RuleNode()->GetDiscretelyAnimatedCSSValue(aProperty,
+                                                               cssValue.get());
+      aComputedValue.SetAndAdoptCSSValueValue(cssValue.release(),
+                                              eUnit_DiscreteCSSValue);
+      return true;
+    }
     case eStyleAnimType_None:
       NS_NOTREACHED("shouldn't use on non-animatable properties");
   }
   return false;
 }
 
 gfxSize
 StyleAnimationValue::GetScaleValue(const nsIFrame* aForFrame) const
@@ -4313,16 +4328,17 @@ StyleAnimationValue::operator=(const Sty
       MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
       break;
     case eUnit_Color:
       mValue.mColor = aOther.mValue.mColor;
       break;
     case eUnit_Calc:
     case eUnit_ObjectPosition:
     case eUnit_URL:
+    case eUnit_DiscreteCSSValue:
       MOZ_ASSERT(IsCSSValueUnit(mUnit),
                  "This clause is for handling nsCSSValue-backed units");
       MOZ_ASSERT(aOther.mValue.mCSSValue, "values may not be null");
       mValue.mCSSValue = new nsCSSValue(*aOther.mValue.mCSSValue);
       break;
     case eUnit_CSSValuePair:
       MOZ_ASSERT(aOther.mValue.mCSSValuePair,
                  "value pairs may not be null");
@@ -4590,16 +4606,17 @@ StyleAnimationValue::operator==(const St
     case eUnit_Percent:
     case eUnit_Float:
       return mValue.mFloat == aOther.mValue.mFloat;
     case eUnit_Color:
       return mValue.mColor == aOther.mValue.mColor;
     case eUnit_Calc:
     case eUnit_ObjectPosition:
     case eUnit_URL:
+    case eUnit_DiscreteCSSValue:
       MOZ_ASSERT(IsCSSValueUnit(mUnit),
                  "This clause is for handling nsCSSValue-backed units");
       return *mValue.mCSSValue == *aOther.mValue.mCSSValue;
     case eUnit_CSSValuePair:
       return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
     case eUnit_CSSValueTriplet:
       return *mValue.mCSSValueTriplet == *aOther.mValue.mCSSValueTriplet;
     case eUnit_CSSRect:
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -272,16 +272,17 @@ public:
     eUnit_Float,
     eUnit_Color,
     eUnit_CurrentColor,
     eUnit_Calc, // nsCSSValue* (never null), always with a single
                 // calc() expression that's either length or length+percent
     eUnit_ObjectPosition, // nsCSSValue* (never null), always with a
                           // 4-entry nsCSSValue::Array
     eUnit_URL, // nsCSSValue* (never null), always with a css::URLValue
+    eUnit_DiscreteCSSValue, // nsCSSValue* (never null)
     eUnit_CSSValuePair, // nsCSSValuePair* (never null)
     eUnit_CSSValueTriplet, // nsCSSValueTriplet* (never null)
     eUnit_CSSRect, // nsCSSRect* (never null)
     eUnit_Dasharray, // nsCSSValueList* (never null)
     eUnit_Shadow, // nsCSSValueList* (may be null)
     eUnit_Shape,  // nsCSSValue::Array* (never null)
     eUnit_Filter, // nsCSSValueList* (may be null)
     eUnit_Transform, // nsCSSValueList* (never null)
@@ -472,17 +473,18 @@ private:
 
   static bool IsIntUnit(Unit aUnit) {
     return aUnit == eUnit_Enumerated || aUnit == eUnit_Visibility ||
            aUnit == eUnit_Integer;
   }
   static bool IsCSSValueUnit(Unit aUnit) {
     return aUnit == eUnit_Calc ||
            aUnit == eUnit_ObjectPosition ||
-           aUnit == eUnit_URL;
+           aUnit == eUnit_URL ||
+           aUnit == eUnit_DiscreteCSSValue;
   }
   static bool IsCSSValuePairUnit(Unit aUnit) {
     return aUnit == eUnit_CSSValuePair;
   }
   static bool IsCSSValueTripletUnit(Unit aUnit) {
     return aUnit == eUnit_CSSValueTriplet;
   }
   static bool IsCSSRectUnit(Unit aUnit) {
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -329,37 +329,37 @@ CSS_PROP_POSITION(
     align-content,
     align_content,
     AlignContent,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
     kAutoCompletionAlignJustifyContent,
     CSS_PROP_NO_OFFSET,
-    eStyleAnimType_None)
+    eStyleAnimType_Discrete)
 CSS_PROP_POSITION(
     align-items,
     align_items,
     AlignItems,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
     kAutoCompletionAlignItems,
     CSS_PROP_NO_OFFSET,
-    eStyleAnimType_None)
+    eStyleAnimType_Discrete)
 CSS_PROP_POSITION(
     align-self,
     align_self,
     AlignSelf,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
     kAutoCompletionAlignJustifySelf,
     CSS_PROP_NO_OFFSET,
-    eStyleAnimType_None)
+    eStyleAnimType_Discrete)
 CSS_PROP_SHORTHAND(
     all,
     all,
     All,
     CSS_PROPERTY_PARSE_FUNCTION,
     "layout.css.all-shorthand.enabled")
 CSS_PROP_SHORTHAND(
     animation,
@@ -2353,38 +2353,38 @@ CSS_PROP_POSITION(
     justify-content,
     justify_content,
     JustifyContent,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
     kAutoCompletionAlignJustifyContent,
     CSS_PROP_NO_OFFSET,
-    eStyleAnimType_None)
+    eStyleAnimType_Discrete)
 CSS_PROP_POSITION(
     justify-items,
     justify_items,
     JustifyItems,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
     // for auto-completion we use same values as justify-self:
     kAutoCompletionAlignJustifySelf,
     CSS_PROP_NO_OFFSET,
-    eStyleAnimType_None)
+    eStyleAnimType_Discrete)
 CSS_PROP_POSITION(
     justify-self,
     justify_self,
     JustifySelf,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
     kAutoCompletionAlignJustifySelf,
     CSS_PROP_NO_OFFSET,
-    eStyleAnimType_None)
+    eStyleAnimType_Discrete)
 #ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_FONT(
     -x-lang,
     _x_lang,
     Lang,
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_INACCESSIBLE,
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -321,16 +321,19 @@ enum nsStyleAnimType {
   eStyleAnimType_Color,
 
   // nsStyleSVGPaint values
   eStyleAnimType_PaintServer,
 
   // RefPtr<nsCSSShadowArray> values
   eStyleAnimType_Shadow,
 
+  // discrete values
+  eStyleAnimType_Discrete,
+
   // property not animatable
   eStyleAnimType_None
 };
 
 namespace mozilla {
 
 // Type trait that determines whether the integral or enum type Type can fit
 // within the integral type Storage without loss.
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -53,16 +53,24 @@ nsHTMLStyleSheet::HTMLColorRule::MapRule
 }
 
 /* virtual */ bool
 nsHTMLStyleSheet::HTMLColorRule::MightMapInheritedStyleData()
 {
   return true;
 }
 
+/* virtual */ bool
+nsHTMLStyleSheet::HTMLColorRule::
+GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty, nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
+
 #ifdef DEBUG
 /* virtual */ void
 nsHTMLStyleSheet::HTMLColorRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString indentStr;
   for (int32_t index = aIndent; --index >= 0; ) {
     indentStr.AppendLiteral("  ");
   }
@@ -98,16 +106,24 @@ nsHTMLStyleSheet::TableTHRule::MapRuleIn
 }
 
 /* virtual */ bool
 nsHTMLStyleSheet::TableTHRule::MightMapInheritedStyleData()
 {
   return true;
 }
 
+/* virtual */ bool
+nsHTMLStyleSheet::TableTHRule::
+GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty, nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
+
 /* virtual */ void
 nsHTMLStyleSheet::TableQuirkColorRule::MapRuleInfoInto(nsRuleData* aRuleData)
 {
   if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Color)) {
     nsCSSValue* color = aRuleData->ValueForColor();
     // We do not check UseDocumentColors() here, because we want to
     // use the body color no matter what.
     if (color->GetUnit() == eCSSUnit_Null)
@@ -117,16 +133,23 @@ nsHTMLStyleSheet::TableQuirkColorRule::M
 }
 
 /* virtual */ bool
 nsHTMLStyleSheet::TableQuirkColorRule::MightMapInheritedStyleData()
 {
   return true;
 }
 
+/* virtual */ bool
+nsHTMLStyleSheet::TableQuirkColorRule::
+GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty, nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
 
 NS_IMPL_ISUPPORTS(nsHTMLStyleSheet::LangRule, nsIStyleRule)
 
 /* virtual */ void
 nsHTMLStyleSheet::LangRule::MapRuleInfoInto(nsRuleData* aRuleData)
 {
   if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) {
     nsCSSValue* lang = aRuleData->ValueForLang();
@@ -137,16 +160,24 @@ nsHTMLStyleSheet::LangRule::MapRuleInfoI
 }
 
 /* virtual */ bool
 nsHTMLStyleSheet::LangRule::MightMapInheritedStyleData()
 {
   return true;
 }
 
+/* virtual */ bool
+nsHTMLStyleSheet::LangRule::
+GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty, nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
+
 #ifdef DEBUG
 /* virtual */ void
 nsHTMLStyleSheet::LangRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString str;
   for (int32_t index = aIndent; --index >= 0; ) {
     str.AppendLiteral("  ");
   }
--- a/layout/style/nsHTMLStyleSheet.h
+++ b/layout/style/nsHTMLStyleSheet.h
@@ -79,16 +79,18 @@ private:
   public:
     HTMLColorRule() {}
 
     NS_DECL_ISUPPORTS
 
     // nsIStyleRule interface
     virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
     virtual bool MightMapInheritedStyleData() override;
+    virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                               nsCSSValue* aValue) override;
   #ifdef DEBUG
     virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
   #endif
 
     nscolor             mColor;
   };
 
   // Implementation of SetLink/VisitedLink/ActiveLinkColor
@@ -102,39 +104,45 @@ private:
   public:
     GenericTableRule() {}
 
     NS_DECL_ISUPPORTS
 
     // nsIStyleRule interface
     virtual void MapRuleInfoInto(nsRuleData* aRuleData) override = 0;
     virtual bool MightMapInheritedStyleData() override = 0;
+    virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                               nsCSSValue* aValue) override = 0;
   #ifdef DEBUG
     virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
   #endif
   };
 
   // this rule handles <th> inheritance
   class TableTHRule;
   friend class TableTHRule;
   class TableTHRule final : public GenericTableRule {
   public:
     TableTHRule() {}
 
     virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
     virtual bool MightMapInheritedStyleData() override;
+    virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                               nsCSSValue* aValue) override;
   };
 
   // Rule to handle quirk table colors
   class TableQuirkColorRule final : public GenericTableRule {
   public:
     TableQuirkColorRule() {}
 
     virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
     virtual bool MightMapInheritedStyleData() override;
+    virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                               nsCSSValue* aValue) override;
   };
 
 public: // for mLangRuleTable structures only
 
   // Rule to handle xml:lang attributes, of which we have exactly one
   // per language string, maintained in mLangRuleTable.
   // We also create one extra rule for the "x-math" language string, used on
   // <math> elements.
@@ -144,16 +152,18 @@ public: // for mLangRuleTable structures
   public:
     explicit LangRule(const nsSubstring& aLang) : mLang(aLang) {}
 
     NS_DECL_ISUPPORTS
 
     // nsIStyleRule interface
     virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
     virtual bool MightMapInheritedStyleData() override;
+    virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                               nsCSSValue* aValue) override;
   #ifdef DEBUG
     virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
   #endif
 
     nsString mLang;
   };
 
 private:
--- a/layout/style/nsIStyleRule.h
+++ b/layout/style/nsIStyleRule.h
@@ -8,18 +8,20 @@
  * information
  */
 
 #ifndef nsIStyleRule_h___
 #define nsIStyleRule_h___
 
 #include <stdio.h>
 
+#include "nsCSSProperty.h"
 #include "nsISupports.h"
 
+class nsCSSValue;
 struct nsRuleData;
 
 // IID for the nsIStyleRule interface {f75f3f70-435d-43a6-a01b-65970489ca26}
 #define NS_ISTYLE_RULE_IID     \
 { 0xf75f3f70, 0x435d, 0x43a6, \
  { 0xa0, 0x1b, 0x65, 0x97, 0x04, 0x89, 0xca, 0x26 } }
 
 /**
@@ -72,16 +74,23 @@ public:
   virtual void MapRuleInfoInto(nsRuleData* aRuleData)=0;
 
   /**
    * Returns whether this style rule has any style data for inherited
    * properties.
    */
   virtual bool MightMapInheritedStyleData() = 0;
 
+  /**
+   * Gets and sets given aProperty's value to aValue.
+   * Returns true, if aValue is filled in this rule.
+   */
+  virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                             nsCSSValue* aValue) = 0;
+
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0;
 #endif
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIStyleRule, NS_ISTYLE_RULE_IID)
 
 #endif /* nsIStyleRule_h___ */
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -10359,16 +10359,31 @@ nsRuleNode::GetStyleData(nsStyleStructID
 
   // Nothing is cached.  We'll have to delve further and examine our rules.
   data = WalkRuleTree(aSID, aContext);
 
   MOZ_ASSERT(data, "should have aborted on out-of-memory");
   return data;
 }
 
+void
+nsRuleNode::GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                          nsCSSValue* aValue)
+{
+  for (nsRuleNode* node = this; node; node = node->GetParent()) {
+    nsIStyleRule* rule = node->GetRule();
+    if (!rule) {
+      continue;
+    }
+    if (rule->GetDiscretelyAnimatedCSSValue(aProperty, aValue)) {
+      return;
+    }
+  }
+}
+
 /* static */ bool
 nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext,
                                     uint32_t ruleTypeMask,
                                     bool aAuthorColorsAllowed)
 {
   uint32_t inheritBits = 0;
   if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND)
     inheritBits |= NS_STYLE_INHERIT_BIT(Background);
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -856,16 +856,18 @@ public:
   nsIStyleRule* GetRule() const { return mRule; }
   // NOTE: Does not |AddRef|.  Never null.
   nsPresContext* PresContext() const { return mPresContext; }
 
   const void* GetStyleData(nsStyleStructID aSID,
                            nsStyleContext* aContext,
                            bool aComputeData);
 
+  void GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                     nsCSSValue* aValue);
 
   // See comments in GetStyleData for an explanation of what the
   // code below does.
   #define STYLE_STRUCT_INHERITED(name_, checkdata_cb_)                        \
   template<bool aComputeData>                                                 \
   const nsStyle##name_*                                                       \
   GetStyle##name_(nsStyleContext* aContext, uint64_t& aContextStyleBits)      \
   {                                                                           \
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -57,16 +57,24 @@ nsEmptyStyleRule::MapRuleInfoInto(nsRule
 }
 
 /* virtual */ bool
 nsEmptyStyleRule::MightMapInheritedStyleData()
 {
   return false;
 }
 
+/* virtual */ bool
+nsEmptyStyleRule::GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                                nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
+
 #ifdef DEBUG
 /* virtual */ void
 nsEmptyStyleRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString indentStr;
   for (int32_t index = aIndent; --index >= 0; ) {
     indentStr.AppendLiteral("  ");
   }
@@ -116,16 +124,24 @@ nsInitialStyleRule::MapRuleInfoInto(nsRu
 }
 
 /* virtual */ bool
 nsInitialStyleRule::MightMapInheritedStyleData()
 {
   return true;
 }
 
+/* virtual */ bool
+nsInitialStyleRule::GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                                  nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
+
 #ifdef DEBUG
 /* virtual */ void
 nsInitialStyleRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString indentStr;
   for (int32_t index = aIndent; --index >= 0; ) {
     indentStr.AppendLiteral("  ");
   }
@@ -147,16 +163,24 @@ nsDisableTextZoomStyleRule::MapRuleInfoI
 }
 
 /* virtual */ bool
 nsDisableTextZoomStyleRule::MightMapInheritedStyleData()
 {
   return true;
 }
 
+/* virtual */ bool
+nsDisableTextZoomStyleRule::
+GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty, nsCSSValue* aValue)
+{
+  MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
+  return false;
+}
+
 #ifdef DEBUG
 /* virtual */ void
 nsDisableTextZoomStyleRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString indentStr;
   for (int32_t index = aIndent; --index >= 0; ) {
     indentStr.AppendLiteral("  ");
   }
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -47,44 +47,50 @@ class nsEmptyStyleRule final : public ns
 {
 private:
   ~nsEmptyStyleRule() {}
 
 public:
   NS_DECL_ISUPPORTS
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
   virtual bool MightMapInheritedStyleData() override;
+  virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                             nsCSSValue* aValue) override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 };
 
 class nsInitialStyleRule final : public nsIStyleRule
 {
 private:
   ~nsInitialStyleRule() {}
 
 public:
   NS_DECL_ISUPPORTS
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
   virtual bool MightMapInheritedStyleData() override;
+  virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                             nsCSSValue* aValue) override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 };
 
 class nsDisableTextZoomStyleRule final : public nsIStyleRule
 {
 private:
   ~nsDisableTextZoomStyleRule() {}
 
 public:
   NS_DECL_ISUPPORTS
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
   virtual bool MightMapInheritedStyleData() override;
+  virtual bool GetDiscretelyAnimatedCSSValue(nsCSSProperty aProperty,
+                                             nsCSSValue* aValue) override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 };
 
 // The style set object is created by the document viewer and ownership is
 // then handed off to the PresShell.  Only the PresShell should delete a
 // style set.
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -278,16 +278,27 @@ CSSTransition::GetCurrentTimeAt(const Do
 
 ////////////////////////// nsTransitionManager ////////////////////////////
 
 NS_IMPL_CYCLE_COLLECTION(nsTransitionManager, mEventDispatcher)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTransitionManager, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTransitionManager, Release)
 
+static inline bool
+ExtractNonDiscreteComputedValue(nsCSSProperty aProperty,
+                                nsStyleContext* aStyleContext,
+                                StyleAnimationValue& aComputedValue)
+{
+  return (nsCSSProps::kAnimTypeTable[aProperty] != eStyleAnimType_Discrete ||
+          aProperty == eCSSProperty_visibility) &&
+         StyleAnimationValue::ExtractComputedValue(aProperty, aStyleContext,
+                                                   aComputedValue);
+}
+
 void
 nsTransitionManager::StyleContextChanged(dom::Element *aElement,
                                          nsStyleContext *aOldStyleContext,
                                          RefPtr<nsStyleContext>* aNewStyleContext /* inout */)
 {
   nsStyleContext* newStyleContext = *aNewStyleContext;
 
   NS_PRECONDITION(aOldStyleContext->GetPseudo() == newStyleContext->GetPseudo(),
@@ -547,19 +558,18 @@ nsTransitionManager::UpdateTransitions(
           // properties no longer in 'transition-property'
       if ((checkProperties &&
            !allTransitionProperties.HasProperty(anim->TransitionProperty())) ||
           // properties whose computed values changed but for which we
           // did not start a new transition (because delay and
           // duration are both zero, or because the new value is not
           // interpolable); a new transition would have anim->ToValue()
           // matching currentValue
-          !StyleAnimationValue::ExtractComputedValue(anim->TransitionProperty(),
-                                                     aNewStyleContext,
-                                                     currentValue) ||
+          !ExtractNonDiscreteComputedValue(anim->TransitionProperty(),
+                                           aNewStyleContext, currentValue) ||
           currentValue != anim->ToValue()) {
         // stop the transition
         if (anim->HasCurrentEffect()) {
           EffectSet* effectSet =
             EffectSet::GetEffectSet(aElement,
                                     aNewStyleContext->GetPseudoType());
           if (effectSet) {
             effectSet->UpdateAnimationGeneration(mPresContext);
@@ -614,22 +624,18 @@ nsTransitionManager::ConsiderStartingTra
   if (nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) {
     return;
   }
 
   dom::DocumentTimeline* timeline = aElement->OwnerDoc()->Timeline();
 
   StyleAnimationValue startValue, endValue, dummyValue;
   bool haveValues =
-    StyleAnimationValue::ExtractComputedValue(aProperty,
-                                              aOldStyleContext,
-                                              startValue) &&
-    StyleAnimationValue::ExtractComputedValue(aProperty,
-                                              aNewStyleContext,
-                                              endValue);
+    ExtractNonDiscreteComputedValue(aProperty, aOldStyleContext, startValue) &&
+    ExtractNonDiscreteComputedValue(aProperty, aNewStyleContext, endValue);
 
   bool haveChange = startValue != endValue;
 
   bool shouldAnimate =
     haveValues &&
     haveChange &&
     // Check that we can interpolate between these values
     // (If this is ever a performance problem, we could add a
@@ -924,19 +930,18 @@ nsTransitionManager::PruneCompletedTrans
 
     if (anim->HasCurrentEffect()) {
       continue;
     }
 
     // Since effect is a finished transition, we know it didn't
     // influence style.
     StyleAnimationValue currentValue;
-    if (!StyleAnimationValue::ExtractComputedValue(anim->TransitionProperty(),
-                                                   aNewStyleContext,
-                                                   currentValue) ||
+    if (!ExtractNonDiscreteComputedValue(anim->TransitionProperty(),
+                                         aNewStyleContext, currentValue) ||
         currentValue != anim->ToValue()) {
       anim->CancelFromStyle();
       animations.RemoveElementAt(i);
     }
   } while (i != 0);
 
   if (collection->mAnimations.IsEmpty()) {
     collection->Destroy();