Bug 1277433 - Part 1: Use discrete animation for appropriate CSS Alignment properties. r=dholbert,r=heycam draft
authorDaisuke Akatsuka <daisuke@mozilla-japan.org>
Tue, 16 Aug 2016 14:29:21 +0900
changeset 401056 aa9da7cba389117e0151062ccdd141302529ce87
parent 400825 054d4856cea6150a6638e5daf7913713281af97d
child 401057 69b7ce95f3839a3dc7248329cdfbfa18912172c1
push id26349
push userbmo:daisuke@mozilla-japan.org
push dateTue, 16 Aug 2016 06:53:50 +0000
reviewersdholbert, heycam
bugs1277433
milestone51.0a1
Bug 1277433 - Part 1: Use discrete animation for appropriate CSS Alignment properties. r=dholbert,r=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
@@ -10356,16 +10356,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();