LCOV - code coverage report
Current view: top level - layout/style - StyleAnimationValue.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 204 2719 7.5 %
Date: 2017-07-14 16:53:18 Functions: 32 142 22.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* Utilities for animation of computed style values */
       8             : 
       9             : #include "mozilla/StyleAnimationValue.h"
      10             : 
      11             : #include "mozilla/ArrayUtils.h"
      12             : #include "mozilla/MathAlgorithms.h"
      13             : #include "mozilla/RuleNodeCacheConditions.h"
      14             : #include "mozilla/ServoBindings.h"
      15             : #include "mozilla/StyleSetHandle.h"
      16             : #include "mozilla/StyleSetHandleInlines.h"
      17             : #include "mozilla/Tuple.h"
      18             : #include "mozilla/UniquePtr.h"
      19             : #include "nsAutoPtr.h"
      20             : #include "nsCOMArray.h"
      21             : #include "nsIStyleRule.h"
      22             : #include "mozilla/css/StyleRule.h"
      23             : #include "nsString.h"
      24             : #include "nsStyleContext.h"
      25             : #include "nsStyleSet.h"
      26             : #include "nsComputedDOMStyle.h"
      27             : #include "nsContentUtils.h"
      28             : #include "nsCSSParser.h"
      29             : #include "nsCSSPseudoElements.h"
      30             : #include "mozilla/css/Declaration.h"
      31             : #include "mozilla/dom/Element.h"
      32             : #include "mozilla/FloatingPoint.h"
      33             : #include "mozilla/KeyframeUtils.h" // KeyframeUtils::ParseProperty
      34             : #include "mozilla/Likely.h"
      35             : #include "mozilla/ServoBindings.h" // RawServoDeclarationBlock
      36             : #include "gfxMatrix.h"
      37             : #include "gfxQuaternion.h"
      38             : #include "nsIDocument.h"
      39             : #include "nsIFrame.h"
      40             : #include "gfx2DGlue.h"
      41             : #include "nsStyleContextInlines.h"
      42             : 
      43             : using namespace mozilla;
      44             : using namespace mozilla::css;
      45             : using namespace mozilla::gfx;
      46             : using nsStyleTransformMatrix::Decompose2DMatrix;
      47             : using nsStyleTransformMatrix::Decompose3DMatrix;
      48             : using nsStyleTransformMatrix::ShearType;
      49             : 
      50             : // HELPER METHODS
      51             : // --------------
      52             : /*
      53             :  * Given two units, this method returns a common unit that they can both be
      54             :  * converted into, if possible.  This is intended to facilitate
      55             :  * interpolation, distance-computation, and addition between "similar" units.
      56             :  *
      57             :  * The ordering of the arguments should not affect the output of this method.
      58             :  *
      59             :  * If there's no sensible common unit, this method returns eUnit_Null.
      60             :  *
      61             :  * @param   aFirstUnit One unit to resolve.
      62             :  * @param   aFirstUnit The other unit to resolve.
      63             :  * @return  A "common" unit that both source units can be converted into, or
      64             :  *          eUnit_Null if that's not possible.
      65             :  */
      66             : static
      67             : StyleAnimationValue::Unit
      68          10 : GetCommonUnit(nsCSSPropertyID aProperty,
      69             :               StyleAnimationValue::Unit aFirstUnit,
      70             :               StyleAnimationValue::Unit aSecondUnit)
      71             : {
      72          10 :   if (aFirstUnit != aSecondUnit) {
      73           0 :     if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
      74           0 :         (aFirstUnit == StyleAnimationValue::eUnit_Coord ||
      75           0 :          aFirstUnit == StyleAnimationValue::eUnit_Percent ||
      76           0 :          aFirstUnit == StyleAnimationValue::eUnit_Calc) &&
      77           0 :         (aSecondUnit == StyleAnimationValue::eUnit_Coord ||
      78           0 :          aSecondUnit == StyleAnimationValue::eUnit_Percent ||
      79             :          aSecondUnit == StyleAnimationValue::eUnit_Calc)) {
      80             :       // We can use calc() as the common unit.
      81           0 :       return StyleAnimationValue::eUnit_Calc;
      82             :     }
      83           0 :     if ((aFirstUnit == StyleAnimationValue::eUnit_Color ||
      84           0 :          aFirstUnit == StyleAnimationValue::eUnit_CurrentColor ||
      85           0 :          aFirstUnit == StyleAnimationValue::eUnit_ComplexColor) &&
      86           0 :         (aSecondUnit == StyleAnimationValue::eUnit_Color ||
      87           0 :          aSecondUnit == StyleAnimationValue::eUnit_CurrentColor ||
      88             :          aSecondUnit == StyleAnimationValue::eUnit_ComplexColor)) {
      89             :       // We can use complex color as the common unit.
      90           0 :       return StyleAnimationValue::eUnit_ComplexColor;
      91             :     }
      92           0 :     return StyleAnimationValue::eUnit_Null;
      93             :   }
      94          10 :   return aFirstUnit;
      95             : }
      96             : 
      97             : static
      98             : nsCSSUnit
      99           0 : GetCommonUnit(nsCSSPropertyID aProperty,
     100             :               nsCSSUnit aFirstUnit,
     101             :               nsCSSUnit aSecondUnit)
     102             : {
     103           0 :   if (aFirstUnit != aSecondUnit) {
     104           0 :     if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
     105           0 :         (aFirstUnit == eCSSUnit_Pixel ||
     106           0 :          aFirstUnit == eCSSUnit_Percent ||
     107           0 :          aFirstUnit == eCSSUnit_Calc) &&
     108           0 :         (aSecondUnit == eCSSUnit_Pixel ||
     109           0 :          aSecondUnit == eCSSUnit_Percent ||
     110             :          aSecondUnit == eCSSUnit_Calc)) {
     111             :       // We can use calc() as the common unit.
     112           0 :       return eCSSUnit_Calc;
     113             :     }
     114           0 :     return eCSSUnit_Null;
     115             :   }
     116           0 :   return aFirstUnit;
     117             : }
     118             : 
     119             : static nsCSSKeyword
     120           0 : ToPrimitive(nsCSSKeyword aKeyword)
     121             : {
     122           0 :   switch (aKeyword) {
     123             :     case eCSSKeyword_translatex:
     124             :     case eCSSKeyword_translatey:
     125             :     case eCSSKeyword_translatez:
     126             :     case eCSSKeyword_translate:
     127           0 :       return eCSSKeyword_translate3d;
     128             :     case eCSSKeyword_scalex:
     129             :     case eCSSKeyword_scaley:
     130             :     case eCSSKeyword_scalez:
     131             :     case eCSSKeyword_scale:
     132           0 :       return eCSSKeyword_scale3d;
     133             :     default:
     134           0 :       return aKeyword;
     135             :   }
     136             : }
     137             : 
     138             : static bool
     139           0 : HasAccumulateMatrix(const nsCSSValueList* aList)
     140             : {
     141           0 :   const nsCSSValueList *item = aList;
     142           0 :   do {
     143             :     nsCSSKeyword func =
     144           0 :       nsStyleTransformMatrix::TransformFunctionOf(item->mValue.GetArrayValue());
     145           0 :     if (func == eCSSKeyword_accumulatematrix) {
     146           0 :       return true;
     147             :     }
     148           0 :     item = item->mNext;
     149           0 :   } while (item);
     150             : 
     151           0 :   return false;
     152             : }
     153             : 
     154             : static bool
     155           0 : TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2)
     156             : {
     157             :   // Handle eCSSKeyword_accumulatematrix as different function to be calculated
     158             :   // (decomposed and recomposed) them later.
     159           0 :   if (func1 == eCSSKeyword_accumulatematrix ||
     160             :       func2 == eCSSKeyword_accumulatematrix) {
     161           0 :     return false;
     162             :   }
     163             : 
     164           0 :   return ToPrimitive(func1) == ToPrimitive(func2);
     165             : }
     166             : 
     167             : static bool
     168           0 : TransformFunctionListsMatch(const nsCSSValueList *list1,
     169             :                             const nsCSSValueList *list2)
     170             : {
     171           0 :   const nsCSSValueList *item1 = list1, *item2 = list2;
     172           0 :   do {
     173             :     nsCSSKeyword func1 = nsStyleTransformMatrix::TransformFunctionOf(
     174           0 :         item1->mValue.GetArrayValue());
     175             :     nsCSSKeyword func2 = nsStyleTransformMatrix::TransformFunctionOf(
     176           0 :         item2->mValue.GetArrayValue());
     177             : 
     178           0 :     if (!TransformFunctionsMatch(func1, func2)) {
     179           0 :       return false;
     180             :     }
     181             : 
     182           0 :     item1 = item1->mNext;
     183           0 :     item2 = item2->mNext;
     184           0 :   } while (item1 && item2);
     185             : 
     186             :   // Length match?
     187           0 :   return !item1 && !item2;
     188             : }
     189             : 
     190             : static already_AddRefed<nsCSSValue::Array>
     191           0 : AppendFunction(nsCSSKeyword aTransformFunction)
     192             : {
     193             :   uint32_t nargs;
     194           0 :   switch (aTransformFunction) {
     195             :     case eCSSKeyword_matrix3d:
     196           0 :       nargs = 16;
     197           0 :       break;
     198             :     case eCSSKeyword_matrix:
     199           0 :       nargs = 6;
     200           0 :       break;
     201             :     case eCSSKeyword_rotate3d:
     202           0 :       nargs = 4;
     203           0 :       break;
     204             :     case eCSSKeyword_interpolatematrix:
     205             :     case eCSSKeyword_accumulatematrix:
     206             :     case eCSSKeyword_translate3d:
     207             :     case eCSSKeyword_scale3d:
     208           0 :       nargs = 3;
     209           0 :       break;
     210             :     case eCSSKeyword_translate:
     211             :     case eCSSKeyword_skew:
     212             :     case eCSSKeyword_scale:
     213           0 :       nargs = 2;
     214           0 :       break;
     215             :     default:
     216           0 :       NS_ERROR("must be a transform function");
     217             :       MOZ_FALLTHROUGH;
     218             :     case eCSSKeyword_translatex:
     219             :     case eCSSKeyword_translatey:
     220             :     case eCSSKeyword_translatez:
     221             :     case eCSSKeyword_scalex:
     222             :     case eCSSKeyword_scaley:
     223             :     case eCSSKeyword_scalez:
     224             :     case eCSSKeyword_skewx:
     225             :     case eCSSKeyword_skewy:
     226             :     case eCSSKeyword_rotate:
     227             :     case eCSSKeyword_rotatex:
     228             :     case eCSSKeyword_rotatey:
     229             :     case eCSSKeyword_rotatez:
     230             :     case eCSSKeyword_perspective:
     231           0 :       nargs = 1;
     232           0 :       break;
     233             :   }
     234             : 
     235           0 :   RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(nargs + 1);
     236           0 :   arr->Item(0).SetIntValue(aTransformFunction, eCSSUnit_Enumerated);
     237             : 
     238           0 :   return arr.forget();
     239             : }
     240             : 
     241             : static already_AddRefed<nsCSSValue::Array>
     242           0 : ToPrimitive(nsCSSValue::Array* aArray)
     243             : {
     244           0 :   nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(aArray);
     245           0 :   nsCSSKeyword primitive = ToPrimitive(tfunc);
     246           0 :   RefPtr<nsCSSValue::Array> arr = AppendFunction(primitive);
     247             : 
     248             :   // FIXME: This would produce fewer calc() expressions if the
     249             :   // zero were of compatible type (length vs. percent) when
     250             :   // needed.
     251             : 
     252           0 :   nsCSSValue zero(0.0f, eCSSUnit_Pixel);
     253           0 :   nsCSSValue one(1.0f, eCSSUnit_Number);
     254           0 :   switch(tfunc) {
     255             :     case eCSSKeyword_translate:
     256             :     {
     257           0 :       MOZ_ASSERT(aArray->Count() == 2 || aArray->Count() == 3,
     258             :                  "unexpected count");
     259           0 :       arr->Item(1) = aArray->Item(1);
     260           0 :       arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : zero;
     261           0 :       arr->Item(3) = zero;
     262           0 :       break;
     263             :     }
     264             :     case eCSSKeyword_translatex:
     265             :     {
     266           0 :       MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
     267           0 :       arr->Item(1) = aArray->Item(1);
     268           0 :       arr->Item(2) = zero;
     269           0 :       arr->Item(3) = zero;
     270           0 :       break;
     271             :     }
     272             :     case eCSSKeyword_translatey:
     273             :     {
     274           0 :       MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
     275           0 :       arr->Item(1) = zero;
     276           0 :       arr->Item(2) = aArray->Item(1);
     277           0 :       arr->Item(3) = zero;
     278           0 :       break;
     279             :     }
     280             :     case eCSSKeyword_translatez:
     281             :     {
     282           0 :       MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
     283           0 :       arr->Item(1) = zero;
     284           0 :       arr->Item(2) = zero;
     285           0 :       arr->Item(3) = aArray->Item(1);
     286           0 :       break;
     287             :     }
     288             :     case eCSSKeyword_scale:
     289             :     {
     290           0 :       MOZ_ASSERT(aArray->Count() == 2 || aArray->Count() == 3,
     291             :                  "unexpected count");
     292           0 :       arr->Item(1) = aArray->Item(1);
     293           0 :       arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : aArray->Item(1);
     294           0 :       arr->Item(3) = one;
     295           0 :       break;
     296             :     }
     297             :     case eCSSKeyword_scalex:
     298             :     {
     299           0 :       MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
     300           0 :       arr->Item(1) = aArray->Item(1);
     301           0 :       arr->Item(2) = one;
     302           0 :       arr->Item(3) = one;
     303           0 :       break;
     304             :     }
     305             :     case eCSSKeyword_scaley:
     306             :     {
     307           0 :       MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
     308           0 :       arr->Item(1) = one;
     309           0 :       arr->Item(2) = aArray->Item(1);
     310           0 :       arr->Item(3) = one;
     311           0 :       break;
     312             :     }
     313             :     case eCSSKeyword_scalez:
     314             :     {
     315           0 :       MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
     316           0 :       arr->Item(1) = one;
     317           0 :       arr->Item(2) = one;
     318           0 :       arr->Item(3) = aArray->Item(1);
     319           0 :       break;
     320             :     }
     321             :     default:
     322           0 :       arr = aArray;
     323             :   }
     324           0 :   return arr.forget();
     325             : }
     326             : 
     327             : static void
     328           0 : AppendCSSShadowValue(const nsCSSShadowItem *aShadow,
     329             :                      nsCSSValueList **&aResultTail,
     330             :                      nsCSSPropertyID aProperty)
     331             : {
     332           0 :   MOZ_ASSERT(aShadow, "shadow expected");
     333             : 
     334             :   // X, Y, Radius, Spread, Color, Inset
     335           0 :   RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(6);
     336           0 :   arr->Item(0).SetIntegerCoordValue(aShadow->mXOffset);
     337           0 :   arr->Item(1).SetIntegerCoordValue(aShadow->mYOffset);
     338           0 :   arr->Item(2).SetIntegerCoordValue(aShadow->mRadius);
     339           0 :   if (aProperty == eCSSProperty_box_shadow) {
     340           0 :     arr->Item(3).SetIntegerCoordValue(aShadow->mSpread);
     341             :   }
     342           0 :   if (aShadow->mHasColor) {
     343           0 :     arr->Item(4).SetColorValue(aShadow->mColor);
     344             :   }
     345           0 :   if (aShadow->mInset) {
     346           0 :     arr->Item(5).SetEnumValue(StyleBoxShadowType::Inset);
     347             :   }
     348             : 
     349           0 :   nsCSSValueList *resultItem = new nsCSSValueList;
     350           0 :   resultItem->mValue.SetArrayValue(arr, eCSSUnit_Array);
     351           0 :   *aResultTail = resultItem;
     352           0 :   aResultTail = &resultItem->mNext;
     353           0 : }
     354             : 
     355             : // Like nsStyleCoord::CalcValue, but with length in float pixels instead
     356             : // of nscoord.
     357             : struct PixelCalcValue
     358             : {
     359             :   float mLength, mPercent;
     360             :   bool mHasPercent;
     361             : };
     362             : 
     363             : // Requires a canonical calc() value that we generated.
     364             : static PixelCalcValue
     365           0 : ExtractCalcValueInternal(const nsCSSValue& aValue)
     366             : {
     367           0 :   MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Calc, "unexpected unit");
     368           0 :   nsCSSValue::Array *arr = aValue.GetArrayValue();
     369           0 :   MOZ_ASSERT(arr->Count() == 1, "unexpected length");
     370             : 
     371           0 :   const nsCSSValue &topval = arr->Item(0);
     372             :   PixelCalcValue result;
     373           0 :   if (topval.GetUnit() == eCSSUnit_Pixel) {
     374           0 :     result.mLength = topval.GetFloatValue();
     375           0 :     result.mPercent = 0.0f;
     376           0 :     result.mHasPercent = false;
     377             :   } else {
     378           0 :     MOZ_ASSERT(topval.GetUnit() == eCSSUnit_Calc_Plus,
     379             :                "unexpected unit");
     380           0 :     nsCSSValue::Array *arr2 = topval.GetArrayValue();
     381           0 :     const nsCSSValue &len = arr2->Item(0);
     382           0 :     const nsCSSValue &pct = arr2->Item(1);
     383           0 :     MOZ_ASSERT(len.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
     384           0 :     MOZ_ASSERT(pct.GetUnit() == eCSSUnit_Percent, "unexpected unit");
     385           0 :     result.mLength = len.GetFloatValue();
     386           0 :     result.mPercent = pct.GetPercentValue();
     387           0 :     result.mHasPercent = true;
     388             :   }
     389             : 
     390           0 :   return result;
     391             : }
     392             : 
     393             : // Requires a canonical calc() value that we generated.
     394             : static PixelCalcValue
     395           0 : ExtractCalcValue(const StyleAnimationValue& aValue)
     396             : {
     397             :   PixelCalcValue result;
     398           0 :   if (aValue.GetUnit() == StyleAnimationValue::eUnit_Coord) {
     399           0 :     result.mLength =
     400           0 :       nsPresContext::AppUnitsToFloatCSSPixels(aValue.GetCoordValue());
     401           0 :     result.mPercent = 0.0f;
     402           0 :     result.mHasPercent = false;
     403           0 :     return result;
     404             :   }
     405           0 :   if (aValue.GetUnit() == StyleAnimationValue::eUnit_Percent) {
     406           0 :     result.mLength = 0.0f;
     407           0 :     result.mPercent = aValue.GetPercentValue();
     408           0 :     result.mHasPercent = true;
     409           0 :     return result;
     410             :   }
     411           0 :   MOZ_ASSERT(aValue.GetUnit() == StyleAnimationValue::eUnit_Calc,
     412             :              "unexpected unit");
     413           0 :   nsCSSValue *val = aValue.GetCSSValueValue();
     414           0 :   return ExtractCalcValueInternal(*val);
     415             : }
     416             : 
     417             : static PixelCalcValue
     418           0 : ExtractCalcValue(const nsCSSValue& aValue)
     419             : {
     420             :   PixelCalcValue result;
     421           0 :   if (aValue.GetUnit() == eCSSUnit_Pixel) {
     422           0 :     result.mLength = aValue.GetFloatValue();
     423           0 :     result.mPercent = 0.0f;
     424           0 :     result.mHasPercent = false;
     425           0 :     return result;
     426             :   }
     427           0 :   if (aValue.GetUnit() == eCSSUnit_Percent) {
     428           0 :     result.mLength = 0.0f;
     429           0 :     result.mPercent = aValue.GetPercentValue();
     430           0 :     result.mHasPercent = true;
     431           0 :     return result;
     432             :   }
     433           0 :   return ExtractCalcValueInternal(aValue);
     434             : }
     435             : 
     436             : static void
     437           0 : CalcValueToCSSValue(const nsStyleCoord::CalcValue* aCalc, nsCSSValue& aValue)
     438             : {
     439           0 :   aValue.SetCalcValue(aCalc);
     440           0 : }
     441             : 
     442             : static void
     443           0 : CalcValueToCSSValue(const PixelCalcValue& aCalc, nsCSSValue& aValue)
     444             : {
     445           0 :   RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
     446           0 :   if (!aCalc.mHasPercent) {
     447           0 :     arr->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel);
     448             :   } else {
     449           0 :     nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
     450           0 :     arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
     451           0 :     arr2->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel);
     452           0 :     arr2->Item(1).SetPercentValue(aCalc.mPercent);
     453             :   }
     454             : 
     455           0 :   aValue.SetArrayValue(arr, eCSSUnit_Calc);
     456           0 : }
     457             : 
     458             : double
     459           0 : CalcPositionSquareDistance(const nsCSSValue& aPos1,
     460             :                            const nsCSSValue& aPos2)
     461             : {
     462           0 :   NS_ASSERTION(aPos1.GetUnit() == eCSSUnit_Array &&
     463             :                aPos2.GetUnit() == eCSSUnit_Array,
     464             :                "Expected two arrays");
     465             : 
     466             :   PixelCalcValue calcVal[4];
     467             : 
     468           0 :   nsCSSValue::Array* posArray = aPos1.GetArrayValue();
     469           0 :   MOZ_ASSERT(posArray->Count() == 4, "Invalid position value");
     470           0 :   NS_ASSERTION(posArray->Item(0).GetUnit() == eCSSUnit_Null &&
     471             :                posArray->Item(2).GetUnit() == eCSSUnit_Null,
     472             :                "Invalid list used");
     473           0 :   for (int i = 0; i < 2; ++i) {
     474           0 :     MOZ_ASSERT(posArray->Item(i*2+1).GetUnit() != eCSSUnit_Null,
     475             :                "Invalid position value");
     476           0 :     calcVal[i] = ExtractCalcValue(posArray->Item(i*2+1));
     477             :   }
     478             : 
     479           0 :   posArray = aPos2.GetArrayValue();
     480           0 :   MOZ_ASSERT(posArray->Count() == 4, "Invalid position value");
     481           0 :   NS_ASSERTION(posArray->Item(0).GetUnit() == eCSSUnit_Null &&
     482             :                posArray->Item(2).GetUnit() == eCSSUnit_Null,
     483             :                "Invalid list used");
     484           0 :   for (int i = 0; i < 2; ++i) {
     485           0 :     MOZ_ASSERT(posArray->Item(i*2+1).GetUnit() != eCSSUnit_Null,
     486             :                "Invalid position value");
     487           0 :     calcVal[i+2] = ExtractCalcValue(posArray->Item(i*2+1));
     488             :   }
     489             : 
     490           0 :   double squareDistance = 0.0;
     491           0 :   for (int i = 0; i < 2; ++i) {
     492           0 :     float difflen = calcVal[i+2].mLength - calcVal[i].mLength;
     493           0 :     float diffpct = calcVal[i+2].mPercent - calcVal[i].mPercent;
     494           0 :     squareDistance += difflen * difflen + diffpct * diffpct;
     495             :   }
     496             : 
     497           0 :   return squareDistance;
     498             : }
     499             : 
     500             : static PixelCalcValue
     501           0 : CalcBackgroundCoord(const nsCSSValue& aCoord)
     502             : {
     503           0 :   NS_ASSERTION(aCoord.GetUnit() == eCSSUnit_Array,
     504             :                "Expected array");
     505             : 
     506           0 :   nsCSSValue::Array* array = aCoord.GetArrayValue();
     507           0 :   MOZ_ASSERT(array->Count() == 2 &&
     508             :              array->Item(0).GetUnit() == eCSSUnit_Null &&
     509             :              array->Item(1).GetUnit() != eCSSUnit_Null,
     510             :              "Invalid position value");
     511           0 :   return ExtractCalcValue(array->Item(1));
     512             : }
     513             : 
     514             : double
     515           0 : CalcPositionCoordSquareDistance(const nsCSSValue& aPos1,
     516             :                                 const nsCSSValue& aPos2)
     517             : {
     518           0 :   PixelCalcValue calcVal1 = CalcBackgroundCoord(aPos1);
     519           0 :   PixelCalcValue calcVal2 = CalcBackgroundCoord(aPos2);
     520             : 
     521           0 :   float difflen = calcVal2.mLength - calcVal1.mLength;
     522           0 :   float diffpct = calcVal2.mPercent - calcVal1.mPercent;
     523           0 :   return difflen * difflen + diffpct * diffpct;
     524             : }
     525             : 
     526             : // Ensure that a float/double value isn't NaN by returning zero instead
     527             : // (NaN doesn't have a sign) as a general restriction for floating point
     528             : // values in RestrictValue.
     529             : template<typename T>
     530             : MOZ_ALWAYS_INLINE T
     531           0 : EnsureNotNan(T aValue)
     532             : {
     533           0 :   return aValue;
     534             : }
     535             : template<>
     536             : MOZ_ALWAYS_INLINE float
     537           0 : EnsureNotNan(float aValue)
     538             : {
     539             :   // This would benefit from a MOZ_FLOAT_IS_NaN if we had one.
     540           0 :   return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0;
     541             : }
     542             : template<>
     543             : MOZ_ALWAYS_INLINE double
     544          10 : EnsureNotNan(double aValue)
     545             : {
     546          10 :   return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0;
     547             : }
     548             : 
     549             : template <typename T>
     550             : T
     551          10 : RestrictValue(uint32_t aRestrictions, T aValue)
     552             : {
     553          10 :   T result = EnsureNotNan(aValue);
     554          10 :   switch (aRestrictions) {
     555             :     case 0:
     556          10 :       break;
     557             :     case CSS_PROPERTY_VALUE_NONNEGATIVE:
     558           0 :       if (result < 0) {
     559           0 :         result = 0;
     560             :       }
     561           0 :       break;
     562             :     case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
     563           0 :       if (result < 1) {
     564           0 :         result = 1;
     565             :       }
     566           0 :       break;
     567             :     default:
     568           0 :       MOZ_ASSERT(false, "bad value restriction");
     569             :       break;
     570             :   }
     571          10 :   return result;
     572             : }
     573             : 
     574             : template <typename T>
     575             : T
     576          10 : RestrictValue(nsCSSPropertyID aProperty, T aValue)
     577             : {
     578          10 :   return RestrictValue(nsCSSProps::ValueRestrictions(aProperty), aValue);
     579             : }
     580             : 
     581             : static void
     582           0 : AddCSSValueAngle(double aCoeff1, const nsCSSValue &aValue1,
     583             :                  double aCoeff2, const nsCSSValue &aValue2,
     584             :                  nsCSSValue &aResult)
     585             : {
     586           0 :   if (aValue1.GetUnit() == aValue2.GetUnit()) {
     587             :     // To avoid floating point error, if the units match, maintain the unit.
     588           0 :     aResult.SetFloatValue(
     589           0 :       EnsureNotNan(aCoeff1 * aValue1.GetFloatValue() +
     590           0 :                    aCoeff2 * aValue2.GetFloatValue()),
     591           0 :       aValue1.GetUnit());
     592             :   } else {
     593           0 :     aResult.SetFloatValue(
     594           0 :       EnsureNotNan(aCoeff1 * aValue1.GetAngleValueInRadians() +
     595           0 :                    aCoeff2 * aValue2.GetAngleValueInRadians()),
     596           0 :       eCSSUnit_Radian);
     597             :   }
     598           0 : }
     599             : 
     600             : static inline void
     601           0 : AddCSSValuePercent(double aCoeff1, const nsCSSValue &aValue1,
     602             :                    double aCoeff2, const nsCSSValue &aValue2,
     603             :                    nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
     604             : {
     605           0 :   MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Percent, "unexpected unit");
     606           0 :   MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Percent, "unexpected unit");
     607           0 :   aResult.SetPercentValue(RestrictValue(aValueRestrictions,
     608           0 :                                         aCoeff1 * aValue1.GetPercentValue() +
     609           0 :                                         aCoeff2 * aValue2.GetPercentValue()));
     610           0 : }
     611             : 
     612             : // Add two canonical-form calc values (eUnit_Calc) to make another
     613             : // canonical-form calc value.
     614             : static void
     615           0 : AddCSSValueCanonicalCalc(double aCoeff1, const nsCSSValue &aValue1,
     616             :                          double aCoeff2, const nsCSSValue &aValue2,
     617             :                          nsCSSValue &aResult)
     618             : {
     619           0 :   PixelCalcValue v1 = ExtractCalcValue(aValue1);
     620           0 :   PixelCalcValue v2 = ExtractCalcValue(aValue2);
     621             :   PixelCalcValue result;
     622           0 :   result.mLength = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
     623           0 :   result.mPercent = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
     624           0 :   result.mHasPercent = v1.mHasPercent || v2.mHasPercent;
     625           0 :   MOZ_ASSERT(result.mHasPercent || result.mPercent == 0.0f,
     626             :              "can't have a nonzero percentage part without having percentages");
     627           0 :   CalcValueToCSSValue(result, aResult);
     628           0 : }
     629             : 
     630             : static inline void
     631           0 : AddCSSValuePixel(double aCoeff1, const nsCSSValue &aValue1,
     632             :                  double aCoeff2, const nsCSSValue &aValue2,
     633             :                  nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
     634             : {
     635           0 :   MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
     636           0 :   MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
     637           0 :   aResult.SetFloatValue(RestrictValue(aValueRestrictions,
     638           0 :                                       aCoeff1 * aValue1.GetFloatValue() +
     639           0 :                                       aCoeff2 * aValue2.GetFloatValue()),
     640           0 :                         eCSSUnit_Pixel);
     641           0 : }
     642             : 
     643             : static bool
     644           0 : AddCSSValuePixelPercentCalc(const uint32_t aValueRestrictions,
     645             :                             const nsCSSUnit aCommonUnit,
     646             :                             double aCoeff1, const nsCSSValue &aValue1,
     647             :                             double aCoeff2, const nsCSSValue &aValue2,
     648             :                             nsCSSValue &aResult)
     649             : {
     650           0 :   switch (aCommonUnit) {
     651             :     case eCSSUnit_Pixel:
     652             :       AddCSSValuePixel(aCoeff1, aValue1,
     653             :                        aCoeff2, aValue2,
     654           0 :                        aResult, aValueRestrictions);
     655           0 :       break;
     656             :     case eCSSUnit_Percent:
     657             :       AddCSSValuePercent(aCoeff1, aValue1,
     658             :                          aCoeff2, aValue2,
     659           0 :                          aResult, aValueRestrictions);
     660           0 :       break;
     661             :     case eCSSUnit_Calc:
     662             :       AddCSSValueCanonicalCalc(aCoeff1, aValue1,
     663             :                                aCoeff2, aValue2,
     664           0 :                                aResult);
     665           0 :       break;
     666             :     default:
     667           0 :       return false;
     668             :   }
     669             : 
     670           0 :   return true;
     671             : }
     672             : 
     673             : static void
     674           0 : AddTransformTranslate(double aCoeff1, const nsCSSValue &aValue1,
     675             :                       double aCoeff2, const nsCSSValue &aValue2,
     676             :                       nsCSSValue &aResult)
     677             : {
     678             :   // Only three possible units: eCSSUnit_Pixel, eCSSUnit_Percent, or
     679             :   // eCSSUnit_Calc.
     680           0 :   MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Percent ||
     681             :              aValue1.GetUnit() == eCSSUnit_Pixel ||
     682             :              aValue1.IsCalcUnit(),
     683             :              "unexpected unit");
     684           0 :   MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Percent ||
     685             :              aValue2.GetUnit() == eCSSUnit_Pixel ||
     686             :              aValue2.IsCalcUnit(),
     687             :              "unexpected unit");
     688           0 :   AddCSSValuePixelPercentCalc(0,
     689           0 :                               (aValue1.GetUnit() != aValue2.GetUnit() ||
     690           0 :                                aValue1.IsCalcUnit())
     691             :                                 ? eCSSUnit_Calc
     692             :                                 : aValue1.GetUnit(),
     693             :                               aCoeff1, aValue1,
     694             :                               aCoeff2, aValue2,
     695           0 :                               aResult);
     696           0 : }
     697             : 
     698             : // Unclamped AddWeightedColors.
     699             : static RGBAColorData
     700           0 : AddWeightedColors(double aCoeff1, const RGBAColorData& aValue1,
     701             :                   double aCoeff2, const RGBAColorData& aValue2)
     702             : {
     703           0 :   float factor1 = aValue1.mA * aCoeff1;
     704           0 :   float factor2 = aValue2.mA * aCoeff2;
     705           0 :   float resultA = factor1 + factor2;
     706           0 :   if (resultA <= 0.0) {
     707           0 :     return {0, 0, 0, 0};
     708             :   }
     709             : 
     710           0 :   if (resultA > 1.0) {
     711           0 :     resultA = 1.0;
     712             :   }
     713             : 
     714           0 :   float resultFactor = 1.0f / resultA;
     715           0 :   return RGBAColorData(
     716           0 :     (aValue1.mR * factor1 + aValue2.mR * factor2) * resultFactor,
     717           0 :     (aValue1.mG * factor1 + aValue2.mG * factor2) * resultFactor,
     718           0 :     (aValue1.mB * factor1 + aValue2.mB * factor2) * resultFactor,
     719           0 :     resultA);
     720             : }
     721             : 
     722             : // CLASS METHODS
     723             : // -------------
     724             : 
     725             : static RGBAColorData
     726           0 : ExtractColor(const nsCSSValue& aValue)
     727             : {
     728           0 :   MOZ_ASSERT(aValue.IsNumericColorUnit(), "The unit should be color");
     729             :   // PercentageRGBColor and PercentageRGBAColor component value might be
     730             :   // greater than 1.0 in case when the color value is accumulated, so we
     731             :   // can't use nsCSSValue::GetColorValue() here because that function
     732             :   // clamps its values.
     733           0 :   if (aValue.GetUnit() == eCSSUnit_PercentageRGBColor ||
     734           0 :       aValue.GetUnit() == eCSSUnit_PercentageRGBAColor) {
     735           0 :     nsCSSValueFloatColor* floatColor = aValue.GetFloatColorValue();
     736             :     return {
     737             :       floatColor->Comp1(),
     738             :       floatColor->Comp2(),
     739             :       floatColor->Comp3(),
     740             :       floatColor->Alpha()
     741           0 :     };
     742             :   }
     743           0 :   return RGBAColorData(aValue.GetColorValue());
     744             : }
     745             : 
     746             : static RGBAColorData
     747           0 : ExtractColor(const StyleAnimationValue& aValue)
     748             : {
     749           0 :   MOZ_ASSERT(aValue.GetUnit() == StyleAnimationValue::eUnit_Color);
     750           0 :   nsCSSValue* value = aValue.GetCSSValueValue();
     751           0 :   MOZ_ASSERT(value, "CSS value must be valid");
     752           0 :   return ExtractColor(*value);
     753             : }
     754             : 
     755             : static ComplexColorData
     756           0 : ExtractComplexColor(const StyleAnimationValue& aValue)
     757             : {
     758           0 :   switch (aValue.GetUnit()) {
     759             :     case StyleAnimationValue::eUnit_Color:
     760           0 :       return ComplexColorData(ExtractColor(aValue), 0.0f);
     761             :     case StyleAnimationValue::eUnit_CurrentColor:
     762           0 :       return ComplexColorData({0, 0, 0, 0}, 1.0f);
     763             :     case StyleAnimationValue::eUnit_ComplexColor:
     764           0 :       return ComplexColorData(aValue.GetComplexColorData());
     765             :     default:
     766           0 :       MOZ_ASSERT_UNREACHABLE("Unknown unit");
     767             :       return ComplexColorData({0, 0, 0, 0}, 0.0f);
     768             :   }
     769             : }
     770             : 
     771             : StyleAnimationValue
     772           0 : StyleAnimationValue::Add(nsCSSPropertyID aProperty,
     773             :                          const StyleAnimationValue& aA,
     774             :                          StyleAnimationValue&& aB)
     775             : {
     776           0 :   StyleAnimationValue result(Move(aB));
     777             : 
     778             :   Unit commonUnit =
     779           0 :     GetCommonUnit(aProperty, result.GetUnit(), aA.GetUnit());
     780           0 :   switch (commonUnit) {
     781             :     case eUnit_Color: {
     782           0 :       RGBAColorData color1 = ExtractColor(result);
     783           0 :       RGBAColorData color2 = ExtractColor(aA);
     784           0 :       result.mValue.mCSSValue->SetRGBAColorValue(
     785           0 :         AddWeightedColors(1.0, color1, 1, color2));
     786           0 :       break;
     787             :     }
     788             :     case eUnit_Filter:
     789             :     case eUnit_Shadow: {
     790             :       // If |aA| has no function list, don't concatinate anything, just return
     791             :       // |aB| as the result.
     792           0 :       if (!aA.GetCSSValueListValue() ||
     793           0 :           aA.GetCSSValueListValue()->mValue.GetUnit() == eCSSUnit_None) {
     794           0 :         break;
     795             :       }
     796           0 :       UniquePtr<nsCSSValueList> resultList(aA.GetCSSValueListValue()->Clone());
     797             : 
     798             :       // If |aB| has function list, concatinate it to |aA|, then return
     799             :       // the concatinated list.
     800           0 :       if (result.GetCSSValueListValue() &&
     801           0 :           result.GetCSSValueListValue()->mValue.GetUnit() != eCSSUnit_None) {
     802           0 :         nsCSSValueList* listA = resultList.get();
     803           0 :         while (listA->mNext) {
     804           0 :           listA = listA->mNext;
     805             :         }
     806             : 
     807           0 :         listA->mNext = result.GetCSSValueListValue();
     808             :       }
     809           0 :       result.mValue.mCSSValueList = resultList.release();
     810           0 :       break;
     811             :     }
     812             :     case eUnit_Transform: {
     813             :       // If |aA| is 'transform:none', don't concatinate anything, just return
     814             :       // |aB| as the result.
     815           0 :       if (aA.GetCSSValueSharedListValue()->mHead->mValue.GetUnit() ==
     816             :             eCSSUnit_None) {
     817           0 :         break;
     818             :       }
     819             : 
     820             :       UniquePtr<nsCSSValueList>
     821           0 :         resultList(aA.GetCSSValueSharedListValue()->mHead->Clone());
     822             : 
     823             :       // If |aB| is not 'transform:none', concatinate it to |aA|, then return
     824             :       // the concatinated list.
     825           0 :       if (result.GetCSSValueSharedListValue()->mHead->mValue.GetUnit() !=
     826             :             eCSSUnit_None) {
     827           0 :         nsCSSValueList* listA = resultList.get();
     828           0 :         while (listA->mNext) {
     829           0 :           listA = listA->mNext;
     830             :         }
     831             : 
     832           0 :         listA->mNext = result.GetCSSValueSharedListValue()->mHead->Clone();
     833             :       }
     834             : 
     835           0 :       result.SetTransformValue(new nsCSSValueSharedList(resultList.release()));
     836           0 :       break;
     837             :     }
     838             :     default:
     839           0 :       Unused << AddWeighted(aProperty,
     840             :                             1.0, result,
     841             :                             1, aA,
     842             :                             result);
     843           0 :       break;
     844             :   }
     845             : 
     846           0 :   return result;
     847             : }
     848             : 
     849             : double
     850           0 : StyleAnimationValue::ComputeColorDistance(const RGBAColorData& aStartColor,
     851             :                                           const RGBAColorData& aEndColor)
     852             : {
     853             :   // http://www.w3.org/TR/smil-animation/#animateColorElement says
     854             :   // that we should use Euclidean RGB cube distance.  However, we
     855             :   // have to extend that to RGBA.  For now, we'll just use the
     856             :   // Euclidean distance in the (part of the) 4-cube of premultiplied
     857             :   // colors.
     858           0 :   double startA = aStartColor.mA;
     859           0 :   double startR = aStartColor.mR * startA;
     860           0 :   double startG = aStartColor.mG * startA;
     861           0 :   double startB = aStartColor.mB * startA;
     862           0 :   double endA = aEndColor.mA;
     863           0 :   double endR = aEndColor.mR * endA;
     864           0 :   double endG = aEndColor.mG * endA;
     865           0 :   double endB = aEndColor.mB * endA;
     866             : 
     867           0 :   double diffA = startA - endA;
     868           0 :   double diffR = startR - endR;
     869           0 :   double diffG = startG - endG;
     870           0 :   double diffB = startB - endB;
     871           0 :   return sqrt(diffA * diffA + diffR * diffR + diffG * diffG + diffB * diffB);
     872             : }
     873             : 
     874             : enum class ColorAdditionType {
     875             :   Clamped, // Clamp each color channel after adding.
     876             :   Unclamped // Do not clamp color channels after adding.
     877             : };
     878             : 
     879             : static UniquePtr<nsCSSValueList>
     880             : AddWeightedFilterFunction(double aCoeff1, const nsCSSValueList* aList1,
     881             :                           double aCoeff2, const nsCSSValueList* aList2,
     882             :                           ColorAdditionType aColorAdditionType);
     883             : 
     884             : static inline float
     885             : GetNumberOrPercent(const nsCSSValue &aValue);
     886             : 
     887             : static bool
     888           0 : ComputeSingleShadowSquareDistance(const nsCSSValueList* aShadow1,
     889             :                                   const nsCSSValueList* aShadow2,
     890             :                                   double& aSquareDistance,
     891             :                                   nsCSSPropertyID aProperty)
     892             : {
     893           0 :   MOZ_ASSERT(aShadow1->mValue.GetUnit() == eCSSUnit_Array, "wrong unit");
     894           0 :   MOZ_ASSERT(aShadow2->mValue.GetUnit() == eCSSUnit_Array, "wrong unit");
     895           0 :   const nsCSSValue::Array* array1 = aShadow1->mValue.GetArrayValue();
     896           0 :   const nsCSSValue::Array* array2 = aShadow2->mValue.GetArrayValue();
     897             : 
     898           0 :   double squareDistance = 0.0;
     899             :   // X, Y, Radius, Spread
     900           0 :   for (size_t i = 0; i < 4; ++i) {
     901             :     // Spread value is not necessary on text-shadow,
     902             :     // so we skip the computing distance.
     903           0 :     if (i == 3 && (aProperty != eCSSProperty_box_shadow)) {
     904           0 :       continue;
     905             :     }
     906           0 :     MOZ_ASSERT(array1->Item(i).GetUnit() == eCSSUnit_Pixel,
     907             :                "unexpected unit");
     908           0 :     MOZ_ASSERT(array2->Item(i).GetUnit() == eCSSUnit_Pixel,
     909             :                "unexpected unit");
     910           0 :     double diff = array1->Item(i).GetFloatValue() -
     911           0 :                   array2->Item(i).GetFloatValue();
     912           0 :     squareDistance += diff * diff;
     913             :   }
     914             : 
     915             :   // Color, Inset
     916           0 :   const nsCSSValue& color1 = array1->Item(4);
     917           0 :   const nsCSSValue& color2 = array2->Item(4);
     918           0 :   const nsCSSValue& inset1 = array1->Item(5);
     919           0 :   const nsCSSValue& inset2 = array2->Item(5);
     920           0 :   if ((color1.GetUnit() != color2.GetUnit() &&
     921           0 :         (!color1.IsNumericColorUnit() ||
     922           0 :          !color2.IsNumericColorUnit())) ||
     923           0 :       inset1 != inset2) {
     924             :     // According to AddWeightedShadowItems, we don't know how to animate
     925             :     // between color and no-color, or between inset and not-inset,
     926             :     // so we cannot compute the distance either.
     927             :     // Note: There are only two possible states of the inset value:
     928             :     //  (1) GetUnit() == eCSSUnit_Null
     929             :     //  (2) GetUnit() == eCSSUnit_Enumerated &&
     930             :     //      GetIntValue() == NS_STYLE_BOX_SHADOW_INSET
     931           0 :     return false;
     932             :   }
     933             : 
     934             :   // We compute the distance of colors only if both are numeric color units.
     935           0 :   if (color1.GetUnit() != eCSSUnit_Null) {
     936             :     double colorDistance =
     937           0 :       StyleAnimationValue::ComputeColorDistance(ExtractColor(color1),
     938           0 :                                                 ExtractColor(color2));
     939           0 :     squareDistance += colorDistance * colorDistance;
     940             :   }
     941             : 
     942           0 :   aSquareDistance = squareDistance;
     943           0 :   return true;
     944             : }
     945             : 
     946             : // Return false if we cannot compute the distance between these filter
     947             : // functions.
     948             : static bool
     949           0 : ComputeFilterSquareDistance(const nsCSSValueList* aList1,
     950             :                             const nsCSSValueList* aList2,
     951             :                             double& aSquareDistance)
     952             : {
     953           0 :   MOZ_ASSERT(aList1, "expected filter list");
     954           0 :   MOZ_ASSERT(aList2, "expected filter list");
     955           0 :   MOZ_ASSERT(aList1->mValue.GetUnit() == eCSSUnit_Function,
     956             :              "expected function");
     957           0 :   MOZ_ASSERT(aList2->mValue.GetUnit() == eCSSUnit_Function,
     958             :              "expected function");
     959             : 
     960           0 :   RefPtr<nsCSSValue::Array> a1 = aList1->mValue.GetArrayValue();
     961           0 :   RefPtr<nsCSSValue::Array> a2 = aList2->mValue.GetArrayValue();
     962           0 :   nsCSSKeyword filterFunction = a1->Item(0).GetKeywordValue();
     963           0 :   if (filterFunction != a2->Item(0).GetKeywordValue()) {
     964           0 :     return false;
     965             :   }
     966             : 
     967           0 :   const nsCSSValue& func1 = a1->Item(1);
     968           0 :   const nsCSSValue& func2 = a2->Item(1);
     969           0 :   switch (filterFunction) {
     970             :     case eCSSKeyword_blur: {
     971           0 :       nsCSSValue diff;
     972             :       // In AddWeightedFilterFunctionImpl, blur may have different units, so we
     973             :       // use eCSSUnit_Calc for that case.
     974           0 :       if (!AddCSSValuePixelPercentCalc(0,
     975           0 :                                        func1.GetUnit() == func2.GetUnit()
     976             :                                          ? func1.GetUnit()
     977             :                                          : eCSSUnit_Calc,
     978             :                                        1.0, func2,
     979             :                                        -1.0, func1,
     980             :                                        diff)) {
     981           0 :         return false;
     982             :       }
     983             :       // ExtractCalcValue makes sure mHasPercent and mPercent are correct.
     984           0 :       PixelCalcValue v = ExtractCalcValue(diff);
     985           0 :       aSquareDistance = v.mLength * v.mLength + v.mPercent * v.mPercent;
     986           0 :       break;
     987             :     }
     988             :     case eCSSKeyword_grayscale:
     989             :     case eCSSKeyword_invert:
     990             :     case eCSSKeyword_sepia:
     991             :     case eCSSKeyword_brightness:
     992             :     case eCSSKeyword_contrast:
     993             :     case eCSSKeyword_opacity:
     994             :     case eCSSKeyword_saturate: {
     995             :       double diff =
     996           0 :         EnsureNotNan(GetNumberOrPercent(func2) - GetNumberOrPercent(func1));
     997           0 :       aSquareDistance = diff * diff;
     998           0 :       break;
     999             :     }
    1000             :     case eCSSKeyword_hue_rotate: {
    1001           0 :       nsCSSValue v;
    1002           0 :       AddCSSValueAngle(1.0, func2, -1.0, func1, v);
    1003           0 :       double diff = v.GetAngleValueInRadians();
    1004           0 :       aSquareDistance = diff * diff;
    1005           0 :       break;
    1006             :     }
    1007             :     case eCSSKeyword_drop_shadow: {
    1008           0 :       MOZ_ASSERT(!func1.GetListValue()->mNext && !func2.GetListValue()->mNext,
    1009             :                  "drop-shadow filter func doesn't support lists");
    1010           0 :       if (!ComputeSingleShadowSquareDistance(func1.GetListValue(),
    1011             :                                              func2.GetListValue(),
    1012             :                                              aSquareDistance,
    1013             :                                              eCSSProperty_filter)) {
    1014           0 :         return false;
    1015             :       }
    1016           0 :       break;
    1017             :     }
    1018             :     default:
    1019           0 :       MOZ_ASSERT_UNREACHABLE("unknown filter function");
    1020             :       return false;
    1021             :   }
    1022           0 :   return true;
    1023             : }
    1024             : 
    1025             : static bool
    1026           0 : ComputeFilterListDistance(const nsCSSValueList* aList1,
    1027             :                           const nsCSSValueList* aList2,
    1028             :                           double& aDistance)
    1029             : {
    1030           0 :   double squareDistance = 0.0;
    1031           0 :   while (aList1 || aList2) {
    1032             :     // Return false if one of the lists is neither none nor a function.
    1033           0 :     if ((aList1 && aList1->mValue.GetUnit() != eCSSUnit_Function) ||
    1034           0 :         (aList2 && aList2->mValue.GetUnit() != eCSSUnit_Function)) {
    1035           0 :       return false;
    1036             :     }
    1037             : 
    1038           0 :     MOZ_ASSERT(aList1 || aList2, "one function list item must not be null");
    1039             : 
    1040           0 :     double currentSquareDistance = 0.0;
    1041           0 :     if (!aList1) {
    1042             :       // This is a tricky to get an equivalent none filter function by 0.0
    1043             :       // coefficients. Although we don't guarantee this function can get the
    1044             :       // correct default values, it can reuse the code from the interpolation.
    1045             :       UniquePtr<nsCSSValueList> none =
    1046             :         AddWeightedFilterFunction(0, aList2, 0, aList2,
    1047           0 :                                   ColorAdditionType::Clamped);
    1048           0 :       if (!ComputeFilterSquareDistance(none.get(), aList2,
    1049             :                                        currentSquareDistance)) {
    1050           0 :         return false;
    1051             :       }
    1052           0 :       aList2 = aList2->mNext;
    1053           0 :     } else if (!aList2) {
    1054             :       UniquePtr<nsCSSValueList> none =
    1055             :         AddWeightedFilterFunction(0, aList1, 0, aList1,
    1056           0 :                                   ColorAdditionType::Clamped);
    1057           0 :       if (!ComputeFilterSquareDistance(aList1, none.get(),
    1058             :                                        currentSquareDistance)) {
    1059           0 :         return false;
    1060             :       }
    1061           0 :       aList1 = aList1->mNext;
    1062             :     } else {
    1063           0 :       if (!ComputeFilterSquareDistance(aList1, aList2,
    1064             :                                        currentSquareDistance)) {
    1065           0 :         return false;
    1066             :       }
    1067           0 :       aList1 = aList1->mNext;
    1068           0 :       aList2 = aList2->mNext;
    1069             :     }
    1070           0 :     squareDistance += currentSquareDistance;
    1071             :   }
    1072           0 :   aDistance = sqrt(squareDistance);
    1073           0 :   return true;
    1074             : }
    1075             : 
    1076             : enum class Restrictions {
    1077             :   Enable,
    1078             :   Disable
    1079             : };
    1080             : 
    1081             : static already_AddRefed<nsCSSValue::Array>
    1082             : AddShapeFunction(nsCSSPropertyID aProperty,
    1083             :                  double aCoeff1, const nsCSSValue::Array* aArray1,
    1084             :                  double aCoeff2, const nsCSSValue::Array* aArray2,
    1085             :                  Restrictions aRestriction = Restrictions::Enable);
    1086             : 
    1087             : static bool
    1088           0 : ComputeShapeDistance(nsCSSPropertyID aProperty,
    1089             :                      const nsCSSValue::Array* aArray1,
    1090             :                      const nsCSSValue::Array* aArray2,
    1091             :                      double& aDistance)
    1092             : {
    1093             :   // Use AddShapeFunction to get the difference between two shape functions.
    1094             :   RefPtr<nsCSSValue::Array> diffShape =
    1095           0 :     AddShapeFunction(aProperty, 1.0, aArray2, -1.0, aArray1,
    1096           0 :                      Restrictions::Disable);
    1097           0 :   if (!diffShape) {
    1098           0 :     return false;
    1099             :   }
    1100             : 
    1101             :   // A helper function to convert a calc() diff value into a double distance.
    1102           0 :   auto pixelCalcDistance = [](const PixelCalcValue& aValue) {
    1103           0 :     MOZ_ASSERT(aValue.mHasPercent || aValue.mPercent == 0.0f,
    1104             :              "can't have a nonzero percentage part without having percentages");
    1105           0 :     return aValue.mLength * aValue.mLength + aValue.mPercent * aValue.mPercent;
    1106             :   };
    1107             : 
    1108           0 :   double squareDistance = 0.0;
    1109           0 :   const nsCSSValue::Array* func = diffShape->Item(0).GetArrayValue();
    1110           0 :   nsCSSKeyword shapeFuncName = func->Item(0).GetKeywordValue();
    1111           0 :   switch (shapeFuncName) {
    1112             :     case eCSSKeyword_ellipse:
    1113             :     case eCSSKeyword_circle: {
    1114             :       // Skip the first element which is the function keyword.
    1115             :       // Also, skip the last element which is an array for <position>
    1116           0 :       const size_t len = func->Count();
    1117           0 :       for (size_t i = 1; i < len - 1; ++i) {
    1118           0 :         squareDistance += pixelCalcDistance(ExtractCalcValue(func->Item(i)));
    1119             :       }
    1120             :       // Only iterate over elements 1 and 3. The <position> is 'uncomputed' to
    1121             :       // only those elements.  See also the comment in SetPositionValue.
    1122           0 :       for (size_t i = 1; i < 4; i += 2) {
    1123           0 :         const nsCSSValue& value = func->Item(len - 1).GetArrayValue()->Item(i);
    1124           0 :         squareDistance += pixelCalcDistance(ExtractCalcValue(value));
    1125             :       }
    1126           0 :       break;
    1127             :     }
    1128             :     case eCSSKeyword_polygon: {
    1129             :       // Don't care about the first element which is the function keyword, and
    1130             :       // the second element which is the fill rule.
    1131           0 :       const nsCSSValuePairList* list = func->Item(2).GetPairListValue();
    1132           0 :       do {
    1133           0 :         squareDistance += pixelCalcDistance(ExtractCalcValue(list->mXValue)) +
    1134           0 :                           pixelCalcDistance(ExtractCalcValue(list->mYValue));
    1135           0 :         list = list->mNext;
    1136           0 :       } while (list);
    1137           0 :       break;
    1138             :     }
    1139             :     case eCSSKeyword_inset: {
    1140             :       // Items 1-4 are respectively the top, right, bottom and left offsets
    1141             :       // from the reference box.
    1142           0 :       for (size_t i = 1; i <= 4; ++i) {
    1143           0 :         const nsCSSValue& value = func->Item(i);
    1144           0 :         squareDistance += pixelCalcDistance(ExtractCalcValue(value));
    1145             :       }
    1146             :       // Item 5 contains the radii of the rounded corners for the inset
    1147             :       // rectangle.
    1148           0 :       const nsCSSValue::Array* array = func->Item(5).GetArrayValue();
    1149           0 :       const size_t len = array->Count();
    1150           0 :       for (size_t i = 0; i < len; ++i) {
    1151           0 :         const nsCSSValuePair& pair = array->Item(i).GetPairValue();
    1152           0 :         squareDistance += pixelCalcDistance(ExtractCalcValue(pair.mXValue)) +
    1153           0 :                           pixelCalcDistance(ExtractCalcValue(pair.mYValue));
    1154             :       }
    1155           0 :       break;
    1156             :     }
    1157             :     default:
    1158           0 :       MOZ_ASSERT_UNREACHABLE("Unknown shape type");
    1159             :   }
    1160           0 :   aDistance = sqrt(squareDistance);
    1161           0 :   return true;
    1162             : }
    1163             : 
    1164             : static nsCSSValueList*
    1165             : AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
    1166             :                   double aCoeff2, const nsCSSValueList* aList2,
    1167             :                   nsCSSKeyword aOperatorType = eCSSKeyword_interpolatematrix);
    1168             : 
    1169             : static double
    1170           0 : ComputeTransform2DMatrixDistance(const Matrix& aMatrix1,
    1171             :                                  const Matrix& aMatrix2)
    1172             : {
    1173           0 :   Point3D scale1(1, 1, 1);
    1174           0 :   Point3D translate1;
    1175           0 :   gfxQuaternion rotate1;
    1176           0 :   nsStyleTransformMatrix::ShearArray shear1{0.0f, 0.0f, 0.0f};
    1177           0 :   Decompose2DMatrix(aMatrix1, scale1, shear1, rotate1, translate1);
    1178             : 
    1179           0 :   Point3D scale2(1, 1, 1);
    1180           0 :   Point3D translate2;
    1181           0 :   gfxQuaternion rotate2;
    1182           0 :   nsStyleTransformMatrix::ShearArray shear2{0.0f, 0.0f, 0.0f};
    1183           0 :   Decompose2DMatrix(aMatrix2, scale2, shear2, rotate2, translate2);
    1184             : 
    1185             :   // Note:
    1186             :   // 1. Shear factor is the tangent value of shear angle, so we need to
    1187             :   //    call atan() to get the angle. For 2D transform, we only have XYSHEAR.
    1188             :   // 2. The quaternion vector of the decomposed 2d matrix is got by
    1189             :   //    "gfxQuaternion(0, 0, sin(rotate/2), cos(rotate/2))"
    1190             :   //                         ^^^^^^^^^^^^^  ^^^^^^^^^^^^^
    1191             :   //                               z              w
    1192             :   //    Therefore, we can get the rotate angle by 2 * atan2f(z, w).
    1193             :   //
    1194             :   //    However, we can also get the rotate angle by the inner product of
    1195             :   //    two quaternion vectors, just as what we do for eCSSKeyword_rotate3d.
    1196             :   //    e.g.
    1197             :   //      rotate3d(0, 0, 1, 60deg)  =>  rotate3d(0, 0, 1, 120deg);
    1198             :   //      quaternion 1: (0, 0, sin(30deg), cos(30deg)) = (0, 0, 1/2, sqrt(3)/2)
    1199             :   //      quaternion 2: (0, 0, sin(60deg), cos(60deg)) = (0, 0, sqrt(3)/2, 1/2)
    1200             :   //      inner product:  sqrt(3)/4 + sqrt(3)/4 = sqrt(3)/2
    1201             :   //      Finally, the rotate angle: 2 * acos(sqrt(3)/2) = 60deg
    1202             :   //
    1203             :   //    I think doing atan() may be faster than doing inner product together
    1204             :   //    with acos(), so let's adopt atan2f().
    1205           0 :   const Point3D diffTranslate = translate2 - translate1;
    1206           0 :   const Point3D diffScale = scale2 - scale1;
    1207           0 :   const double diffShear = atan(shear2[ShearType::XYSHEAR]) -
    1208           0 :                            atan(shear1[ShearType::XYSHEAR]);
    1209           0 :   const double diffRotate = 2.0 * (atan2f(rotate2.z, rotate2.w) -
    1210           0 :                                    atan2f(rotate1.z, rotate1.w));
    1211             :   // Returns the sum of squares because we will take a square root in
    1212             :   // ComputeTransformListDistance.
    1213           0 :   return diffTranslate.DotProduct(diffTranslate) +
    1214           0 :          diffScale.DotProduct(diffScale) +
    1215           0 :          diffRotate * diffRotate +
    1216           0 :          diffShear * diffShear;
    1217             : }
    1218             : 
    1219             : static double
    1220           0 : ComputeTransform3DMatrixDistance(const Matrix4x4& aMatrix1,
    1221             :                                  const Matrix4x4& aMatrix2)
    1222             : {
    1223           0 :   Point3D scale1(1, 1, 1);
    1224           0 :   Point3D translate1;
    1225           0 :   Point4D perspective1(0, 0, 0, 1);
    1226           0 :   gfxQuaternion rotate1;
    1227           0 :   nsStyleTransformMatrix::ShearArray shear1{0.0f, 0.0f, 0.0f};
    1228             :   Decompose3DMatrix(aMatrix1, scale1, shear1, rotate1, translate1,
    1229           0 :                     perspective1);
    1230             : 
    1231           0 :   Point3D scale2(1, 1, 1);
    1232           0 :   Point3D translate2;
    1233           0 :   Point4D perspective2(0, 0, 0, 1);
    1234           0 :   gfxQuaternion rotate2;
    1235           0 :   nsStyleTransformMatrix::ShearArray shear2{0.0f, 0.0f, 0.0f};
    1236             :   Decompose3DMatrix(aMatrix2, scale2, shear2, rotate2, translate2,
    1237           0 :                     perspective2);
    1238             : 
    1239             :   // Note:
    1240             :   // 1. Shear factor is the tangent value of shear angle, so we need to
    1241             :   //    call atan() to get the angle.
    1242             :   // 2. We use the same way to get the rotate angle of two quaternion vectors as
    1243             :   //    what we do for rotate3d.
    1244           0 :   const Point3D diffTranslate = translate2 - translate1;
    1245           0 :   const Point3D diffScale = scale2 - scale1;
    1246           0 :   const Point3D diffShear(atan(shear2[ShearType::XYSHEAR]) -
    1247           0 :                             atan(shear1[ShearType::XYSHEAR]),
    1248           0 :                           atan(shear2[ShearType::XZSHEAR]) -
    1249           0 :                             atan(shear1[ShearType::XZSHEAR]),
    1250           0 :                           atan(shear2[ShearType::YZSHEAR]) -
    1251           0 :                             atan(shear1[ShearType::YZSHEAR]));
    1252           0 :   const Point4D diffPerspective = perspective2 - perspective1;
    1253           0 :   const double dot = clamped(rotate1.DotProduct(rotate2), -1.0, 1.0);
    1254           0 :   const double diffRotate = 2.0 * acos(dot);
    1255             :   // Returns the sum of squares because we will take a square root in
    1256             :   // ComputeTransformListDistance.
    1257           0 :   return diffTranslate.DotProduct(diffTranslate) +
    1258           0 :          diffScale.DotProduct(diffScale) +
    1259           0 :          diffPerspective.DotProduct(diffPerspective) +
    1260           0 :          diffShear.DotProduct(diffShear) +
    1261           0 :          diffRotate * diffRotate;
    1262             : }
    1263             : 
    1264             : static double
    1265           0 : ComputeTransformDistance(nsCSSValue::Array* aArray1,
    1266             :                          nsCSSValue::Array* aArray2)
    1267             : {
    1268           0 :   MOZ_ASSERT(aArray1, "aArray1 should be non-null.");
    1269           0 :   MOZ_ASSERT(aArray2, "aArray2 should be non-null.");
    1270             : 
    1271             :   // Normalize translate and scale functions to equivalent "translate3d" and
    1272             :   // "scale3d" functions.
    1273           0 :   RefPtr<nsCSSValue::Array> a1 = ToPrimitive(aArray1),
    1274           0 :                             a2 = ToPrimitive(aArray2);
    1275           0 :   nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
    1276           0 :   MOZ_ASSERT(nsStyleTransformMatrix::TransformFunctionOf(a2) == tfunc);
    1277             : 
    1278           0 :   double distance = 0.0;
    1279           0 :   switch (tfunc) {
    1280             :     case eCSSKeyword_translate3d: {
    1281           0 :       MOZ_ASSERT(a1->Count() == 4, "unexpected count");
    1282           0 :       MOZ_ASSERT(a2->Count() == 4, "unexpected count");
    1283             : 
    1284           0 :       nsCSSValue x, y, z;
    1285           0 :       AddTransformTranslate(1.0, a2->Item(1), -1.0, a1->Item(1), x);
    1286           0 :       AddTransformTranslate(1.0, a2->Item(2), -1.0, a1->Item(2), y);
    1287           0 :       AddTransformTranslate(1.0, a2->Item(3), -1.0, a1->Item(3), z);
    1288             :       // Drop percent part because we only compute distance by computed values.
    1289           0 :       double c1 = ExtractCalcValue(x).mLength;
    1290           0 :       double c2 = ExtractCalcValue(y).mLength;
    1291           0 :       double c3 = z.GetFloatValue();
    1292           0 :       distance = c1 * c1 + c2 * c2 + c3 * c3;
    1293           0 :       break;
    1294             :     }
    1295             :     case eCSSKeyword_scale3d: {
    1296           0 :       MOZ_ASSERT(a1->Count() == 4, "unexpected count");
    1297           0 :       MOZ_ASSERT(a2->Count() == 4, "unexpected count");
    1298             : 
    1299             :       auto ComputeScaleDiff = [](const nsCSSValue& aValue1,
    1300           0 :                                  const nsCSSValue& aValue2) {
    1301           0 :         float v1 = aValue1.GetFloatValue();
    1302           0 :         float v2 = aValue2.GetFloatValue();
    1303           0 :         return EnsureNotNan(v2 - v1);
    1304             :       };
    1305             : 
    1306           0 :       double c1 = ComputeScaleDiff(a1->Item(1), a2->Item(1));
    1307           0 :       double c2 = ComputeScaleDiff(a1->Item(2), a2->Item(2));
    1308           0 :       double c3 = ComputeScaleDiff(a1->Item(3), a2->Item(3));
    1309           0 :       distance = c1 * c1 + c2 * c2 + c3 * c3;
    1310           0 :       break;
    1311             :     }
    1312             :     case eCSSKeyword_skew: {
    1313           0 :       MOZ_ASSERT(a1->Count() == 2 || a1->Count() == 3, "unexpected count");
    1314           0 :       MOZ_ASSERT(a2->Count() == 2 || a2->Count() == 3, "unexpected count");
    1315             : 
    1316           0 :       const nsCSSValue zero(0.0f, eCSSUnit_Radian);
    1317           0 :       nsCSSValue x, y;
    1318           0 :       AddCSSValueAngle(1.0, a2->Item(1), -1.0, a1->Item(1), x);
    1319           0 :       AddCSSValueAngle(1.0, a2->Count() == 3 ? a2->Item(2) : zero,
    1320           0 :                       -1.0, a1->Count() == 3 ? a1->Item(2) : zero,
    1321           0 :                        y);
    1322           0 :       distance = x.GetAngleValueInRadians() * x.GetAngleValueInRadians() +
    1323           0 :                  y.GetAngleValueInRadians() * y.GetAngleValueInRadians();
    1324           0 :       break;
    1325             :     }
    1326             :     case eCSSKeyword_skewx:
    1327             :     case eCSSKeyword_skewy:
    1328             :     case eCSSKeyword_rotate:
    1329             :     case eCSSKeyword_rotatex:
    1330             :     case eCSSKeyword_rotatey:
    1331             :     case eCSSKeyword_rotatez: {
    1332           0 :       MOZ_ASSERT(a1->Count() == 2, "unexpected count");
    1333           0 :       MOZ_ASSERT(a2->Count() == 2, "unexpected count");
    1334             : 
    1335           0 :       nsCSSValue angle;
    1336           0 :       AddCSSValueAngle(1.0, a2->Item(1), -1.0, a1->Item(1), angle);
    1337           0 :       distance = angle.GetAngleValueInRadians() *
    1338           0 :                  angle.GetAngleValueInRadians();
    1339           0 :       break;
    1340             :     }
    1341             :     case eCSSKeyword_rotate3d: {
    1342           0 :       MOZ_ASSERT(a1->Count() == 5, "unexpected count");
    1343           0 :       MOZ_ASSERT(a2->Count() == 5, "unexpected count");
    1344             : 
    1345           0 :       Point3D vector1(a1->Item(1).GetFloatValue(),
    1346           0 :                       a1->Item(2).GetFloatValue(),
    1347           0 :                       a1->Item(3).GetFloatValue());
    1348           0 :       vector1.Normalize();
    1349           0 :       Point3D vector2(a2->Item(1).GetFloatValue(),
    1350           0 :                       a2->Item(2).GetFloatValue(),
    1351           0 :                       a2->Item(3).GetFloatValue());
    1352           0 :       vector2.Normalize();
    1353             : 
    1354           0 :       if (vector1 == vector2) {
    1355             :         // Handle rotate3d with matched (normalized) vectors.
    1356           0 :         nsCSSValue angle;
    1357           0 :         AddCSSValueAngle(1.0, a2->Item(4), -1.0, a1->Item(4), angle);
    1358           0 :         distance = angle.GetAngleValueInRadians() *
    1359           0 :                    angle.GetAngleValueInRadians();
    1360             :       } else {
    1361             :         // Use quaternion vectors to get the angle difference. Both q1 and q2
    1362             :         // are unit vectors, so we can get their angle difference by
    1363             :         // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2.
    1364           0 :         gfxQuaternion q1(vector1, a1->Item(4).GetAngleValueInRadians());
    1365           0 :         gfxQuaternion q2(vector2, a2->Item(4).GetAngleValueInRadians());
    1366           0 :         distance = 2.0 * acos(clamped(q1.DotProduct(q2), -1.0, 1.0));
    1367           0 :         distance = distance * distance;
    1368             :       }
    1369           0 :       break;
    1370             :     }
    1371             :     case eCSSKeyword_perspective: {
    1372           0 :       MOZ_ASSERT(a1->Count() == 2, "unexpected count");
    1373           0 :       MOZ_ASSERT(a2->Count() == 2, "unexpected count");
    1374             : 
    1375             :       // We convert a perspective function into an equivalent matrix3d, and
    1376             :       // then do matrix decomposition to get the distance.
    1377             :       // Why don't we just subtract one perspective depth from the other?
    1378             :       // I think it's better to follow the logic of our interpolation,
    1379             :       // which does linear interpolation between two decomposed perspective
    1380             :       // vectors.
    1381             :       // e.g.
    1382             :       // Do interpolation between perspective(100px) and perspective(1000px).
    1383             :       //   1) Convert them into matrix3d, and then do matrix decomposition:
    1384             :       //      perspective vector 1: perspective(0, 0, -1/100, 1);
    1385             :       //      perspective vector 2: perspective(0, 0, -1/1000, 1);
    1386             :       //   2) Do linear interpolation between these two vectors.
    1387             :       // Therefore, we use the same rule to get the distance as what we do for
    1388             :       // matrix3d.
    1389             : 
    1390             :       using nsStyleTransformMatrix::ApplyPerspectiveToMatrix;
    1391           0 :       Matrix4x4 m1;
    1392           0 :       ApplyPerspectiveToMatrix(m1, a1->Item(1).GetFloatValue());
    1393           0 :       Matrix4x4 m2;
    1394           0 :       ApplyPerspectiveToMatrix(m2, a2->Item(1).GetFloatValue());
    1395             : 
    1396           0 :       distance = ComputeTransform3DMatrixDistance(m1, m2);
    1397           0 :       break;
    1398             :     }
    1399             :     case eCSSKeyword_matrix: {
    1400           0 :       MOZ_ASSERT(a1->Count() == 7, "unexpected count");
    1401           0 :       MOZ_ASSERT(a2->Count() == 7, "unexpected count");
    1402             : 
    1403             :       distance = ComputeTransform2DMatrixDistance(
    1404           0 :         nsStyleTransformMatrix::CSSValueArrayTo2DMatrix(a1),
    1405           0 :         nsStyleTransformMatrix::CSSValueArrayTo2DMatrix(a2));
    1406           0 :       break;
    1407             :     }
    1408             :     case eCSSKeyword_matrix3d: {
    1409           0 :       MOZ_ASSERT(a1->Count() == 17, "unexpected count");
    1410           0 :       MOZ_ASSERT(a2->Count() == 17, "unexpected count");
    1411             : 
    1412             :       distance = ComputeTransform3DMatrixDistance(
    1413           0 :         nsStyleTransformMatrix::CSSValueArrayTo3DMatrix(a1),
    1414           0 :         nsStyleTransformMatrix::CSSValueArrayTo3DMatrix(a2));
    1415           0 :       break;
    1416             :     }
    1417             :     case eCSSKeyword_interpolatematrix:
    1418             :     case eCSSKeyword_accumulatematrix:
    1419             :     default:
    1420           0 :       MOZ_ASSERT_UNREACHABLE("Unsupported transform function");
    1421             :       break;
    1422             :   }
    1423           0 :   return distance;
    1424             : }
    1425             : 
    1426             : static double
    1427           0 : ComputeTransformListDistance(const nsCSSValueList* aList1,
    1428             :                              const nsCSSValueList* aList2)
    1429             : {
    1430           0 :   MOZ_ASSERT(aList1, "aList1 should be non-null.");
    1431           0 :   MOZ_ASSERT(aList2, "aList2 should be non-null.");
    1432             : 
    1433           0 :   double distance = 0.0;
    1434           0 :   do {
    1435           0 :     distance += ComputeTransformDistance(aList1->mValue.GetArrayValue(),
    1436             :                                          aList2->mValue.GetArrayValue());
    1437           0 :     aList1 = aList1->mNext;
    1438           0 :     aList2 = aList2->mNext;
    1439           0 :     MOZ_ASSERT(!aList1 == !aList2,
    1440             :                "aList1 and aList2 should have the same length.");
    1441           0 :   } while (aList1);
    1442           0 :   return sqrt(distance);
    1443             : }
    1444             : 
    1445             : static double
    1446           0 : ComputeMismatchedTransfromListDistance(const nsCSSValueList* aList1,
    1447             :                                        const nsCSSValueList* aList2,
    1448             :                                        nsStyleContext* aStyleContext)
    1449             : {
    1450             :   // We need nsStyleContext and nsPresContext to compute calc() values while
    1451             :   // processing the translate part of transforms.
    1452           0 :   if (!aStyleContext) {
    1453           0 :     return 0.0;
    1454             :   }
    1455             : 
    1456           0 :   RuleNodeCacheConditions dontCare;
    1457             :   bool dontCareBool;
    1458           0 :   nsStyleTransformMatrix::TransformReferenceBox emptyRefBox;
    1459             : 
    1460             :   Matrix4x4 m1 = nsStyleTransformMatrix::ReadTransforms(
    1461             :                    aList1,
    1462             :                    aStyleContext,
    1463             :                    aStyleContext->PresContext(),
    1464             :                    dontCare,
    1465             :                    emptyRefBox,
    1466           0 :                    nsPresContext::AppUnitsPerCSSPixel(),
    1467           0 :                    &dontCareBool);
    1468             :   Matrix4x4 m2 = nsStyleTransformMatrix::ReadTransforms(
    1469             :                    aList2,
    1470             :                    aStyleContext,
    1471             :                    aStyleContext->PresContext(),
    1472             :                    dontCare,
    1473             :                    emptyRefBox,
    1474           0 :                    nsPresContext::AppUnitsPerCSSPixel(),
    1475           0 :                    &dontCareBool);
    1476           0 :   return sqrt(ComputeTransform3DMatrixDistance(m1, m2));
    1477             : }
    1478             : 
    1479             : bool
    1480           0 : StyleAnimationValue::ComputeDistance(nsCSSPropertyID aProperty,
    1481             :                                      const StyleAnimationValue& aStartValue,
    1482             :                                      const StyleAnimationValue& aEndValue,
    1483             :                                      nsStyleContext* aStyleContext,
    1484             :                                      double& aDistance)
    1485             : {
    1486             :   Unit commonUnit =
    1487           0 :     GetCommonUnit(aProperty, aStartValue.GetUnit(), aEndValue.GetUnit());
    1488             : 
    1489           0 :   switch (commonUnit) {
    1490             :     case eUnit_Null:
    1491             :     case eUnit_Auto:
    1492             :     case eUnit_None:
    1493             :     case eUnit_Normal:
    1494             :     case eUnit_UnparsedString:
    1495             :     case eUnit_URL:
    1496             :     case eUnit_DiscreteCSSValue:
    1497           0 :       return false;
    1498             : 
    1499             :     case eUnit_Enumerated:
    1500           0 :       switch (aProperty) {
    1501             :         case eCSSProperty_font_stretch: {
    1502             :           // just like eUnit_Integer.
    1503           0 :           int32_t startInt = aStartValue.GetIntValue();
    1504           0 :           int32_t endInt = aEndValue.GetIntValue();
    1505           0 :           aDistance = Abs(endInt - startInt);
    1506           0 :           return true;
    1507             :         }
    1508             :         default:
    1509           0 :           return false;
    1510             :       }
    1511             :    case eUnit_Visibility: {
    1512           0 :       int32_t startEnum = aStartValue.GetIntValue();
    1513           0 :       int32_t endEnum = aEndValue.GetIntValue();
    1514           0 :       if (startEnum == endEnum) {
    1515           0 :         aDistance = 0;
    1516           0 :         return true;
    1517             :       }
    1518           0 :       if ((startEnum == NS_STYLE_VISIBILITY_VISIBLE) ==
    1519             :           (endEnum == NS_STYLE_VISIBILITY_VISIBLE)) {
    1520           0 :         return false;
    1521             :       }
    1522           0 :       aDistance = 1;
    1523           0 :       return true;
    1524             :     }
    1525             :     case eUnit_Integer: {
    1526           0 :       int32_t startInt = aStartValue.GetIntValue();
    1527           0 :       int32_t endInt = aEndValue.GetIntValue();
    1528           0 :       aDistance = Abs(double(endInt) - double(startInt));
    1529           0 :       return true;
    1530             :     }
    1531             :     case eUnit_Coord: {
    1532           0 :       nscoord startCoord = aStartValue.GetCoordValue();
    1533           0 :       nscoord endCoord = aEndValue.GetCoordValue();
    1534           0 :       aDistance = Abs(double(endCoord) - double(startCoord));
    1535           0 :       return true;
    1536             :     }
    1537             :     case eUnit_Percent: {
    1538           0 :       float startPct = aStartValue.GetPercentValue();
    1539           0 :       float endPct = aEndValue.GetPercentValue();
    1540           0 :       aDistance = Abs(double(endPct) - double(startPct));
    1541           0 :       return true;
    1542             :     }
    1543             :     case eUnit_Float: {
    1544           0 :       float startFloat = aStartValue.GetFloatValue();
    1545           0 :       float endFloat = aEndValue.GetFloatValue();
    1546           0 :       aDistance = Abs(double(endFloat) - double(startFloat));
    1547           0 :       return true;
    1548             :     }
    1549             :     case eUnit_Color: {
    1550           0 :       aDistance = ComputeColorDistance(ExtractColor(aStartValue),
    1551           0 :                                        ExtractColor(aEndValue));
    1552           0 :       return true;
    1553             :     }
    1554             :     case eUnit_CurrentColor: {
    1555           0 :       aDistance = 0;
    1556           0 :       return true;
    1557             :     }
    1558             :     case eUnit_ComplexColor: {
    1559           0 :       ComplexColorData color1 = ExtractComplexColor(aStartValue);
    1560           0 :       ComplexColorData color2 = ExtractComplexColor(aEndValue);
    1561             :       // Common case is interpolating between a color and a currentcolor
    1562           0 :       if (color1.IsNumericColor() && color2.IsCurrentColor()) {
    1563           0 :         double dist = ComputeColorDistance(color1.mColor, NS_RGBA(0, 0, 0, 0));
    1564           0 :         aDistance = sqrt(dist * dist + 1);
    1565           0 :         return true;
    1566             :       }
    1567           0 :       if (color1.IsCurrentColor() && color2.IsNumericColor()) {
    1568           0 :         double dist = ComputeColorDistance(NS_RGBA(0, 0, 0, 0), color2.mColor);
    1569           0 :         aDistance = sqrt(dist * dist + 1);
    1570           0 :         return true;
    1571             :       }
    1572             :       // If we ever reach here, we may want to use the code in
    1573             :       // bug 1299741 comment 79 to compute it.
    1574           0 :       MOZ_ASSERT_UNREACHABLE("We shouldn't get here as we only call "
    1575             :                              "ComputeDistance on pre-interpolation values");
    1576             :       aDistance = 0.0;
    1577             :       return true;
    1578             :     }
    1579             :     case eUnit_Calc: {
    1580           0 :       PixelCalcValue v1 = ExtractCalcValue(aStartValue);
    1581           0 :       PixelCalcValue v2 = ExtractCalcValue(aEndValue);
    1582           0 :       float difflen = v2.mLength - v1.mLength;
    1583           0 :       float diffpct = v2.mPercent - v1.mPercent;
    1584           0 :       aDistance = sqrt(difflen * difflen + diffpct * diffpct);
    1585           0 :       return true;
    1586             :     }
    1587             :     case eUnit_ObjectPosition: {
    1588           0 :       const nsCSSValue* position1 = aStartValue.GetCSSValueValue();
    1589           0 :       const nsCSSValue* position2 = aEndValue.GetCSSValueValue();
    1590             :       double squareDistance =
    1591             :         CalcPositionSquareDistance(*position1,
    1592           0 :                                    *position2);
    1593           0 :       aDistance = sqrt(squareDistance);
    1594           0 :       return true;
    1595             :     }
    1596             :     case eUnit_CSSValuePair: {
    1597           0 :       const nsCSSValuePair *pair1 = aStartValue.GetCSSValuePairValue();
    1598           0 :       const nsCSSValuePair *pair2 = aEndValue.GetCSSValuePairValue();
    1599             :       nsCSSUnit unit[2];
    1600           0 :       unit[0] = GetCommonUnit(aProperty, pair1->mXValue.GetUnit(),
    1601             :                               pair2->mXValue.GetUnit());
    1602           0 :       unit[1] = GetCommonUnit(aProperty, pair1->mYValue.GetUnit(),
    1603             :                               pair2->mYValue.GetUnit());
    1604           0 :       if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
    1605           0 :           unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) {
    1606           0 :         return false;
    1607             :       }
    1608             : 
    1609           0 :       double squareDistance = 0.0;
    1610             :       static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
    1611             :         &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
    1612             :       };
    1613           0 :       for (uint32_t i = 0; i < 2; ++i) {
    1614           0 :         nsCSSValue nsCSSValuePair::*member = pairValues[i];
    1615             :         double diffsquared;
    1616           0 :         switch (unit[i]) {
    1617             :           case eCSSUnit_Pixel: {
    1618           0 :             float diff = (pair1->*member).GetFloatValue() -
    1619           0 :                          (pair2->*member).GetFloatValue();
    1620           0 :             diffsquared = diff * diff;
    1621           0 :             break;
    1622             :           }
    1623             :           case eCSSUnit_Percent: {
    1624           0 :             float diff = (pair1->*member).GetPercentValue() -
    1625           0 :                          (pair2->*member).GetPercentValue();
    1626           0 :             diffsquared = diff * diff;
    1627           0 :             break;
    1628             :           }
    1629             :           case eCSSUnit_Calc: {
    1630           0 :             PixelCalcValue v1 = ExtractCalcValue(pair1->*member);
    1631           0 :             PixelCalcValue v2 = ExtractCalcValue(pair2->*member);
    1632           0 :             float difflen = v2.mLength - v1.mLength;
    1633           0 :             float diffpct = v2.mPercent - v1.mPercent;
    1634           0 :             diffsquared = difflen * difflen + diffpct * diffpct;
    1635           0 :             break;
    1636             :           }
    1637             :           default:
    1638           0 :             MOZ_ASSERT(false, "unexpected unit");
    1639             :             return false;
    1640             :         }
    1641           0 :         squareDistance += diffsquared;
    1642             :       }
    1643             : 
    1644           0 :       aDistance = sqrt(squareDistance);
    1645           0 :       return true;
    1646             :     }
    1647             :     case eUnit_CSSValueTriplet: {
    1648           0 :       const nsCSSValueTriplet *triplet1 = aStartValue.GetCSSValueTripletValue();
    1649           0 :       const nsCSSValueTriplet *triplet2 = aEndValue.GetCSSValueTripletValue();
    1650             :       nsCSSUnit unit[3];
    1651           0 :       unit[0] = GetCommonUnit(aProperty, triplet1->mXValue.GetUnit(),
    1652             :                               triplet2->mXValue.GetUnit());
    1653           0 :       unit[1] = GetCommonUnit(aProperty, triplet1->mYValue.GetUnit(),
    1654             :                               triplet2->mYValue.GetUnit());
    1655           0 :       unit[2] = GetCommonUnit(aProperty, triplet1->mZValue.GetUnit(),
    1656             :                               triplet2->mZValue.GetUnit());
    1657           0 :       if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
    1658           0 :           unit[2] == eCSSUnit_Null) {
    1659           0 :         return false;
    1660             :       }
    1661             : 
    1662           0 :       double squareDistance = 0.0;
    1663             :       static nsCSSValue nsCSSValueTriplet::* const pairValues[3] = {
    1664             :         &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
    1665             :       };
    1666           0 :       for (uint32_t i = 0; i < 3; ++i) {
    1667           0 :         nsCSSValue nsCSSValueTriplet::*member = pairValues[i];
    1668             :         double diffsquared;
    1669           0 :         switch (unit[i]) {
    1670             :           case eCSSUnit_Pixel: {
    1671           0 :             float diff = (triplet1->*member).GetFloatValue() -
    1672           0 :                          (triplet2->*member).GetFloatValue();
    1673           0 :             diffsquared = diff * diff;
    1674           0 :             break;
    1675             :           }
    1676             :           case eCSSUnit_Percent: {
    1677           0 :             float diff = (triplet1->*member).GetPercentValue() -
    1678           0 :                          (triplet2->*member).GetPercentValue();
    1679           0 :              diffsquared = diff * diff;
    1680           0 :              break;
    1681             :           }
    1682             :           case eCSSUnit_Calc: {
    1683           0 :             PixelCalcValue v1 = ExtractCalcValue(triplet1->*member);
    1684           0 :             PixelCalcValue v2 = ExtractCalcValue(triplet2->*member);
    1685           0 :             float difflen = v2.mLength - v1.mLength;
    1686           0 :             float diffpct = v2.mPercent - v1.mPercent;
    1687           0 :             diffsquared = difflen * difflen + diffpct * diffpct;
    1688           0 :             break;
    1689             :           }
    1690             :           case eCSSUnit_Null:
    1691           0 :             diffsquared = 0;
    1692           0 :             break;
    1693             :           default:
    1694           0 :             MOZ_ASSERT(false, "unexpected unit");
    1695             :             return false;
    1696             :         }
    1697           0 :         squareDistance += diffsquared;
    1698             :       }
    1699             : 
    1700           0 :       aDistance = sqrt(squareDistance);
    1701           0 :       return true;
    1702             :     }
    1703             :     case eUnit_CSSRect: {
    1704           0 :       const nsCSSRect *rect1 = aStartValue.GetCSSRectValue();
    1705           0 :       const nsCSSRect *rect2 = aEndValue.GetCSSRectValue();
    1706           0 :       if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
    1707           0 :           rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
    1708           0 :           rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
    1709           0 :           rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
    1710             :         // At least until we have calc()
    1711           0 :         return false;
    1712             :       }
    1713             : 
    1714           0 :       double squareDistance = 0.0;
    1715           0 :       for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) {
    1716           0 :         nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
    1717           0 :         MOZ_ASSERT((rect1->*member).GetUnit() == (rect2->*member).GetUnit(),
    1718             :                    "should have returned above");
    1719             :         double diff;
    1720           0 :         switch ((rect1->*member).GetUnit()) {
    1721             :           case eCSSUnit_Pixel:
    1722           0 :             diff = (rect1->*member).GetFloatValue() -
    1723           0 :                    (rect2->*member).GetFloatValue();
    1724           0 :             break;
    1725             :           case eCSSUnit_Auto:
    1726           0 :             diff = 0;
    1727           0 :             break;
    1728             :           default:
    1729           0 :             MOZ_ASSERT(false, "unexpected unit");
    1730             :             return false;
    1731             :         }
    1732           0 :         squareDistance += diff * diff;
    1733             :       }
    1734             : 
    1735           0 :       aDistance = sqrt(squareDistance);
    1736           0 :       return true;
    1737             :     }
    1738             :     case eUnit_Dasharray: {
    1739             :       // NOTE: This produces results on substantially different scales
    1740             :       // for length values and percentage values, which might even be
    1741             :       // mixed in the same property value.  This means the result isn't
    1742             :       // particularly useful for paced animation.
    1743             : 
    1744             :       // Call AddWeighted to make us lists of the same length.
    1745           0 :       StyleAnimationValue normValue1, normValue2;
    1746           0 :       if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
    1747           0 :                        normValue1) ||
    1748           0 :           !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
    1749             :                        normValue2)) {
    1750           0 :         return false;
    1751             :       }
    1752             : 
    1753           0 :       double squareDistance = 0.0;
    1754           0 :       const nsCSSValueList *list1 = normValue1.GetCSSValueListValue();
    1755           0 :       const nsCSSValueList *list2 = normValue2.GetCSSValueListValue();
    1756             : 
    1757           0 :       MOZ_ASSERT(!list1 == !list2, "lists should be same length");
    1758           0 :       while (list1) {
    1759           0 :         const nsCSSValue &val1 = list1->mValue;
    1760           0 :         const nsCSSValue &val2 = list2->mValue;
    1761             : 
    1762           0 :         MOZ_ASSERT(val1.GetUnit() == val2.GetUnit(),
    1763             :                    "unit match should be assured by AddWeighted");
    1764             :         double diff;
    1765           0 :         switch (val1.GetUnit()) {
    1766             :           case eCSSUnit_Percent:
    1767           0 :             diff = val1.GetPercentValue() - val2.GetPercentValue();
    1768           0 :             break;
    1769             :           case eCSSUnit_Number:
    1770           0 :             diff = val1.GetFloatValue() - val2.GetFloatValue();
    1771           0 :             break;
    1772             :           default:
    1773           0 :             MOZ_ASSERT(false, "unexpected unit");
    1774             :             return false;
    1775             :         }
    1776           0 :         squareDistance += diff * diff;
    1777             : 
    1778           0 :         list1 = list1->mNext;
    1779           0 :         list2 = list2->mNext;
    1780           0 :         MOZ_ASSERT(!list1 == !list2, "lists should be same length");
    1781             :       }
    1782             : 
    1783           0 :       aDistance = sqrt(squareDistance);
    1784           0 :       return true;
    1785             :     }
    1786             :     case eUnit_Shadow: {
    1787             :       // Call AddWeighted to make us lists of the same length.
    1788           0 :       StyleAnimationValue normValue1, normValue2;
    1789           0 :       if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
    1790           0 :                        normValue1) ||
    1791           0 :           !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
    1792             :                        normValue2)) {
    1793           0 :         return false;
    1794             :       }
    1795             : 
    1796           0 :       const nsCSSValueList* shadow1 = normValue1.GetCSSValueListValue();
    1797           0 :       const nsCSSValueList* shadow2 = normValue2.GetCSSValueListValue();
    1798             : 
    1799           0 :       aDistance = 0.0;
    1800           0 :       MOZ_ASSERT(!shadow1 == !shadow2, "lists should be same length");
    1801           0 :       while (shadow1) {
    1802           0 :         double squareDistance = 0.0;
    1803           0 :         if (!ComputeSingleShadowSquareDistance(shadow1, shadow2,
    1804             :                                                squareDistance,
    1805             :                                                aProperty)) {
    1806           0 :           NS_ERROR("Unexpected ComputeSingleShadowSquareDistance failure; "
    1807             :                    "why didn't we fail earlier, in AddWeighted calls above?");
    1808             :         }
    1809           0 :         aDistance += squareDistance; // cumulative distance^2; sqrt()'d below.
    1810             : 
    1811           0 :         shadow1 = shadow1->mNext;
    1812           0 :         shadow2 = shadow2->mNext;
    1813           0 :         MOZ_ASSERT(!shadow1 == !shadow2, "lists should be same length");
    1814             :       }
    1815           0 :       aDistance = sqrt(aDistance);
    1816           0 :       return true;
    1817             :     }
    1818             :     case eUnit_Shape:
    1819             :       return ComputeShapeDistance(aProperty,
    1820           0 :                                   aStartValue.GetCSSValueArrayValue(),
    1821           0 :                                   aEndValue.GetCSSValueArrayValue(),
    1822           0 :                                   aDistance);
    1823             : 
    1824             :     case eUnit_Filter:
    1825           0 :       return ComputeFilterListDistance(aStartValue.GetCSSValueListValue(),
    1826           0 :                                        aEndValue.GetCSSValueListValue(),
    1827           0 :                                        aDistance);
    1828             : 
    1829             :     case eUnit_Transform: {
    1830             :       // FIXME: We don't have an official spec to define the distance of
    1831             :       // two transform lists, but paced spacing (defined in Web Animations API)
    1832             :       // needs this, so we implement this according to the concept of the
    1833             :       // interpolation of two transform lists.
    1834             :       // Issue: https://www.w3.org/TR/web-animations-1/#issue-789f9fd1
    1835             : 
    1836             :       const nsCSSValueList* list1 =
    1837           0 :         aStartValue.GetCSSValueSharedListValue()->mHead;
    1838             :       const nsCSSValueList* list2 =
    1839           0 :         aEndValue.GetCSSValueSharedListValue()->mHead;
    1840           0 :       MOZ_ASSERT(list1);
    1841           0 :       MOZ_ASSERT(list2);
    1842             : 
    1843           0 :       if (list1->mValue.GetUnit() == eCSSUnit_None &&
    1844           0 :           list2->mValue.GetUnit() == eCSSUnit_None) {
    1845             :         // Both none, nothing happens.
    1846           0 :         aDistance = 0.0;
    1847           0 :       } else if (list1->mValue.GetUnit() == eCSSUnit_None) {
    1848           0 :         nsAutoPtr<nsCSSValueList> none(AddTransformLists(0, list2, 0, list2));
    1849           0 :         aDistance = ComputeTransformListDistance(none, list2);
    1850           0 :       } else if (list2->mValue.GetUnit() == eCSSUnit_None) {
    1851           0 :         nsAutoPtr<nsCSSValueList> none(AddTransformLists(0, list1, 0, list1));
    1852           0 :         aDistance = ComputeTransformListDistance(list1, none);
    1853           0 :       } else if (TransformFunctionListsMatch(list1, list2)) {
    1854           0 :         aDistance = ComputeTransformListDistance(list1, list2);
    1855             :       } else {
    1856           0 :         aDistance =
    1857           0 :           ComputeMismatchedTransfromListDistance(list1, list2, aStyleContext);
    1858             :       }
    1859           0 :       return true;
    1860             :     }
    1861             :     case eUnit_BackgroundPositionCoord: {
    1862           0 :       const nsCSSValueList *position1 = aStartValue.GetCSSValueListValue();
    1863           0 :       const nsCSSValueList *position2 = aEndValue.GetCSSValueListValue();
    1864             : 
    1865           0 :       double squareDistance = 0.0;
    1866           0 :       MOZ_ASSERT(!position1 == !position2, "lists should be same length");
    1867             : 
    1868           0 :       while (position1 && position2) {
    1869           0 :         squareDistance += CalcPositionCoordSquareDistance(position1->mValue,
    1870             :                                                           position2->mValue);
    1871           0 :         position1 = position1->mNext;
    1872           0 :         position2 = position2->mNext;
    1873             :       }
    1874             :       // fail if lists differ in length.
    1875           0 :       if (position1 || position2) {
    1876           0 :         return false;
    1877             :       }
    1878             : 
    1879           0 :       aDistance = sqrt(squareDistance);
    1880           0 :       return true;
    1881             :     }
    1882             :     case eUnit_CSSValuePairList: {
    1883           0 :       const nsCSSValuePairList *list1 = aStartValue.GetCSSValuePairListValue();
    1884           0 :       const nsCSSValuePairList *list2 = aEndValue.GetCSSValuePairListValue();
    1885           0 :       double squareDistance = 0.0;
    1886           0 :       do {
    1887             :         static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
    1888             :           &nsCSSValuePairList::mXValue,
    1889             :           &nsCSSValuePairList::mYValue,
    1890             :         };
    1891           0 :         for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) {
    1892           0 :           const nsCSSValue &v1 = list1->*(pairListValues[i]);
    1893           0 :           const nsCSSValue &v2 = list2->*(pairListValues[i]);
    1894             :           nsCSSUnit unit =
    1895           0 :             GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
    1896           0 :           if (unit == eCSSUnit_Null) {
    1897           0 :             return false;
    1898             :           }
    1899           0 :           double diffsquared = 0.0;
    1900           0 :           switch (unit) {
    1901             :             case eCSSUnit_Number:
    1902             :             case eCSSUnit_Pixel: {
    1903           0 :               float diff = v1.GetFloatValue() - v2.GetFloatValue();
    1904           0 :               diffsquared = diff * diff;
    1905           0 :               break;
    1906             :             }
    1907             :             case eCSSUnit_Percent: {
    1908           0 :               float diff = v1.GetPercentValue() - v2.GetPercentValue();
    1909           0 :               diffsquared = diff * diff;
    1910           0 :               break;
    1911             :             }
    1912             :             case eCSSUnit_Calc: {
    1913           0 :               PixelCalcValue val1 = ExtractCalcValue(v1);
    1914           0 :               PixelCalcValue val2 = ExtractCalcValue(v2);
    1915           0 :               float difflen = val2.mLength - val1.mLength;
    1916           0 :               float diffpct = val2.mPercent - val1.mPercent;
    1917           0 :               diffsquared = difflen * difflen + diffpct * diffpct;
    1918           0 :               break;
    1919             :             }
    1920             :             default:
    1921           0 :               if (v1 != v2) {
    1922           0 :                 return false;
    1923             :               }
    1924           0 :               break;
    1925             :           }
    1926           0 :           squareDistance += diffsquared;
    1927             :         }
    1928           0 :         list1 = list1->mNext;
    1929           0 :         list2 = list2->mNext;
    1930           0 :       } while (list1 && list2);
    1931           0 :       if (list1 || list2) {
    1932             :         // We can't interpolate lists of different lengths.
    1933           0 :         return false;
    1934             :       }
    1935           0 :       aDistance = sqrt(squareDistance);
    1936           0 :       return true;
    1937             :     }
    1938             :   }
    1939             : 
    1940           0 :   MOZ_ASSERT(false, "Can't compute distance using the given common unit");
    1941             :   return false;
    1942             : }
    1943             : 
    1944             : static inline void
    1945           0 : AddCSSValueNumber(double aCoeff1, const nsCSSValue &aValue1,
    1946             :                   double aCoeff2, const nsCSSValue &aValue2,
    1947             :                   nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
    1948             : {
    1949           0 :   MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
    1950           0 :   MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
    1951           0 :   aResult.SetFloatValue(RestrictValue(aValueRestrictions,
    1952           0 :                                       aCoeff1 * aValue1.GetFloatValue() +
    1953           0 :                                       aCoeff2 * aValue2.GetFloatValue()),
    1954           0 :                         eCSSUnit_Number);
    1955           0 : }
    1956             : 
    1957             : static inline float
    1958           0 : GetNumberOrPercent(const nsCSSValue &aValue)
    1959             : {
    1960           0 :   nsCSSUnit unit = aValue.GetUnit();
    1961           0 :   MOZ_ASSERT(unit == eCSSUnit_Number || unit == eCSSUnit_Percent,
    1962             :              "unexpected unit");
    1963           0 :   return (unit == eCSSUnit_Number) ?
    1964           0 :     aValue.GetFloatValue() : aValue.GetPercentValue();
    1965             : }
    1966             : 
    1967             : static inline void
    1968           0 : AddCSSValuePercentNumber(const uint32_t aValueRestrictions,
    1969             :                          double aCoeff1, const nsCSSValue &aValue1,
    1970             :                          double aCoeff2, const nsCSSValue &aValue2,
    1971             :                          nsCSSValue &aResult, float aInitialVal)
    1972             : {
    1973           0 :   float n1 = GetNumberOrPercent(aValue1);
    1974           0 :   float n2 = GetNumberOrPercent(aValue2);
    1975             : 
    1976             :   // Rather than interpolating aValue1 and aValue2 directly, we
    1977             :   // interpolate their *distances from aInitialVal* (the initial value,
    1978             :   // which is either 1 or 0 for "filter" functions).  This matters in
    1979             :   // cases where aInitialVal is nonzero and the coefficients don't add
    1980             :   // up to 1.  For example, if initialVal is 1, aCoeff1 is 0.5, and
    1981             :   // aCoeff2 is 0, then we'll return the value halfway between 1 and
    1982             :   // aValue1, rather than the value halfway between 0 and aValue1.
    1983             :   // Note that we do something similar in AddTransformScale().
    1984           0 :   float result = (n1 - aInitialVal) * aCoeff1 + (n2 - aInitialVal) * aCoeff2;
    1985           0 :   aResult.SetFloatValue(RestrictValue(aValueRestrictions, result + aInitialVal),
    1986           0 :                         eCSSUnit_Number);
    1987           0 : }
    1988             : 
    1989             : // Multiplies |aValue| color by |aDilutionRatio|.
    1990             : static nscolor
    1991           0 : DiluteColor(const RGBAColorData& aValue, double aDilutionRatio)
    1992             : {
    1993           0 :   float resultA = aValue.mA * aDilutionRatio;
    1994           0 :   return resultA <= 0.0 ? NS_RGBA(0, 0, 0, 0)
    1995           0 :                         : aValue.WithAlpha(resultA).ToColor();
    1996             : }
    1997             : 
    1998             : // Clamped AddWeightedColors.
    1999             : static nscolor
    2000           0 : AddWeightedColorsAndClamp(double aCoeff1, const RGBAColorData& aValue1,
    2001             :                           double aCoeff2, const RGBAColorData& aValue2)
    2002             : {
    2003             :   // We are using AddWeighted() with a zero aCoeff2 for colors to
    2004             :   // pretend AddWeighted() against transparent color, i.e. rgba(0, 0, 0, 0).
    2005             :   // But unpremultiplication in AddWeightedColors() does not work well
    2006             :   // for such cases, so we use another function named DiluteColor() which
    2007             :   // has a similar logic to AddWeightedColors().
    2008             :   return aCoeff2 == 0.0
    2009           0 :     ? DiluteColor(aValue1, aCoeff1)
    2010           0 :     : AddWeightedColors(aCoeff1, aValue1, aCoeff2, aValue2).ToColor();
    2011             : }
    2012             : 
    2013             : void
    2014           0 : AppendToCSSValueList(UniquePtr<nsCSSValueList>& aHead,
    2015             :                      UniquePtr<nsCSSValueList>&& aValueToAppend,
    2016             :                      nsCSSValueList** aTail)
    2017             : {
    2018           0 :   MOZ_ASSERT(!aHead == !*aTail,
    2019             :              "Can't have head w/o tail, & vice versa");
    2020             : 
    2021           0 :   if (!aHead) {
    2022           0 :     aHead = Move(aValueToAppend);
    2023           0 :     *aTail = aHead.get();
    2024             :   } else {
    2025           0 :     (*aTail) = (*aTail)->mNext = aValueToAppend.release();
    2026             :   }
    2027           0 : }
    2028             : 
    2029             : void
    2030           0 : AppendToCSSValuePairList(UniquePtr<nsCSSValuePairList>& aHead,
    2031             :                          UniquePtr<nsCSSValuePairList>&& aValueToAppend,
    2032             :                          nsCSSValuePairList** aTail)
    2033             : {
    2034           0 :   MOZ_ASSERT(!aHead == !*aTail,
    2035             :              "Can't have head w/o tail, & vice versa");
    2036             : 
    2037           0 :   if (!aHead) {
    2038           0 :     aHead = Move(aValueToAppend);
    2039           0 :     *aTail = aHead.get();
    2040             :   } else {
    2041           0 :     (*aTail) = (*aTail)->mNext = aValueToAppend.release();
    2042             :   }
    2043           0 : }
    2044             : 
    2045             : static UniquePtr<nsCSSValueList>
    2046           0 : AddWeightedShadowItems(double aCoeff1, const nsCSSValue &aValue1,
    2047             :                        double aCoeff2, const nsCSSValue &aValue2,
    2048             :                        ColorAdditionType aColorAdditionType,
    2049             :                        nsCSSPropertyID aProperty)
    2050             : {
    2051             :   // X, Y, Radius, Spread, Color, Inset
    2052           0 :   MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Array,
    2053             :              "wrong unit");
    2054           0 :   MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Array,
    2055             :              "wrong unit");
    2056           0 :   nsCSSValue::Array *array1 = aValue1.GetArrayValue();
    2057           0 :   nsCSSValue::Array *array2 = aValue2.GetArrayValue();
    2058           0 :   RefPtr<nsCSSValue::Array> resultArray = nsCSSValue::Array::Create(6);
    2059             : 
    2060           0 :   for (size_t i = 0; i < 4; ++i) {
    2061             :     // The text-shadow is not need to spread radius,
    2062             :     // So we skip this interpolation.
    2063           0 :     if (i == 3 && (aProperty != eCSSProperty_box_shadow)) continue;
    2064           0 :     AddCSSValuePixel(aCoeff1, array1->Item(i), aCoeff2, array2->Item(i),
    2065             :                      resultArray->Item(i),
    2066             :                      // blur radius must be nonnegative
    2067           0 :                      (i == 2) ? CSS_PROPERTY_VALUE_NONNEGATIVE : 0);
    2068             :   }
    2069             : 
    2070           0 :   const nsCSSValue& colorValue1 = array1->Item(4);
    2071           0 :   const nsCSSValue& colorValue2 = array2->Item(4);
    2072           0 :   const nsCSSValue& inset1 = array1->Item(5);
    2073           0 :   const nsCSSValue& inset2 = array2->Item(5);
    2074           0 :   if ((colorValue1.GetUnit() != colorValue2.GetUnit() &&
    2075           0 :        (!colorValue1.IsNumericColorUnit() ||
    2076           0 :         !colorValue2.IsNumericColorUnit())) ||
    2077           0 :       inset1.GetUnit() != inset2.GetUnit()) {
    2078             :     // We don't know how to animate between color and no-color, or
    2079             :     // between inset and not-inset.
    2080             :     // NOTE: In case when both colors' units are eCSSUnit_Null, that means
    2081             :     // neither color value was specified, so we can interpolate.
    2082           0 :     return nullptr;
    2083             :   }
    2084             : 
    2085           0 :   if (colorValue1.GetUnit() != eCSSUnit_Null) {
    2086           0 :     RGBAColorData color1 = ExtractColor(colorValue1);
    2087           0 :     RGBAColorData color2 = ExtractColor(colorValue2);
    2088           0 :     if (aColorAdditionType == ColorAdditionType::Clamped) {
    2089           0 :       resultArray->Item(4).SetColorValue(
    2090           0 :         AddWeightedColorsAndClamp(aCoeff1, color1, aCoeff2, color2));
    2091             :     } else {
    2092           0 :       resultArray->Item(4).SetRGBAColorValue(
    2093           0 :         AddWeightedColors(aCoeff1, color1, aCoeff2, color2));
    2094             :     }
    2095             :   }
    2096             : 
    2097           0 :   MOZ_ASSERT(inset1 == inset2, "should match");
    2098           0 :   resultArray->Item(5) = inset1;
    2099             : 
    2100           0 :   auto resultItem = MakeUnique<nsCSSValueList>();
    2101           0 :   resultItem->mValue.SetArrayValue(resultArray, eCSSUnit_Array);
    2102           0 :   return resultItem;
    2103             : }
    2104             : 
    2105             : static void
    2106           0 : AddTransformScale(double aCoeff1, const nsCSSValue &aValue1,
    2107             :                   double aCoeff2, const nsCSSValue &aValue2,
    2108             :                   nsCSSValue &aResult)
    2109             : {
    2110             :   // Handle scale, and the two matrix components where identity is 1, by
    2111             :   // subtracting 1, multiplying by the coefficients, and then adding 1
    2112             :   // back.  This gets the right AddWeighted behavior and gets us the
    2113             :   // interpolation-against-identity behavior for free.
    2114           0 :   MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
    2115           0 :   MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
    2116             : 
    2117           0 :   float v1 = aValue1.GetFloatValue() - 1.0f,
    2118           0 :         v2 = aValue2.GetFloatValue() - 1.0f;
    2119           0 :   float result = v1 * aCoeff1 + v2 * aCoeff2;
    2120           0 :   aResult.SetFloatValue(EnsureNotNan(result + 1.0f), eCSSUnit_Number);
    2121           0 : }
    2122             : 
    2123             : /* static */ already_AddRefed<nsCSSValue::Array>
    2124           0 : StyleAnimationValue::AppendTransformFunction(nsCSSKeyword aTransformFunction,
    2125             :                                              nsCSSValueList**& aListTail)
    2126             : {
    2127           0 :   RefPtr<nsCSSValue::Array> arr = AppendFunction(aTransformFunction);
    2128           0 :   nsCSSValueList *item = new nsCSSValueList;
    2129           0 :   item->mValue.SetArrayValue(arr, eCSSUnit_Function);
    2130             : 
    2131           0 :   *aListTail = item;
    2132           0 :   aListTail = &item->mNext;
    2133             : 
    2134           0 :   return arr.forget();
    2135             : }
    2136             : 
    2137             : static nsCSSValueList*
    2138           0 : AddDifferentTransformLists(double aCoeff1, const nsCSSValueList* aList1,
    2139             :                            double aCoeff2, const nsCSSValueList* aList2,
    2140             :                            nsCSSKeyword aOperatorType)
    2141             : {
    2142           0 :   nsAutoPtr<nsCSSValueList> result;
    2143           0 :   nsCSSValueList **resultTail = getter_Transfers(result);
    2144             : 
    2145           0 :   RefPtr<nsCSSValue::Array> arr;
    2146             :   arr =
    2147           0 :     StyleAnimationValue::AppendTransformFunction(aOperatorType,
    2148           0 :                                                  resultTail);
    2149             : 
    2150             :   // FIXME: We should change the other transform code to also only
    2151             :   // take a single progress value, as having values that don't
    2152             :   // sum to 1 doesn't make sense for these.
    2153           0 :   if (aList1 == aList2) {
    2154           0 :     arr->Item(1).Reset();
    2155             :     // For accumulation, we need to increase accumulation count for |aList1|.
    2156           0 :     if (aOperatorType == eCSSKeyword_accumulatematrix) {
    2157           0 :       aCoeff2 += 1.0;
    2158             :     }
    2159             :   } else {
    2160           0 :     aList1->CloneInto(arr->Item(1).SetListValue());
    2161             :   }
    2162             : 
    2163           0 :   aList2->CloneInto(arr->Item(2).SetListValue());
    2164           0 :   arr->Item(3).SetPercentValue(aCoeff2);
    2165             : 
    2166           0 :   return result.forget();
    2167             : }
    2168             : 
    2169             : static UniquePtr<nsCSSValueList>
    2170           0 : AddWeightedFilterFunctionImpl(double aCoeff1, const nsCSSValueList* aList1,
    2171             :                               double aCoeff2, const nsCSSValueList* aList2,
    2172             :                               ColorAdditionType aColorAdditionType)
    2173             : {
    2174             :   // AddWeightedFilterFunction should be our only caller, and it should ensure
    2175             :   // that both args are non-null.
    2176           0 :   MOZ_ASSERT(aList1, "expected filter list");
    2177           0 :   MOZ_ASSERT(aList2, "expected filter list");
    2178           0 :   MOZ_ASSERT(aList1->mValue.GetUnit() == eCSSUnit_Function,
    2179             :              "expected function");
    2180           0 :   MOZ_ASSERT(aList2->mValue.GetUnit() == eCSSUnit_Function,
    2181             :              "expected function");
    2182           0 :   RefPtr<nsCSSValue::Array> a1 = aList1->mValue.GetArrayValue(),
    2183           0 :                               a2 = aList2->mValue.GetArrayValue();
    2184           0 :   nsCSSKeyword filterFunction = a1->Item(0).GetKeywordValue();
    2185           0 :   if (filterFunction != a2->Item(0).GetKeywordValue()) {
    2186           0 :     return nullptr; // Can't add two filters of different types.
    2187             :   }
    2188             : 
    2189           0 :   auto resultList = MakeUnique<nsCSSValueList>();
    2190             :   nsCSSValue::Array* result =
    2191           0 :     resultList->mValue.InitFunction(filterFunction, 1);
    2192             : 
    2193             :   // "hue-rotate" is the only filter-function that accepts negative values, and
    2194             :   // we don't use this "restrictions" variable in its clause below.
    2195           0 :   const uint32_t restrictions = CSS_PROPERTY_VALUE_NONNEGATIVE;
    2196           0 :   const nsCSSValue& funcArg1 = a1->Item(1);
    2197           0 :   const nsCSSValue& funcArg2 = a2->Item(1);
    2198           0 :   nsCSSValue& resultArg = result->Item(1);
    2199           0 :   float initialVal = 1.0f;
    2200           0 :   switch (filterFunction) {
    2201             :     case eCSSKeyword_blur: {
    2202             :       nsCSSUnit unit;
    2203           0 :       if (funcArg1.GetUnit() == funcArg2.GetUnit()) {
    2204           0 :         unit = funcArg1.GetUnit();
    2205             :       } else {
    2206             :         // If units differ, we'll just combine them with calc().
    2207           0 :         unit = eCSSUnit_Calc;
    2208             :       }
    2209           0 :       if (!AddCSSValuePixelPercentCalc(restrictions,
    2210             :                                        unit,
    2211             :                                        aCoeff1, funcArg1,
    2212             :                                        aCoeff2, funcArg2,
    2213             :                                        resultArg)) {
    2214           0 :         return nullptr;
    2215             :       }
    2216           0 :       break;
    2217             :     }
    2218             :     case eCSSKeyword_grayscale:
    2219             :     case eCSSKeyword_invert:
    2220             :     case eCSSKeyword_sepia:
    2221           0 :       initialVal = 0.0f;
    2222             :       MOZ_FALLTHROUGH;
    2223             :     case eCSSKeyword_brightness:
    2224             :     case eCSSKeyword_contrast:
    2225             :     case eCSSKeyword_opacity:
    2226             :     case eCSSKeyword_saturate:
    2227             :       AddCSSValuePercentNumber(restrictions,
    2228             :                                aCoeff1, funcArg1,
    2229             :                                aCoeff2, funcArg2,
    2230             :                                resultArg,
    2231           0 :                                initialVal);
    2232           0 :       break;
    2233             :     case eCSSKeyword_hue_rotate:
    2234             :       AddCSSValueAngle(aCoeff1, funcArg1,
    2235             :                        aCoeff2, funcArg2,
    2236           0 :                        resultArg);
    2237           0 :       break;
    2238             :     case eCSSKeyword_drop_shadow: {
    2239           0 :       MOZ_ASSERT(!funcArg1.GetListValue()->mNext &&
    2240             :                  !funcArg2.GetListValue()->mNext,
    2241             :                  "drop-shadow filter func doesn't support lists");
    2242             :       UniquePtr<nsCSSValueList> shadowValue =
    2243             :         AddWeightedShadowItems(aCoeff1,
    2244           0 :                                funcArg1.GetListValue()->mValue,
    2245             :                                aCoeff2,
    2246           0 :                                funcArg2.GetListValue()->mValue,
    2247             :                                aColorAdditionType,
    2248           0 :                                eCSSProperty_filter);
    2249           0 :       if (!shadowValue) {
    2250           0 :         return nullptr;
    2251             :       }
    2252           0 :       resultArg.AdoptListValue(Move(shadowValue));
    2253           0 :       break;
    2254             :     }
    2255             :     default:
    2256           0 :       MOZ_ASSERT(false, "unknown filter function");
    2257             :       return nullptr;
    2258             :   }
    2259             : 
    2260           0 :   return resultList;
    2261             : }
    2262             : 
    2263             : static UniquePtr<nsCSSValueList>
    2264           0 : AddWeightedFilterFunction(double aCoeff1, const nsCSSValueList* aList1,
    2265             :                           double aCoeff2, const nsCSSValueList* aList2,
    2266             :                           ColorAdditionType aColorAdditionType)
    2267             : {
    2268           0 :   MOZ_ASSERT(aList1 || aList2,
    2269             :              "one function list item must not be null");
    2270             :   // Note that one of our arguments could be null, indicating that
    2271             :   // it's the initial value. Rather than adding special null-handling
    2272             :   // logic, we just check for null values and replace them with
    2273             :   // 0 * the other value. That way, AddWeightedFilterFunctionImpl can assume
    2274             :   // its args are non-null.
    2275           0 :   if (!aList1) {
    2276             :     return AddWeightedFilterFunctionImpl(aCoeff2, aList2, 0, aList2,
    2277           0 :                                          aColorAdditionType);
    2278             :   }
    2279           0 :   if (!aList2) {
    2280             :     return AddWeightedFilterFunctionImpl(aCoeff1, aList1, 0, aList1,
    2281           0 :                                          aColorAdditionType);
    2282             :   }
    2283             : 
    2284             :   return AddWeightedFilterFunctionImpl(aCoeff1, aList1, aCoeff2, aList2,
    2285           0 :                                        aColorAdditionType);
    2286             : }
    2287             : 
    2288             : static inline uint32_t
    2289           0 : ShapeArgumentCount(nsCSSKeyword aShapeFunction)
    2290             : {
    2291           0 :   switch (aShapeFunction) {
    2292             :     case eCSSKeyword_circle:
    2293           0 :       return 2; // radius and center point
    2294             :     case eCSSKeyword_polygon:
    2295           0 :       return 2; // fill rule and a list of points
    2296             :     case eCSSKeyword_ellipse:
    2297           0 :       return 3; // two radii and center point
    2298             :     case eCSSKeyword_inset:
    2299           0 :       return 5; // four edge offsets and a list of corner radii
    2300             :     default:
    2301           0 :       MOZ_ASSERT_UNREACHABLE("Unknown shape type");
    2302             :       return 0;
    2303             :   }
    2304             : }
    2305             : 
    2306             : static void
    2307           0 : AddPositions(double aCoeff1, const nsCSSValue& aPos1,
    2308             :              double aCoeff2, const nsCSSValue& aPos2,
    2309             :              nsCSSValue& aResultPos)
    2310             : {
    2311           0 :   MOZ_ASSERT(aPos1.GetUnit() == eCSSUnit_Array &&
    2312             :              aPos2.GetUnit() == eCSSUnit_Array,
    2313             :              "Args should be CSS <position>s, encoded as arrays");
    2314             : 
    2315           0 :   const nsCSSValue::Array* posArray1 = aPos1.GetArrayValue();
    2316           0 :   const nsCSSValue::Array* posArray2 = aPos2.GetArrayValue();
    2317           0 :   MOZ_ASSERT(posArray1->Count() == 4 && posArray2->Count() == 4,
    2318             :              "CSSParserImpl::ParsePositionValue creates an array of length "
    2319             :              "4 - how did we get here?");
    2320             : 
    2321           0 :   nsCSSValue::Array* resultPosArray = nsCSSValue::Array::Create(4);
    2322           0 :   aResultPos.SetArrayValue(resultPosArray, eCSSUnit_Array);
    2323             : 
    2324             :   // Only iterate over elements 1 and 3.  The <position> is 'uncomputed' to
    2325             :   // only those elements.  See also the comment in SetPositionValue.
    2326           0 :   for (size_t i = 1; i < 4; i += 2) {
    2327           0 :     const nsCSSValue& v1 = posArray1->Item(i);
    2328           0 :     const nsCSSValue& v2 = posArray2->Item(i);
    2329           0 :     nsCSSValue& vr = resultPosArray->Item(i);
    2330             :     AddCSSValueCanonicalCalc(aCoeff1, v1,
    2331           0 :                              aCoeff2, v2, vr);
    2332             :   }
    2333           0 : }
    2334             : 
    2335             : static Maybe<nsCSSValuePair>
    2336           0 : AddCSSValuePair(nsCSSPropertyID aProperty, uint32_t aRestrictions,
    2337             :                 double aCoeff1, const nsCSSValuePair* aPair1,
    2338             :                 double aCoeff2, const nsCSSValuePair* aPair2)
    2339             : {
    2340           0 :   MOZ_ASSERT(aPair1, "expected pair");
    2341           0 :   MOZ_ASSERT(aPair2, "expected pair");
    2342             : 
    2343           0 :   Maybe<nsCSSValuePair> result;
    2344             :   nsCSSUnit unit[2];
    2345           0 :   unit[0] = GetCommonUnit(aProperty, aPair1->mXValue.GetUnit(),
    2346             :                           aPair2->mXValue.GetUnit());
    2347           0 :   unit[1] = GetCommonUnit(aProperty, aPair1->mYValue.GetUnit(),
    2348             :                           aPair2->mYValue.GetUnit());
    2349           0 :   if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
    2350           0 :       unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) {
    2351           0 :     return result; // Nothing() (returning |result| for RVO)
    2352             :   }
    2353             : 
    2354           0 :   result.emplace();
    2355             : 
    2356             :   static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
    2357             :     &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
    2358             :   };
    2359           0 :   for (uint32_t i = 0; i < 2; ++i) {
    2360           0 :     nsCSSValue nsCSSValuePair::*member = pairValues[i];
    2361           0 :     if (!AddCSSValuePixelPercentCalc(aRestrictions, unit[i],
    2362             :                                      aCoeff1, aPair1->*member,
    2363             :                                      aCoeff2, aPair2->*member,
    2364           0 :                                      result.ref().*member) ) {
    2365           0 :       MOZ_ASSERT(false, "unexpected unit");
    2366             :       result.reset();
    2367             :       return result; // Nothing() (returning |result| for RVO)
    2368             :     }
    2369             :   }
    2370             : 
    2371           0 :   return result;
    2372             : }
    2373             : 
    2374             : static UniquePtr<nsCSSValuePairList>
    2375           0 : AddCSSValuePairList(nsCSSPropertyID aProperty,
    2376             :                     double aCoeff1, const nsCSSValuePairList* aList1,
    2377             :                     double aCoeff2, const nsCSSValuePairList* aList2)
    2378             : {
    2379           0 :   MOZ_ASSERT(aList1, "Can't add a null list");
    2380           0 :   MOZ_ASSERT(aList2, "Can't add a null list");
    2381             : 
    2382           0 :   auto result = MakeUnique<nsCSSValuePairList>();
    2383           0 :   nsCSSValuePairList* resultPtr = result.get();
    2384             : 
    2385           0 :   do {
    2386             :     static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
    2387             :       &nsCSSValuePairList::mXValue,
    2388             :       &nsCSSValuePairList::mYValue,
    2389             :     };
    2390           0 :     uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
    2391           0 :     for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) {
    2392           0 :       const nsCSSValue& v1 = aList1->*(pairListValues[i]);
    2393           0 :       const nsCSSValue& v2 = aList2->*(pairListValues[i]);
    2394             : 
    2395           0 :       nsCSSValue& vr = resultPtr->*(pairListValues[i]);
    2396             :       nsCSSUnit unit =
    2397           0 :         GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
    2398           0 :       if (unit == eCSSUnit_Null) {
    2399           0 :         return nullptr;
    2400             :       }
    2401           0 :       if (unit == eCSSUnit_Number) {
    2402             :         AddCSSValueNumber(aCoeff1, v1,
    2403             :                           aCoeff2, v2,
    2404           0 :                           vr, restrictions);
    2405           0 :       } else if (!AddCSSValuePixelPercentCalc(restrictions, unit,
    2406             :                                               aCoeff1, v1,
    2407             :                                               aCoeff2, v2, vr)) {
    2408           0 :         if (v1 != v2) {
    2409           0 :           return nullptr;
    2410             :         }
    2411           0 :         vr = v1;
    2412             :       }
    2413             :     }
    2414           0 :     aList1 = aList1->mNext;
    2415           0 :     aList2 = aList2->mNext;
    2416           0 :     if (!aList1 || !aList2) {
    2417             :       break;
    2418             :     }
    2419           0 :     resultPtr->mNext = new nsCSSValuePairList;
    2420           0 :     resultPtr = resultPtr->mNext;
    2421           0 :   } while (aList1 && aList2);
    2422             : 
    2423           0 :   if (aList1 || aList2) {
    2424           0 :     return nullptr; // We can't interpolate lists of different lengths
    2425             :   }
    2426             : 
    2427           0 :   return result;
    2428             : }
    2429             : 
    2430             : static already_AddRefed<nsCSSValue::Array>
    2431           0 : AddShapeFunction(nsCSSPropertyID aProperty,
    2432             :                  double aCoeff1, const nsCSSValue::Array* aArray1,
    2433             :                  double aCoeff2, const nsCSSValue::Array* aArray2,
    2434             :                  Restrictions aRestriction)
    2435             : {
    2436           0 :   MOZ_ASSERT(aArray1 && aArray1->Count() == 2, "expected shape function");
    2437           0 :   MOZ_ASSERT(aArray2 && aArray2->Count() == 2, "expected shape function");
    2438           0 :   MOZ_ASSERT(aArray1->Item(0).GetUnit() == eCSSUnit_Function,
    2439             :              "expected function");
    2440           0 :   MOZ_ASSERT(aArray2->Item(0).GetUnit() == eCSSUnit_Function,
    2441             :              "expected function");
    2442           0 :   MOZ_ASSERT(aArray1->Item(1).GetUnit() == eCSSUnit_Enumerated,
    2443             :              "expected geometry-box");
    2444           0 :   MOZ_ASSERT(aArray2->Item(1).GetUnit() == eCSSUnit_Enumerated,
    2445             :              "expected geometry-box");
    2446             : 
    2447           0 :   if (aArray1->Item(1).GetIntValue() != aArray2->Item(1).GetIntValue()) {
    2448           0 :     return nullptr; // Both shapes must use the same reference box.
    2449             :   }
    2450             : 
    2451           0 :   const nsCSSValue::Array* func1 = aArray1->Item(0).GetArrayValue();
    2452           0 :   const nsCSSValue::Array* func2 = aArray2->Item(0).GetArrayValue();
    2453           0 :   nsCSSKeyword shapeFuncName = func1->Item(0).GetKeywordValue();
    2454           0 :   if (shapeFuncName != func2->Item(0).GetKeywordValue()) {
    2455           0 :     return nullptr; // Can't add two shapes of different types.
    2456             :   }
    2457             : 
    2458           0 :   RefPtr<nsCSSValue::Array> result = nsCSSValue::Array::Create(2);
    2459             : 
    2460             :   nsCSSValue::Array* resultFuncArgs =
    2461           0 :     result->Item(0).InitFunction(shapeFuncName,
    2462           0 :                                  ShapeArgumentCount(shapeFuncName));
    2463           0 :   switch (shapeFuncName) {
    2464             :     case eCSSKeyword_ellipse:
    2465             :       // Add ellipses' |ry| values (but fail if we encounter an enum):
    2466           0 :       if (!AddCSSValuePixelPercentCalc(aRestriction == Restrictions::Enable
    2467             :                                          ? CSS_PROPERTY_VALUE_NONNEGATIVE
    2468             :                                          : 0,
    2469             :                                        GetCommonUnit(aProperty,
    2470           0 :                                                      func1->Item(2).GetUnit(),
    2471           0 :                                                      func2->Item(2).GetUnit()),
    2472             :                                        aCoeff1, func1->Item(2),
    2473             :                                        aCoeff2, func2->Item(2),
    2474             :                                        resultFuncArgs->Item(2))) {
    2475           0 :         return nullptr;
    2476             :       }
    2477             :       MOZ_FALLTHROUGH;  // to handle rx and center point
    2478             :     case eCSSKeyword_circle: {
    2479             :       // Add circles' |r| (or ellipses' |rx|) values:
    2480           0 :       if (!AddCSSValuePixelPercentCalc(aRestriction == Restrictions::Enable
    2481             :                                          ? CSS_PROPERTY_VALUE_NONNEGATIVE
    2482             :                                          : 0,
    2483             :                                        GetCommonUnit(aProperty,
    2484           0 :                                                      func1->Item(1).GetUnit(),
    2485           0 :                                                      func2->Item(1).GetUnit()),
    2486             :                                        aCoeff1, func1->Item(1),
    2487             :                                        aCoeff2, func2->Item(1),
    2488             :                                        resultFuncArgs->Item(1))) {
    2489           0 :         return nullptr;
    2490             :       }
    2491             :       // Add center points (defined as a <position>).
    2492           0 :       size_t posIndex = shapeFuncName == eCSSKeyword_circle ? 2 : 3;
    2493           0 :       AddPositions(aCoeff1, func1->Item(posIndex),
    2494             :                    aCoeff2, func2->Item(posIndex),
    2495           0 :                    resultFuncArgs->Item(posIndex));
    2496           0 :       break;
    2497             :     }
    2498             :     case eCSSKeyword_polygon: {
    2499             :       // Add polygons' corresponding points (if the fill rule matches):
    2500           0 :       int32_t fillRule = func1->Item(1).GetIntValue();
    2501           0 :       if (fillRule != func2->Item(1).GetIntValue()) {
    2502           0 :         return nullptr; // can't interpolate between different fill rules
    2503             :       }
    2504           0 :       resultFuncArgs->Item(1).SetIntValue(fillRule, eCSSUnit_Enumerated);
    2505             : 
    2506           0 :       const nsCSSValuePairList* points1 = func1->Item(2).GetPairListValue();
    2507           0 :       const nsCSSValuePairList* points2 = func2->Item(2).GetPairListValue();
    2508             :       UniquePtr<nsCSSValuePairList> resultPoints =
    2509           0 :         AddCSSValuePairList(aProperty, aCoeff1, points1, aCoeff2, points2);
    2510           0 :       if (!resultPoints) {
    2511           0 :         return nullptr;
    2512             :       }
    2513           0 :       resultFuncArgs->Item(2).AdoptPairListValue(Move(resultPoints));
    2514           0 :       break;
    2515             :     }
    2516             :     case eCSSKeyword_inset: {
    2517           0 :       MOZ_ASSERT(func1->Count() == 6 && func2->Count() == 6,
    2518             :                  "Update for CSSParserImpl::ParseInsetFunction changes");
    2519             :       // Items 1-4 are respectively the top, right, bottom and left offsets
    2520             :       // from the reference box.
    2521           0 :       for (size_t i = 1; i <= 4; ++i) {
    2522           0 :         if (!AddCSSValuePixelPercentCalc(aRestriction == Restrictions::Enable
    2523             :                                            ? CSS_PROPERTY_VALUE_NONNEGATIVE
    2524             :                                            : 0,
    2525             :                                          GetCommonUnit(aProperty,
    2526           0 :                                                        func1->Item(i).GetUnit(),
    2527           0 :                                                        func2->Item(i).GetUnit()),
    2528             :                                          aCoeff1, func1->Item(i),
    2529             :                                          aCoeff2, func2->Item(i),
    2530             :                                          resultFuncArgs->Item(i))) {
    2531           0 :           return nullptr;
    2532             :         }
    2533             :       }
    2534             :       // Item 5 contains the radii of the rounded corners for the inset
    2535             :       // rectangle.
    2536           0 :       MOZ_ASSERT(func1->Item(5).GetUnit() == eCSSUnit_Array &&
    2537             :                  func2->Item(5).GetUnit() == eCSSUnit_Array,
    2538             :                  "Expected two arrays");
    2539           0 :       const nsCSSValue::Array* radii1 = func1->Item(5).GetArrayValue();
    2540           0 :       const nsCSSValue::Array* radii2 = func2->Item(5).GetArrayValue();
    2541           0 :       MOZ_ASSERT(radii1->Count() == 4 && radii2->Count() == 4);
    2542           0 :       nsCSSValue::Array* resultRadii = nsCSSValue::Array::Create(4);
    2543           0 :       resultFuncArgs->Item(5).SetArrayValue(resultRadii, eCSSUnit_Array);
    2544             :       // We use an arbitrary border-radius property here to get the appropriate
    2545             :       // restrictions for radii since this is a <border-radius> value.
    2546             :       uint32_t restrictions =
    2547             :         aRestriction == Restrictions::Enable
    2548           0 :           ? nsCSSProps::ValueRestrictions(eCSSProperty_border_top_left_radius)
    2549           0 :           : 0;
    2550           0 :       for (size_t i = 0; i < 4; ++i) {
    2551           0 :         const nsCSSValuePair& pair1 = radii1->Item(i).GetPairValue();
    2552           0 :         const nsCSSValuePair& pair2 = radii2->Item(i).GetPairValue();
    2553             :         const Maybe<nsCSSValuePair> pairResult =
    2554             :           AddCSSValuePair(aProperty, restrictions,
    2555             :                           aCoeff1, &pair1,
    2556           0 :                           aCoeff2, &pair2);
    2557           0 :         if (!pairResult) {
    2558           0 :           return nullptr;
    2559             :         }
    2560           0 :         resultRadii->Item(i).SetPairValue(pairResult.ptr());
    2561             :       }
    2562           0 :       break;
    2563             :     }
    2564             :     default:
    2565           0 :       MOZ_ASSERT_UNREACHABLE("Unknown shape type");
    2566             :       return nullptr;
    2567             :   }
    2568             : 
    2569             :   // set the geometry-box value
    2570           0 :   result->Item(1).SetIntValue(aArray1->Item(1).GetIntValue(),
    2571           0 :                               eCSSUnit_Enumerated);
    2572             : 
    2573           0 :   return result.forget();
    2574             : }
    2575             : 
    2576             : static nsCSSValueList*
    2577           0 : AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
    2578             :                   double aCoeff2, const nsCSSValueList* aList2,
    2579             :                   nsCSSKeyword aOperatorType)
    2580             : {
    2581           0 :   nsAutoPtr<nsCSSValueList> result;
    2582           0 :   nsCSSValueList **resultTail = getter_Transfers(result);
    2583             : 
    2584           0 :   do {
    2585           0 :     RefPtr<nsCSSValue::Array> a1 = ToPrimitive(aList1->mValue.GetArrayValue()),
    2586           0 :                                 a2 = ToPrimitive(aList2->mValue.GetArrayValue());
    2587           0 :     MOZ_ASSERT(
    2588             :       TransformFunctionsMatch(nsStyleTransformMatrix::TransformFunctionOf(a1),
    2589             :                               nsStyleTransformMatrix::TransformFunctionOf(a2)),
    2590             :       "transform function mismatch");
    2591           0 :     MOZ_ASSERT(!*resultTail,
    2592             :                "resultTail isn't pointing to the tail (may leak)");
    2593             : 
    2594           0 :     nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
    2595           0 :     RefPtr<nsCSSValue::Array> arr;
    2596           0 :     if (tfunc != eCSSKeyword_matrix &&
    2597           0 :         tfunc != eCSSKeyword_matrix3d &&
    2598           0 :         tfunc != eCSSKeyword_interpolatematrix &&
    2599           0 :         tfunc != eCSSKeyword_rotate3d &&
    2600             :         tfunc != eCSSKeyword_perspective) {
    2601           0 :       arr = StyleAnimationValue::AppendTransformFunction(tfunc, resultTail);
    2602             :     }
    2603             : 
    2604           0 :     switch (tfunc) {
    2605             :       case eCSSKeyword_translate3d: {
    2606           0 :           MOZ_ASSERT(a1->Count() == 4, "unexpected count");
    2607           0 :           MOZ_ASSERT(a2->Count() == 4, "unexpected count");
    2608           0 :           AddTransformTranslate(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
    2609           0 :                                 arr->Item(1));
    2610           0 :           AddTransformTranslate(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2),
    2611           0 :                                 arr->Item(2));
    2612           0 :           AddTransformTranslate(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3),
    2613           0 :                                 arr->Item(3));
    2614           0 :           break;
    2615             :       }
    2616             :       case eCSSKeyword_scale3d: {
    2617           0 :           MOZ_ASSERT(a1->Count() == 4, "unexpected count");
    2618           0 :           MOZ_ASSERT(a2->Count() == 4, "unexpected count");
    2619             : 
    2620           0 :           AddTransformScale(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
    2621           0 :                             arr->Item(1));
    2622           0 :           AddTransformScale(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2),
    2623           0 :                             arr->Item(2));
    2624           0 :           AddTransformScale(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3),
    2625           0 :                             arr->Item(3));
    2626             : 
    2627           0 :           break;
    2628             :       }
    2629             :       // It would probably be nicer to animate skew in tangent space
    2630             :       // rather than angle space.  However, it's easy to specify
    2631             :       // skews with infinite tangents, and behavior changes pretty
    2632             :       // drastically when crossing such skews (since the direction of
    2633             :       // animation flips), so interop is probably more important here.
    2634             :       case eCSSKeyword_skew: {
    2635           0 :         MOZ_ASSERT(a1->Count() == 2 || a1->Count() == 3,
    2636             :                    "unexpected count");
    2637           0 :         MOZ_ASSERT(a2->Count() == 2 || a2->Count() == 3,
    2638             :                    "unexpected count");
    2639             : 
    2640           0 :         nsCSSValue zero(0.0f, eCSSUnit_Radian);
    2641             :         // Add Y component of skew.
    2642           0 :         AddCSSValueAngle(aCoeff1,
    2643           0 :                          a1->Count() == 3 ? a1->Item(2) : zero,
    2644             :                          aCoeff2,
    2645           0 :                          a2->Count() == 3 ? a2->Item(2) : zero,
    2646           0 :                          arr->Item(2));
    2647             : 
    2648             :         // Add X component of skew (which can be merged with case below
    2649             :         // in non-DEBUG).
    2650           0 :         AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
    2651           0 :                          arr->Item(1));
    2652             : 
    2653           0 :         break;
    2654             :       }
    2655             :       case eCSSKeyword_skewx:
    2656             :       case eCSSKeyword_skewy:
    2657             :       case eCSSKeyword_rotate:
    2658             :       case eCSSKeyword_rotatex:
    2659             :       case eCSSKeyword_rotatey:
    2660             :       case eCSSKeyword_rotatez: {
    2661           0 :         MOZ_ASSERT(a1->Count() == 2, "unexpected count");
    2662           0 :         MOZ_ASSERT(a2->Count() == 2, "unexpected count");
    2663             : 
    2664           0 :         AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
    2665           0 :                          arr->Item(1));
    2666             : 
    2667           0 :         break;
    2668             :       }
    2669             :       case eCSSKeyword_rotate3d: {
    2670           0 :         Point3D vector1(a1->Item(1).GetFloatValue(),
    2671           0 :                         a1->Item(2).GetFloatValue(),
    2672           0 :                         a1->Item(3).GetFloatValue());
    2673           0 :         vector1.Normalize();
    2674           0 :         Point3D vector2(a2->Item(1).GetFloatValue(),
    2675           0 :                         a2->Item(2).GetFloatValue(),
    2676           0 :                         a2->Item(3).GetFloatValue());
    2677           0 :         vector2.Normalize();
    2678             : 
    2679             :         // Handle rotate3d with matched (normalized) vectors,
    2680             :         // otherwise fallthrough to the next switch statement
    2681             :         // and do matrix decomposition.
    2682           0 :         if (vector1 == vector2) {
    2683             :           // We skipped appending a transform function above for rotate3d,
    2684             :           // so do it now.
    2685           0 :           arr = StyleAnimationValue::AppendTransformFunction(tfunc, resultTail);
    2686           0 :           arr->Item(1).SetFloatValue(vector1.x, eCSSUnit_Number);
    2687           0 :           arr->Item(2).SetFloatValue(vector1.y, eCSSUnit_Number);
    2688           0 :           arr->Item(3).SetFloatValue(vector1.z, eCSSUnit_Number);
    2689             : 
    2690           0 :           AddCSSValueAngle(aCoeff1, a1->Item(4), aCoeff2, a2->Item(4),
    2691           0 :                            arr->Item(4));
    2692           0 :           break;
    2693             :         }
    2694             :         MOZ_FALLTHROUGH;
    2695             :       }
    2696             :       case eCSSKeyword_matrix:
    2697             :       case eCSSKeyword_matrix3d:
    2698             :       case eCSSKeyword_perspective:
    2699           0 :         if (aCoeff1 == 0.0 && aCoeff2 == 0.0) {
    2700             :           // Special case. If both coefficients are 0.0, we should apply an
    2701             :           // identity transform function.
    2702           0 :           arr = StyleAnimationValue::AppendTransformFunction(tfunc, resultTail);
    2703             : 
    2704           0 :           if (tfunc == eCSSKeyword_rotate3d) {
    2705           0 :             arr->Item(1).SetFloatValue(0.0, eCSSUnit_Number);
    2706           0 :             arr->Item(2).SetFloatValue(0.0, eCSSUnit_Number);
    2707           0 :             arr->Item(3).SetFloatValue(1.0, eCSSUnit_Number);
    2708           0 :             arr->Item(4).SetFloatValue(0.0, eCSSUnit_Radian);
    2709           0 :           } else if (tfunc == eCSSKeyword_perspective) {
    2710             :             // The parameter of the identity perspective function is
    2711             :             // positive infinite.
    2712           0 :             arr->Item(1).SetFloatValue(std::numeric_limits<float>::infinity(),
    2713           0 :                                        eCSSUnit_Pixel);
    2714             :           } else {
    2715           0 :             nsStyleTransformMatrix::SetIdentityMatrix(arr);
    2716             :           }
    2717           0 :           break;
    2718             :         }
    2719             :         MOZ_FALLTHROUGH;
    2720             :       case eCSSKeyword_interpolatematrix: {
    2721             :         // FIXME: If the matrix contains only numbers then we could decompose
    2722             :         // here.
    2723             : 
    2724             :         // Construct temporary lists with only this item in them.
    2725           0 :         nsCSSValueList tempList1, tempList2;
    2726           0 :         tempList1.mValue = aList1->mValue;
    2727           0 :         tempList2.mValue = aList2->mValue;
    2728             : 
    2729           0 :         if (aList1 == aList2) {
    2730           0 :           *resultTail =
    2731           0 :             AddDifferentTransformLists(aCoeff1, &tempList1,
    2732             :                                        aCoeff2, &tempList1,
    2733             :                                        aOperatorType);
    2734             :         } else {
    2735           0 :           *resultTail =
    2736           0 :             AddDifferentTransformLists(aCoeff1, &tempList1,
    2737             :                                        aCoeff2, &tempList2,
    2738             :                                        aOperatorType);
    2739             :         }
    2740             : 
    2741             :         // Now advance resultTail to point to the new tail slot.
    2742           0 :         while (*resultTail) {
    2743           0 :           resultTail = &(*resultTail)->mNext;
    2744             :         }
    2745             : 
    2746           0 :         break;
    2747             :       }
    2748             :       default:
    2749           0 :         MOZ_ASSERT_UNREACHABLE(
    2750             :           "unknown transform function or accumulatematrix");
    2751             :     }
    2752             : 
    2753           0 :     aList1 = aList1->mNext;
    2754           0 :     aList2 = aList2->mNext;
    2755           0 :   } while (aList1);
    2756           0 :   MOZ_ASSERT(!aList2, "list length mismatch");
    2757           0 :   MOZ_ASSERT(!*resultTail,
    2758             :              "resultTail isn't pointing to the tail");
    2759             : 
    2760           0 :   return result.forget();
    2761             : }
    2762             : 
    2763             : static void
    2764           0 : AddPositionCoords(double aCoeff1, const nsCSSValue& aPos1,
    2765             :                   double aCoeff2, const nsCSSValue& aPos2,
    2766             :                   nsCSSValue& aResultPos)
    2767             : {
    2768           0 :   const nsCSSValue::Array* posArray1 = aPos1.GetArrayValue();
    2769           0 :   const nsCSSValue::Array* posArray2 = aPos2.GetArrayValue();
    2770           0 :   nsCSSValue::Array* resultPosArray = nsCSSValue::Array::Create(2);
    2771           0 :   aResultPos.SetArrayValue(resultPosArray, eCSSUnit_Array);
    2772             : 
    2773             :   /* Only compute element 1. The <position-coord> is
    2774             :    * 'uncomputed' to only that element.
    2775             :    */
    2776           0 :   const nsCSSValue& v1 = posArray1->Item(1);
    2777           0 :   const nsCSSValue& v2 = posArray2->Item(1);
    2778           0 :   nsCSSValue& vr = resultPosArray->Item(1);
    2779             :   AddCSSValueCanonicalCalc(aCoeff1, v1,
    2780           0 :                            aCoeff2, v2, vr);
    2781           0 : }
    2782             : 
    2783             : static UniquePtr<nsCSSValueList>
    2784           0 : AddWeightedShadowList(double aCoeff1,
    2785             :                       const nsCSSValueList* aShadow1,
    2786             :                       double aCoeff2,
    2787             :                       const nsCSSValueList* aShadow2,
    2788             :                       ColorAdditionType aColorAdditionType,
    2789             :                       nsCSSPropertyID aProperty)
    2790             : {
    2791             :   // This is implemented according to:
    2792             :   // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
    2793             :   // and the third item in the summary of:
    2794             :   // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
    2795           0 :   UniquePtr<nsCSSValueList> result;
    2796           0 :   nsCSSValueList* tail = nullptr;
    2797           0 :   while (aShadow1 && aShadow2) {
    2798             :     UniquePtr<nsCSSValueList> shadowValue =
    2799             :       AddWeightedShadowItems(aCoeff1, aShadow1->mValue,
    2800             :                              aCoeff2, aShadow2->mValue,
    2801             :                              aColorAdditionType,
    2802           0 :                              aProperty);
    2803           0 :     if (!shadowValue) {
    2804           0 :       return nullptr;
    2805             :     }
    2806           0 :     aShadow1 = aShadow1->mNext;
    2807           0 :     aShadow2 = aShadow2->mNext;
    2808           0 :     AppendToCSSValueList(result, Move(shadowValue), &tail);
    2809             :   }
    2810           0 :   if (aShadow1 || aShadow2) {
    2811             :     const nsCSSValueList *longShadow;
    2812             :     double longCoeff;
    2813           0 :     if (aShadow1) {
    2814           0 :       longShadow = aShadow1;
    2815           0 :       longCoeff = aCoeff1;
    2816             :     } else {
    2817           0 :       longShadow = aShadow2;
    2818           0 :       longCoeff = aCoeff2;
    2819             :     }
    2820             : 
    2821           0 :     while (longShadow) {
    2822             :       // Passing coefficients that add to less than 1 produces the
    2823             :       // desired result of interpolating "0 0 0 transparent" with
    2824             :       // the current shadow.
    2825             :       UniquePtr<nsCSSValueList> shadowValue =
    2826             :         AddWeightedShadowItems(longCoeff, longShadow->mValue,
    2827             :                                0.0, longShadow->mValue,
    2828           0 :                                aColorAdditionType, aProperty);
    2829           0 :       if (!shadowValue) {
    2830           0 :         return nullptr;
    2831             :       }
    2832             : 
    2833           0 :       longShadow = longShadow->mNext;
    2834           0 :       AppendToCSSValueList(result, Move(shadowValue), &tail);
    2835             :     }
    2836             :   }
    2837           0 :   return result;
    2838             : }
    2839             : 
    2840             : static UniquePtr<nsCSSValueList>
    2841           0 : AddWeightedFilterList(double aCoeff1, const nsCSSValueList* aList1,
    2842             :                       double aCoeff2, const nsCSSValueList* aList2,
    2843             :                       ColorAdditionType aColorAdditionType)
    2844             : {
    2845           0 :   UniquePtr<nsCSSValueList> result;
    2846           0 :   nsCSSValueList* tail = nullptr;
    2847           0 :   while (aList1 || aList2) {
    2848           0 :     if ((aList1 && aList1->mValue.GetUnit() != eCSSUnit_Function) ||
    2849           0 :         (aList2 && aList2->mValue.GetUnit() != eCSSUnit_Function)) {
    2850             :       // If we don't have filter-functions, we must have filter-URLs, which
    2851             :       // we can't add or interpolate.
    2852           0 :       return nullptr;
    2853             :     }
    2854             : 
    2855             :     UniquePtr<nsCSSValueList> resultFunction =
    2856             :       AddWeightedFilterFunction(aCoeff1, aList1, aCoeff2, aList2,
    2857           0 :                                 aColorAdditionType);
    2858           0 :     if (!resultFunction) {
    2859             :       // filter function mismatch
    2860           0 :       return nullptr;
    2861             :     }
    2862             : 
    2863           0 :     AppendToCSSValueList(result, Move(resultFunction), &tail);
    2864             : 
    2865             :     // move to next aList items
    2866           0 :     if (aList1) {
    2867           0 :       aList1 = aList1->mNext;
    2868             :     }
    2869           0 :     if (aList2) {
    2870           0 :       aList2 = aList2->mNext;
    2871             :     }
    2872             :   }
    2873             : 
    2874           0 :   return result;
    2875             : }
    2876             : 
    2877             : bool
    2878          10 : StyleAnimationValue::AddWeighted(nsCSSPropertyID aProperty,
    2879             :                                  double aCoeff1,
    2880             :                                  const StyleAnimationValue& aValue1,
    2881             :                                  double aCoeff2,
    2882             :                                  const StyleAnimationValue& aValue2,
    2883             :                                  StyleAnimationValue& aResultValue)
    2884             : {
    2885             :   Unit commonUnit =
    2886          10 :     GetCommonUnit(aProperty, aValue1.GetUnit(), aValue2.GetUnit());
    2887             :   // Maybe need a followup method to convert the inputs into the common
    2888             :   // unit-type, if they don't already match it. (Or would it make sense to do
    2889             :   // that in GetCommonUnit? in which case maybe ConvertToCommonUnit would be
    2890             :   // better.)
    2891             : 
    2892          10 :   switch (commonUnit) {
    2893             :     case eUnit_Null:
    2894             :     case eUnit_Auto:
    2895             :     case eUnit_None:
    2896             :     case eUnit_Normal:
    2897             :     case eUnit_UnparsedString:
    2898             :     case eUnit_URL:
    2899             :     case eUnit_DiscreteCSSValue:
    2900           0 :       return false;
    2901             : 
    2902             :     case eUnit_Enumerated:
    2903           0 :       switch (aProperty) {
    2904             :         case eCSSProperty_font_stretch: {
    2905             :           // https://drafts.csswg.org/css-fonts-3/#font-stretch-animation
    2906           0 :           double interpolatedValue = aCoeff1 * double(aValue1.GetIntValue()) +
    2907           0 :                                      aCoeff2 * double(aValue2.GetIntValue());
    2908           0 :           int32_t result = floor(interpolatedValue + 0.5);
    2909           0 :           if (result < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED) {
    2910           0 :             result = NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED;
    2911           0 :           } else if (result > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
    2912           0 :             result = NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED;
    2913             :           }
    2914           0 :           aResultValue.SetIntValue(result, eUnit_Enumerated);
    2915           0 :           return true;
    2916             :         }
    2917             :         default:
    2918           0 :           return false;
    2919             :       }
    2920             :     case eUnit_Visibility: {
    2921           0 :       int32_t enum1 = aValue1.GetIntValue();
    2922           0 :       int32_t enum2 = aValue2.GetIntValue();
    2923           0 :       if (enum1 == enum2) {
    2924           0 :         aResultValue.SetIntValue(enum1, eUnit_Visibility);
    2925           0 :         return true;
    2926             :       }
    2927           0 :       if ((enum1 == NS_STYLE_VISIBILITY_VISIBLE) ==
    2928             :           (enum2 == NS_STYLE_VISIBILITY_VISIBLE)) {
    2929           0 :         return false;
    2930             :       }
    2931           0 :       int32_t val1 = enum1 == NS_STYLE_VISIBILITY_VISIBLE;
    2932           0 :       int32_t val2 = enum2 == NS_STYLE_VISIBILITY_VISIBLE;
    2933           0 :       double interp = aCoeff1 * val1 + aCoeff2 * val2;
    2934           0 :       int32_t result = interp > 0.0 ? NS_STYLE_VISIBILITY_VISIBLE
    2935           0 :                                     : (val1 ? enum2 : enum1);
    2936           0 :       aResultValue.SetIntValue(result, eUnit_Visibility);
    2937           0 :       return true;
    2938             :     }
    2939             :     case eUnit_Integer: {
    2940             :       // https://drafts.csswg.org/css-transitions/#animtype-integer
    2941           0 :       double interpolatedValue = aCoeff1 * double(aValue1.GetIntValue()) +
    2942           0 :                                  aCoeff2 * double(aValue2.GetIntValue());
    2943           0 :       int32_t result = floor(interpolatedValue + 0.5);
    2944           0 :       if (aProperty == eCSSProperty_font_weight) {
    2945             :         // https://drafts.csswg.org/css-transitions/#animtype-font-weight
    2946           0 :         result += 50;
    2947           0 :         result -= result % 100;
    2948           0 :         result = Clamp(result, 100, 900);
    2949             :       } else {
    2950           0 :         result = RestrictValue(aProperty, result);
    2951             :       }
    2952           0 :       aResultValue.SetIntValue(result, eUnit_Integer);
    2953           0 :       return true;
    2954             :     }
    2955             :     case eUnit_Coord: {
    2956           0 :       aResultValue.SetCoordValue(RestrictValue(aProperty, NSToCoordRound(
    2957           0 :         aCoeff1 * aValue1.GetCoordValue() +
    2958           0 :         aCoeff2 * aValue2.GetCoordValue())));
    2959           0 :       return true;
    2960             :     }
    2961             :     case eUnit_Percent: {
    2962           0 :       aResultValue.SetPercentValue(RestrictValue(aProperty,
    2963           0 :         aCoeff1 * aValue1.GetPercentValue() +
    2964           0 :         aCoeff2 * aValue2.GetPercentValue()));
    2965           0 :       return true;
    2966             :     }
    2967             :     case eUnit_Float: {
    2968          10 :       aResultValue.SetFloatValue(RestrictValue(aProperty,
    2969          10 :         aCoeff1 * aValue1.GetFloatValue() +
    2970          20 :         aCoeff2 * aValue2.GetFloatValue()));
    2971          10 :       return true;
    2972             :     }
    2973             :     case eUnit_Color: {
    2974           0 :       RGBAColorData color1 = ExtractColor(aValue1);
    2975           0 :       RGBAColorData color2 = ExtractColor(aValue2);
    2976           0 :       auto resultColor = MakeUnique<nsCSSValue>();
    2977           0 :       resultColor->SetColorValue(
    2978           0 :         AddWeightedColorsAndClamp(aCoeff1, color1, aCoeff2, color2));
    2979           0 :       aResultValue.SetAndAdoptCSSValueValue(resultColor.release(), eUnit_Color);
    2980           0 :       return true;
    2981             :     }
    2982             :     case eUnit_CurrentColor: {
    2983           0 :       aResultValue.SetCurrentColorValue();
    2984           0 :       return true;
    2985             :     }
    2986             :     case eUnit_ComplexColor: {
    2987           0 :       ComplexColorData color1 = ExtractComplexColor(aValue1);
    2988           0 :       ComplexColorData color2 = ExtractComplexColor(aValue2);
    2989           0 :       RefPtr<ComplexColorValue> result = new ComplexColorValue;
    2990             :       // Common case is interpolating between a color and a currentcolor.
    2991           0 :       if (color1.IsNumericColor() && color2.IsCurrentColor()) {
    2992           0 :         result->mColor = color1.mColor;
    2993           0 :         result->mForegroundRatio = aCoeff2;
    2994           0 :       } else if (color1.IsCurrentColor() && color2.IsNumericColor()) {
    2995           0 :         result->mColor = color2.mColor;
    2996           0 :         result->mForegroundRatio = aCoeff1;
    2997             :       } else {
    2998           0 :         float ratio1 = 1.0f - color1.mForegroundRatio;
    2999           0 :         float ratio2 = 1.0f - color2.mForegroundRatio;
    3000           0 :         float alpha1 = color1.mColor.mA * ratio1;
    3001           0 :         float alpha2 = color2.mColor.mA * ratio2;
    3002             :         RGBAColorData resultColor =
    3003           0 :           AddWeightedColors(aCoeff1, color1.mColor.WithAlpha(alpha1),
    3004           0 :                             aCoeff2, color2.mColor.WithAlpha(alpha2));
    3005           0 :         float resultRatio = color1.mForegroundRatio * aCoeff1 +
    3006           0 :                             color2.mForegroundRatio * aCoeff2;
    3007           0 :         float resultAlpha = resultColor.mA / (1.0f - resultRatio);
    3008           0 :         result->mColor = resultColor.WithAlpha(resultAlpha);
    3009           0 :         result->mForegroundRatio = resultRatio;
    3010             :       }
    3011           0 :       aResultValue.SetComplexColorValue(result.forget());
    3012           0 :       return true;
    3013             :     }
    3014             :     case eUnit_Calc: {
    3015           0 :       PixelCalcValue v1 = ExtractCalcValue(aValue1);
    3016           0 :       PixelCalcValue v2 = ExtractCalcValue(aValue2);
    3017           0 :       double len = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
    3018           0 :       double pct = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
    3019           0 :       bool hasPct = (aCoeff1 != 0.0 && v1.mHasPercent) ||
    3020           0 :                       (aCoeff2 != 0.0 && v2.mHasPercent);
    3021           0 :       nsCSSValue *val = new nsCSSValue();
    3022           0 :       nsCSSValue::Array *arr = nsCSSValue::Array::Create(1);
    3023           0 :       val->SetArrayValue(arr, eCSSUnit_Calc);
    3024           0 :       if (hasPct) {
    3025           0 :         nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
    3026           0 :         arr2->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
    3027           0 :         arr2->Item(1).SetPercentValue(pct);
    3028           0 :         arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
    3029             :       } else {
    3030           0 :         arr->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
    3031             :       }
    3032           0 :       aResultValue.SetAndAdoptCSSValueValue(val, eUnit_Calc);
    3033           0 :       return true;
    3034             :     }
    3035             :     case eUnit_ObjectPosition: {
    3036           0 :       const nsCSSValue* position1 = aValue1.GetCSSValueValue();
    3037           0 :       const nsCSSValue* position2 = aValue2.GetCSSValueValue();
    3038             : 
    3039           0 :       nsAutoPtr<nsCSSValue> result(new nsCSSValue);
    3040           0 :       AddPositions(aCoeff1, *position1,
    3041           0 :                    aCoeff2, *position2, *result);
    3042             : 
    3043           0 :       aResultValue.SetAndAdoptCSSValueValue(result.forget(),
    3044           0 :                                             eUnit_ObjectPosition);
    3045           0 :       return true;
    3046             :     }
    3047             :     case eUnit_CSSValuePair: {
    3048           0 :       uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
    3049             :       Maybe<nsCSSValuePair> result =
    3050             :         AddCSSValuePair(aProperty, restrictions,
    3051           0 :                         aCoeff1, aValue1.GetCSSValuePairValue(),
    3052           0 :                         aCoeff2, aValue2.GetCSSValuePairValue());
    3053           0 :       if (!result) {
    3054           0 :         return false;
    3055             :       }
    3056             : 
    3057             :       // We need a heap allocated object to adopt here:
    3058           0 :       auto heapResult = MakeUnique<nsCSSValuePair>(*result);
    3059           0 :       aResultValue.SetAndAdoptCSSValuePairValue(heapResult.release(),
    3060           0 :                                                 eUnit_CSSValuePair);
    3061           0 :       return true;
    3062             :     }
    3063             :     case eUnit_CSSValueTriplet: {
    3064           0 :       nsCSSValueTriplet triplet1(*aValue1.GetCSSValueTripletValue());
    3065           0 :       nsCSSValueTriplet triplet2(*aValue2.GetCSSValueTripletValue());
    3066             : 
    3067             :       nsCSSUnit unit[3];
    3068           0 :       unit[0] = GetCommonUnit(aProperty, triplet1.mXValue.GetUnit(),
    3069             :                               triplet2.mXValue.GetUnit());
    3070           0 :       unit[1] = GetCommonUnit(aProperty, triplet1.mYValue.GetUnit(),
    3071             :                                triplet2.mYValue.GetUnit());
    3072           0 :       unit[2] = GetCommonUnit(aProperty, triplet1.mZValue.GetUnit(),
    3073             :                               triplet2.mZValue.GetUnit());
    3074           0 :       if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
    3075           0 :           unit[2] == eCSSUnit_Null) {
    3076           0 :         return false;
    3077             :       }
    3078             : 
    3079           0 :       nsAutoPtr<nsCSSValueTriplet> result(new nsCSSValueTriplet);
    3080             :       static nsCSSValue nsCSSValueTriplet::* const tripletValues[3] = {
    3081             :         &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
    3082             :       };
    3083           0 :       uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
    3084           0 :       for (uint32_t i = 0; i < 3; ++i) {
    3085           0 :         nsCSSValue nsCSSValueTriplet::*member = tripletValues[i];
    3086           0 :         if (!AddCSSValuePixelPercentCalc(restrictions, unit[i],
    3087             :                                          aCoeff1, &triplet1->*member,
    3088             :                                          aCoeff2, &triplet2->*member,
    3089           0 :                                          result->*member) ) {
    3090           0 :           MOZ_ASSERT(false, "unexpected unit");
    3091             :           return false;
    3092             :         }
    3093             :       }
    3094             : 
    3095           0 :       aResultValue.SetAndAdoptCSSValueTripletValue(result.forget(),
    3096           0 :                                                    eUnit_CSSValueTriplet);
    3097           0 :       return true;
    3098             :     }
    3099             :     case eUnit_CSSRect: {
    3100           0 :       MOZ_ASSERT(nsCSSProps::ValueRestrictions(aProperty) == 0,
    3101             :                  "must add code for handling value restrictions");
    3102           0 :       const nsCSSRect *rect1 = aValue1.GetCSSRectValue();
    3103           0 :       const nsCSSRect *rect2 = aValue2.GetCSSRectValue();
    3104           0 :       if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
    3105           0 :           rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
    3106           0 :           rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
    3107           0 :           rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
    3108             :         // At least until we have calc()
    3109           0 :         return false;
    3110             :       }
    3111             : 
    3112           0 :       nsAutoPtr<nsCSSRect> result(new nsCSSRect);
    3113           0 :       for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) {
    3114           0 :         nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
    3115           0 :         MOZ_ASSERT((rect1->*member).GetUnit() == (rect2->*member).GetUnit(),
    3116             :                    "should have returned above");
    3117           0 :         switch ((rect1->*member).GetUnit()) {
    3118             :           case eCSSUnit_Pixel:
    3119           0 :             AddCSSValuePixel(aCoeff1, rect1->*member, aCoeff2, rect2->*member,
    3120           0 :                              result->*member);
    3121           0 :             break;
    3122             :           case eCSSUnit_Auto:
    3123           0 :             if (float(aCoeff1 + aCoeff2) != 1.0f) {
    3124             :               // Interpolating between two auto values makes sense;
    3125             :               // adding in other ratios does not.
    3126           0 :               return false;
    3127             :             }
    3128           0 :             (result->*member).SetAutoValue();
    3129           0 :             break;
    3130             :           default:
    3131           0 :             MOZ_ASSERT(false, "unexpected unit");
    3132             :             return false;
    3133             :         }
    3134             :       }
    3135             : 
    3136           0 :       aResultValue.SetAndAdoptCSSRectValue(result.forget(), eUnit_CSSRect);
    3137           0 :       return true;
    3138             :     }
    3139             :     case eUnit_Dasharray: {
    3140           0 :       const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
    3141           0 :       const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
    3142             : 
    3143           0 :       uint32_t len1 = 0, len2 = 0;
    3144           0 :       for (const nsCSSValueList *v = list1; v; v = v->mNext) {
    3145           0 :         ++len1;
    3146             :       }
    3147           0 :       for (const nsCSSValueList *v = list2; v; v = v->mNext) {
    3148           0 :         ++len2;
    3149             :       }
    3150           0 :       MOZ_ASSERT(len1 > 0 && len2 > 0, "unexpected length");
    3151             : 
    3152           0 :       nsAutoPtr<nsCSSValueList> result;
    3153           0 :       nsCSSValueList **resultTail = getter_Transfers(result);
    3154           0 :       for (uint32_t i = 0, i_end = EuclidLCM<uint32_t>(len1, len2); i != i_end; ++i) {
    3155           0 :         const nsCSSValue &v1 = list1->mValue;
    3156           0 :         const nsCSSValue &v2 = list2->mValue;
    3157           0 :         MOZ_ASSERT(v1.GetUnit() == eCSSUnit_Number ||
    3158             :                    v1.GetUnit() == eCSSUnit_Percent, "unexpected");
    3159           0 :         MOZ_ASSERT(v2.GetUnit() == eCSSUnit_Number ||
    3160             :                    v2.GetUnit() == eCSSUnit_Percent, "unexpected");
    3161           0 :         if (v1.GetUnit() != v2.GetUnit()) {
    3162             :           // Can't animate between lengths and percentages (until calc()).
    3163           0 :           return false;
    3164             :         }
    3165             : 
    3166           0 :         nsCSSValueList *item = new nsCSSValueList;
    3167           0 :         *resultTail = item;
    3168           0 :         resultTail = &item->mNext;
    3169             : 
    3170           0 :         if (v1.GetUnit() == eCSSUnit_Number) {
    3171           0 :           AddCSSValueNumber(aCoeff1, v1, aCoeff2, v2, item->mValue,
    3172           0 :                             CSS_PROPERTY_VALUE_NONNEGATIVE);
    3173             :         } else {
    3174           0 :           AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, item->mValue,
    3175           0 :                              CSS_PROPERTY_VALUE_NONNEGATIVE);
    3176             :         }
    3177             : 
    3178           0 :         list1 = list1->mNext;
    3179           0 :         if (!list1) {
    3180           0 :           list1 = aValue1.GetCSSValueListValue();
    3181             :         }
    3182           0 :         list2 = list2->mNext;
    3183           0 :         if (!list2) {
    3184           0 :           list2 = aValue2.GetCSSValueListValue();
    3185             :         }
    3186             :       }
    3187             : 
    3188           0 :       aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
    3189           0 :                                                 eUnit_Dasharray);
    3190           0 :       return true;
    3191             :     }
    3192             :     case eUnit_Shadow: {
    3193             :       UniquePtr<nsCSSValueList> result =
    3194             :         AddWeightedShadowList(aCoeff1,
    3195           0 :                               aValue1.GetCSSValueListValue(),
    3196             :                               aCoeff2,
    3197           0 :                               aValue2.GetCSSValueListValue(),
    3198             :                               ColorAdditionType::Clamped,
    3199           0 :                               aProperty);
    3200           0 :       if (!result) {
    3201           0 :         return false;
    3202             :       }
    3203           0 :       aResultValue.SetAndAdoptCSSValueListValue(result.release(), eUnit_Shadow);
    3204           0 :       return true;
    3205             :     }
    3206             :     case eUnit_Shape: {
    3207             :       RefPtr<nsCSSValue::Array> result =
    3208           0 :         AddShapeFunction(aProperty,
    3209           0 :                          aCoeff1, aValue1.GetCSSValueArrayValue(),
    3210           0 :                          aCoeff2, aValue2.GetCSSValueArrayValue());
    3211           0 :       if (!result) {
    3212           0 :         return false;
    3213             :       }
    3214           0 :       aResultValue.SetCSSValueArrayValue(result, eUnit_Shape);
    3215           0 :       return true;
    3216             :     }
    3217             :     case eUnit_Filter: {
    3218             :       UniquePtr<nsCSSValueList> result =
    3219           0 :         AddWeightedFilterList(aCoeff1, aValue1.GetCSSValueListValue(),
    3220           0 :                               aCoeff2, aValue2.GetCSSValueListValue(),
    3221           0 :                               ColorAdditionType::Clamped);
    3222           0 :       if (!result) {
    3223           0 :         return false;
    3224             :       }
    3225             : 
    3226           0 :       aResultValue.SetAndAdoptCSSValueListValue(result.release(),
    3227           0 :                                                 eUnit_Filter);
    3228           0 :       return true;
    3229             :     }
    3230             : 
    3231             :     case eUnit_Transform: {
    3232           0 :       const nsCSSValueList* list1 = aValue1.GetCSSValueSharedListValue()->mHead;
    3233           0 :       const nsCSSValueList* list2 = aValue2.GetCSSValueSharedListValue()->mHead;
    3234             : 
    3235           0 :       MOZ_ASSERT(list1);
    3236           0 :       MOZ_ASSERT(list2);
    3237             : 
    3238             :       // We want to avoid the matrix decomposition when we can, since
    3239             :       // avoiding it can produce better results both for compound
    3240             :       // transforms and for skew and skewY (see below).  We can do this
    3241             :       // in two cases:
    3242             :       //   (1) if one of the transforms is 'none'
    3243             :       //   (2) if the lists have the same length and the transform
    3244             :       //       functions match
    3245           0 :       nsAutoPtr<nsCSSValueList> result;
    3246           0 :       if (list1->mValue.GetUnit() == eCSSUnit_None) {
    3247           0 :         if (list2->mValue.GetUnit() == eCSSUnit_None) {
    3248           0 :           result = new nsCSSValueList;
    3249           0 :           if (result) {
    3250           0 :             result->mValue.SetNoneValue();
    3251             :           }
    3252           0 :         } else if (HasAccumulateMatrix(list2)) {
    3253             :           result = AddDifferentTransformLists(0, list2, aCoeff2, list2,
    3254           0 :                                               eCSSKeyword_interpolatematrix);
    3255             :         } else {
    3256           0 :           result = AddTransformLists(0, list2, aCoeff2, list2);
    3257             :         }
    3258             :       } else {
    3259           0 :         if (list2->mValue.GetUnit() == eCSSUnit_None) {
    3260           0 :           if (HasAccumulateMatrix(list1)) {
    3261             :             result = AddDifferentTransformLists(0, list1,
    3262             :                                                 aCoeff1, list1,
    3263           0 :                                                 eCSSKeyword_interpolatematrix);
    3264             :           } else {
    3265           0 :             result = AddTransformLists(0, list1, aCoeff1, list1);
    3266             :           }
    3267           0 :         } else if (TransformFunctionListsMatch(list1, list2)) {
    3268             :           result = AddTransformLists(aCoeff1, list1, aCoeff2, list2,
    3269           0 :                                      eCSSKeyword_interpolatematrix);
    3270             :         } else {
    3271             :           result = AddDifferentTransformLists(aCoeff1, list1,
    3272             :                                               aCoeff2, list2,
    3273           0 :                                               eCSSKeyword_interpolatematrix);
    3274             :         }
    3275             :       }
    3276             : 
    3277           0 :       aResultValue.SetTransformValue(new nsCSSValueSharedList(result.forget()));
    3278           0 :       return true;
    3279             :     }
    3280             :     case eUnit_BackgroundPositionCoord: {
    3281           0 :       const nsCSSValueList *position1 = aValue1.GetCSSValueListValue();
    3282           0 :       const nsCSSValueList *position2 = aValue2.GetCSSValueListValue();
    3283           0 :       nsAutoPtr<nsCSSValueList> result;
    3284           0 :       nsCSSValueList **resultTail = getter_Transfers(result);
    3285           0 :       while (position1 && position2) {
    3286           0 :         nsCSSValueList *item = new nsCSSValueList;
    3287           0 :         *resultTail = item;
    3288           0 :         resultTail = &item->mNext;
    3289             : 
    3290           0 :         AddPositionCoords(aCoeff1, position1->mValue,
    3291           0 :                           aCoeff2, position2->mValue, item->mValue);
    3292             : 
    3293           0 :         position1 = position1->mNext;
    3294           0 :         position2 = position2->mNext;
    3295             :       }
    3296             : 
    3297             :       // Check for different lengths
    3298           0 :       if (position1 || position2) {
    3299           0 :         return false;
    3300             :       }
    3301             : 
    3302           0 :       aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
    3303           0 :                                                 eUnit_BackgroundPositionCoord);
    3304           0 :       return true;
    3305             :     }
    3306             :     case eUnit_CSSValuePairList: {
    3307           0 :       const nsCSSValuePairList *list1 = aValue1.GetCSSValuePairListValue();
    3308           0 :       const nsCSSValuePairList *list2 = aValue2.GetCSSValuePairListValue();
    3309             :       UniquePtr<nsCSSValuePairList> result =
    3310           0 :         AddCSSValuePairList(aProperty, aCoeff1, list1, aCoeff2, list2);
    3311           0 :       if (!result) {
    3312           0 :         return false;
    3313             :       }
    3314           0 :       aResultValue.SetAndAdoptCSSValuePairListValue(result.release());
    3315           0 :       return true;
    3316             :     }
    3317             :   }
    3318             : 
    3319           0 :   MOZ_ASSERT(false, "Can't interpolate using the given common unit");
    3320             :   return false;
    3321             : }
    3322             : 
    3323             : StyleAnimationValue
    3324           0 : StyleAnimationValue::Accumulate(nsCSSPropertyID aProperty,
    3325             :                                 const StyleAnimationValue& aA,
    3326             :                                 StyleAnimationValue&& aB,
    3327             :                                 uint64_t aCount)
    3328             : {
    3329           0 :   StyleAnimationValue result(Move(aB));
    3330             : 
    3331           0 :   if (aCount == 0) {
    3332           0 :     return result;
    3333             :   }
    3334             : 
    3335             :   Unit commonUnit =
    3336           0 :     GetCommonUnit(aProperty, result.GetUnit(), aA.GetUnit());
    3337           0 :   switch (commonUnit) {
    3338             :     case eUnit_Filter: {
    3339             :       UniquePtr<nsCSSValueList> resultList =
    3340           0 :         AddWeightedFilterList(1.0, result.GetCSSValueListValue(),
    3341           0 :                               aCount, aA.GetCSSValueListValue(),
    3342           0 :                               ColorAdditionType::Unclamped);
    3343           0 :       if (resultList) {
    3344           0 :         result.SetAndAdoptCSSValueListValue(resultList.release(), eUnit_Filter);
    3345             :       }
    3346           0 :       break;
    3347             :     }
    3348             :     case eUnit_Shadow: {
    3349             :       UniquePtr<nsCSSValueList> resultList =
    3350           0 :         AddWeightedShadowList(1.0, result.GetCSSValueListValue(),
    3351           0 :                               aCount, aA.GetCSSValueListValue(),
    3352             :                               ColorAdditionType::Unclamped,
    3353           0 :                               aProperty);
    3354           0 :       if (resultList) {
    3355           0 :         result.SetAndAdoptCSSValueListValue(resultList.release(), eUnit_Shadow);
    3356             :       }
    3357           0 :       break;
    3358             :     }
    3359             :     case eUnit_Color: {
    3360           0 :       RGBAColorData color1 = ExtractColor(result);
    3361           0 :       RGBAColorData color2 = ExtractColor(aA);
    3362           0 :       result.mValue.mCSSValue->SetRGBAColorValue(
    3363           0 :         AddWeightedColors(1.0, color1, aCount, color2));
    3364           0 :       break;
    3365             :     }
    3366             :     case eUnit_Transform: {
    3367             :       const nsCSSValueList* listA =
    3368           0 :         aA.GetCSSValueSharedListValue()->mHead;
    3369             :       const nsCSSValueList* listB =
    3370           0 :         result.GetCSSValueSharedListValue()->mHead;
    3371             : 
    3372           0 :       MOZ_ASSERT(listA);
    3373           0 :       MOZ_ASSERT(listB);
    3374             : 
    3375           0 :       nsAutoPtr<nsCSSValueList> resultList;
    3376           0 :       if (listA->mValue.GetUnit() == eCSSUnit_None) {
    3377             :         // If |aA| is 'none' then we are calculating:
    3378             :         //
    3379             :         //    none * |aCount| + |aB|
    3380             :         //    = none + |aB|
    3381             :         //    = |aB|
    3382             :         //
    3383             :         // Hence the result should just be |aB|, even if |aB| is also 'none'.
    3384             :         // Since |result| is already initialized to |aB|, we just return that.
    3385           0 :         break;
    3386           0 :       } else if (listB->mValue.GetUnit() == eCSSUnit_None) {
    3387             :         resultList = AddTransformLists(0.0, listA, aCount, listA,
    3388           0 :                                        eCSSKeyword_accumulatematrix);
    3389           0 :       } else if (TransformFunctionListsMatch(listA, listB)) {
    3390             :         resultList = AddTransformLists(1.0, listB, aCount, listA,
    3391           0 :                                        eCSSKeyword_accumulatematrix);
    3392             :       } else {
    3393             :         resultList = AddDifferentTransformLists(1.0, listB,
    3394             :                                                 aCount, listA,
    3395           0 :                                                 eCSSKeyword_accumulatematrix);
    3396             :       }
    3397           0 :       result.SetTransformValue(new nsCSSValueSharedList(resultList.forget()));
    3398           0 :       break;
    3399             :     }
    3400             :     default:
    3401           0 :       Unused << AddWeighted(aProperty,
    3402             :                             1.0, result,
    3403             :                             aCount, aA,
    3404             :                             result);
    3405           0 :       break;
    3406             :   }
    3407           0 :   return result;
    3408             : }
    3409             : 
    3410             : already_AddRefed<css::StyleRule>
    3411           0 : BuildStyleRule(nsCSSPropertyID aProperty,
    3412             :                dom::Element* aTargetElement,
    3413             :                const nsAString& aSpecifiedValue,
    3414             :                bool aUseSVGMode)
    3415             : {
    3416             :   // Set up an empty CSS Declaration
    3417           0 :   RefPtr<css::Declaration> declaration(new css::Declaration());
    3418           0 :   declaration->InitializeEmpty();
    3419             : 
    3420             :   bool changed; // ignored, but needed as outparam for ParseProperty
    3421           0 :   nsIDocument* doc = aTargetElement->OwnerDoc();
    3422           0 :   nsCOMPtr<nsIURI> baseURI = aTargetElement->GetBaseURI();
    3423           0 :   nsCSSParser parser(doc->CSSLoader());
    3424             : 
    3425           0 :   nsCSSPropertyID propertyToCheck = nsCSSProps::IsShorthand(aProperty) ?
    3426           0 :     nsCSSProps::SubpropertyEntryFor(aProperty)[0] : aProperty;
    3427             : 
    3428             :   // Get a parser, parse the property, and check for CSS parsing errors.
    3429             :   // If this fails, we bail out and delete the declaration.
    3430           0 :   parser.ParseProperty(aProperty, aSpecifiedValue, doc->GetDocumentURI(),
    3431             :                        baseURI, aTargetElement->NodePrincipal(), declaration,
    3432           0 :                        &changed, false, aUseSVGMode);
    3433             : 
    3434             :   // check whether property parsed without CSS parsing errors
    3435           0 :   if (!declaration->HasNonImportantValueFor(propertyToCheck)) {
    3436           0 :     return nullptr;
    3437             :   }
    3438             : 
    3439             :   RefPtr<css::StyleRule> rule = new css::StyleRule(nullptr,
    3440             :                                                      declaration,
    3441           0 :                                                      0, 0);
    3442           0 :   return rule.forget();
    3443             : }
    3444             : 
    3445             : already_AddRefed<css::StyleRule>
    3446          16 : BuildStyleRule(nsCSSPropertyID aProperty,
    3447             :                dom::Element* aTargetElement,
    3448             :                const nsCSSValue& aSpecifiedValue,
    3449             :                bool aUseSVGMode)
    3450             : {
    3451          16 :   MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
    3452             :              "Should be a longhand property");
    3453             : 
    3454             :   // Check if longhand failed to parse correctly.
    3455          16 :   if (aSpecifiedValue.GetUnit() == eCSSUnit_Null) {
    3456           0 :     return nullptr;
    3457             :   }
    3458             : 
    3459             :   // Set up an empty CSS Declaration
    3460          32 :   RefPtr<css::Declaration> declaration(new css::Declaration());
    3461          16 :   declaration->InitializeEmpty();
    3462             : 
    3463             :   // Add our longhand value
    3464          32 :   nsCSSExpandedDataBlock block;
    3465          16 :   declaration->ExpandTo(&block);
    3466          16 :   block.AddLonghandProperty(aProperty, aSpecifiedValue);
    3467          16 :   declaration->ValueAppended(aProperty);
    3468          16 :   declaration->CompressFrom(&block);
    3469             : 
    3470          48 :   RefPtr<css::StyleRule> rule = new css::StyleRule(nullptr, declaration, 0, 0);
    3471          16 :   return rule.forget();
    3472             : }
    3473             : 
    3474             : static bool
    3475          16 : ComputeValuesFromStyleContext(
    3476             :   nsCSSPropertyID aProperty,
    3477             :   CSSEnabledState aEnabledState,
    3478             :   nsStyleContext* aStyleContext,
    3479             :   nsTArray<PropertyStyleAnimationValuePair>& aValues)
    3480             : {
    3481             :   // Extract computed value of our property (or all longhand components, if
    3482             :   // aProperty is a shorthand) from the temporary style context
    3483          16 :   if (nsCSSProps::IsShorthand(aProperty)) {
    3484           0 :     CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty, aEnabledState) {
    3485           0 :       if (nsCSSProps::kAnimTypeTable[*p] == eStyleAnimType_None) {
    3486             :         // Skip non-animatable component longhands.
    3487           0 :         continue;
    3488             :       }
    3489           0 :       PropertyStyleAnimationValuePair* pair = aValues.AppendElement();
    3490           0 :       pair->mProperty = *p;
    3491           0 :       if (!StyleAnimationValue::ExtractComputedValue(*p, aStyleContext,
    3492             :                                                      pair->mValue.mGecko)) {
    3493           0 :         return false;
    3494             :       }
    3495             :     }
    3496           0 :     return true;
    3497             :   }
    3498             : 
    3499          16 :   PropertyStyleAnimationValuePair* pair = aValues.AppendElement();
    3500          16 :   pair->mProperty = aProperty;
    3501          16 :   return StyleAnimationValue::ExtractComputedValue(aProperty, aStyleContext,
    3502          16 :                                                    pair->mValue.mGecko);
    3503             : }
    3504             : 
    3505             : static bool
    3506          16 : ComputeValuesFromStyleRule(nsCSSPropertyID aProperty,
    3507             :                            CSSEnabledState aEnabledState,
    3508             :                            nsStyleContext* aStyleContext,
    3509             :                            css::StyleRule* aStyleRule,
    3510             :                            nsTArray<PropertyStyleAnimationValuePair>& aValues,
    3511             :                            bool* aIsContextSensitive)
    3512             : {
    3513          16 :   MOZ_ASSERT(aStyleContext);
    3514          16 :   if (!nsCSSProps::IsEnabled(aProperty, aEnabledState)) {
    3515           0 :     return false;
    3516             :   }
    3517             : 
    3518          16 :   MOZ_ASSERT(aStyleContext->PresContext()->StyleSet()->IsGecko(),
    3519             :              "ServoStyleSet should not use StyleAnimationValue for animations");
    3520          16 :   nsStyleSet* styleSet = aStyleContext->PresContext()->StyleSet()->AsGecko();
    3521             : 
    3522          32 :   RefPtr<nsStyleContext> tmpStyleContext;
    3523          16 :   if (aIsContextSensitive) {
    3524           0 :     MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
    3525             :                "to correctly set aIsContextSensitive for shorthand properties, "
    3526             :                "this code must be adjusted");
    3527             : 
    3528           0 :     nsCOMArray<nsIStyleRule> ruleArray;
    3529           0 :     ruleArray.AppendObject(styleSet->InitialStyleRule());
    3530           0 :     css::Declaration* declaration = aStyleRule->GetDeclaration();
    3531           0 :     ruleArray.AppendObject(declaration);
    3532           0 :     declaration->SetImmutable();
    3533             :     tmpStyleContext =
    3534           0 :       styleSet->ResolveStyleByAddingRules(aStyleContext, ruleArray);
    3535           0 :     if (!tmpStyleContext) {
    3536           0 :       return false;
    3537             :     }
    3538             : 
    3539             :     // Force walk of rule tree
    3540           0 :     nsStyleStructID sid = nsCSSProps::kSIDTable[aProperty];
    3541           0 :     tmpStyleContext->AsGecko()->StyleData(sid);
    3542             : 
    3543             :     // The rule node will have unconditional cached style data if the value is
    3544             :     // not context-sensitive.  So if there's nothing cached, it's not context
    3545             :     // sensitive.
    3546           0 :     *aIsContextSensitive =
    3547           0 :       !tmpStyleContext->RuleNode()->NodeHasCachedUnconditionalData(sid);
    3548             :   }
    3549             : 
    3550             :   // If we're not concerned whether the property is context sensitive then just
    3551             :   // add the rule to a new temporary style context alongside the target
    3552             :   // element's style context.
    3553             :   // Also, if we previously discovered that this property IS context-sensitive
    3554             :   // then we need to throw the temporary style context out since the property's
    3555             :   // value may have been biased by the 'initial' values supplied.
    3556          16 :   if (!aIsContextSensitive || *aIsContextSensitive) {
    3557          32 :     nsCOMArray<nsIStyleRule> ruleArray;
    3558          16 :     css::Declaration* declaration = aStyleRule->GetDeclaration();
    3559          16 :     ruleArray.AppendObject(declaration);
    3560          16 :     declaration->SetImmutable();
    3561             :     tmpStyleContext =
    3562          16 :       styleSet->ResolveStyleByAddingRules(aStyleContext, ruleArray);
    3563          16 :     if (!tmpStyleContext) {
    3564           0 :       return false;
    3565             :     }
    3566             :   }
    3567             : 
    3568          16 :   return ComputeValuesFromStyleContext(aProperty, aEnabledState,
    3569          16 :                                        tmpStyleContext, aValues);
    3570             : }
    3571             : 
    3572             : /* static */ bool
    3573           0 : StyleAnimationValue::ComputeValue(nsCSSPropertyID aProperty,
    3574             :                                   dom::Element* aTargetElement,
    3575             :                                   nsStyleContext* aStyleContext,
    3576             :                                   const nsAString& aSpecifiedValue,
    3577             :                                   bool aUseSVGMode,
    3578             :                                   StyleAnimationValue& aComputedValue,
    3579             :                                   bool* aIsContextSensitive)
    3580             : {
    3581           0 :   MOZ_ASSERT(aTargetElement, "null target element");
    3582             : 
    3583             :   // Parse specified value into a temporary css::StyleRule
    3584             :   // Note: BuildStyleRule needs an element's OwnerDoc, BaseURI, and Principal.
    3585             :   // If it is a pseudo element, use its parent element's OwnerDoc, BaseURI,
    3586             :   // and Principal.
    3587             :   RefPtr<css::StyleRule> styleRule =
    3588           0 :     BuildStyleRule(aProperty, aTargetElement, aSpecifiedValue, aUseSVGMode);
    3589           0 :   if (!styleRule) {
    3590           0 :     return false;
    3591             :   }
    3592             : 
    3593           0 :   if (nsCSSProps::IsShorthand(aProperty) ||
    3594           0 :       nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) {
    3595             :     // Just capture the specified value
    3596           0 :     aComputedValue.SetUnparsedStringValue(nsString(aSpecifiedValue));
    3597           0 :     if (aIsContextSensitive) {
    3598             :       // Since we're just returning the string as-is, aComputedValue isn't going
    3599             :       // to change depending on the context
    3600           0 :       *aIsContextSensitive = false;
    3601             :     }
    3602           0 :     return true;
    3603             :   }
    3604             : 
    3605           0 :   AutoTArray<PropertyStyleAnimationValuePair,1> values;
    3606           0 :   bool ok = ComputeValuesFromStyleRule(aProperty,
    3607             :                                        CSSEnabledState::eIgnoreEnabledState,
    3608             :                                        aStyleContext, styleRule,
    3609           0 :                                        values, aIsContextSensitive);
    3610           0 :   if (!ok) {
    3611           0 :     return false;
    3612             :   }
    3613             : 
    3614           0 :   MOZ_ASSERT(values.Length() == 1);
    3615           0 :   MOZ_ASSERT(values[0].mProperty == aProperty);
    3616             : 
    3617           0 :   aComputedValue = values[0].mValue.mGecko;
    3618           0 :   return true;
    3619             : }
    3620             : 
    3621             : template <class T>
    3622             : bool
    3623          16 : ComputeValuesFromSpecifiedValue(
    3624             :     nsCSSPropertyID aProperty,
    3625             :     CSSEnabledState aEnabledState,
    3626             :     dom::Element* aTargetElement,
    3627             :     nsStyleContext* aStyleContext,
    3628             :     T& aSpecifiedValue,
    3629             :     bool aUseSVGMode,
    3630             :     nsTArray<PropertyStyleAnimationValuePair>& aResult)
    3631             : {
    3632          16 :   MOZ_ASSERT(aTargetElement, "null target element");
    3633             : 
    3634             :   // Parse specified value into a temporary css::StyleRule
    3635             :   // Note: BuildStyleRule needs an element's OwnerDoc, BaseURI, and Principal.
    3636             :   // If it is a pseudo element, use its parent element's OwnerDoc, BaseURI,
    3637             :   // and Principal.
    3638             :   RefPtr<css::StyleRule> styleRule =
    3639          32 :     BuildStyleRule(aProperty, aTargetElement, aSpecifiedValue, aUseSVGMode);
    3640          16 :   if (!styleRule) {
    3641           0 :     return false;
    3642             :   }
    3643             : 
    3644          16 :   aResult.Clear();
    3645          16 :   return ComputeValuesFromStyleRule(aProperty, aEnabledState,
    3646             :                                     aStyleContext, styleRule, aResult,
    3647          16 :                                     /* aIsContextSensitive */ nullptr);
    3648             : }
    3649             : 
    3650             : /* static */ bool
    3651           0 : StyleAnimationValue::ComputeValues(
    3652             :     nsCSSPropertyID aProperty,
    3653             :     CSSEnabledState aEnabledState,
    3654             :     dom::Element* aTargetElement,
    3655             :     nsStyleContext* aStyleContext,
    3656             :     const nsAString& aSpecifiedValue,
    3657             :     bool aUseSVGMode,
    3658             :     nsTArray<PropertyStyleAnimationValuePair>& aResult)
    3659             : {
    3660           0 :   return ComputeValuesFromSpecifiedValue(aProperty, aEnabledState,
    3661             :                                          aTargetElement, aStyleContext,
    3662             :                                          aSpecifiedValue, aUseSVGMode,
    3663           0 :                                          aResult);
    3664             : }
    3665             : 
    3666             : /* static */ bool
    3667          16 : StyleAnimationValue::ComputeValues(
    3668             :     nsCSSPropertyID aProperty,
    3669             :     CSSEnabledState aEnabledState,
    3670             :     dom::Element* aTargetElement,
    3671             :     nsStyleContext* aStyleContext,
    3672             :     const nsCSSValue& aSpecifiedValue,
    3673             :     bool aUseSVGMode,
    3674             :     nsTArray<PropertyStyleAnimationValuePair>& aResult)
    3675             : {
    3676          16 :   return ComputeValuesFromSpecifiedValue(aProperty, aEnabledState,
    3677             :                                          aTargetElement, aStyleContext,
    3678             :                                          aSpecifiedValue, aUseSVGMode,
    3679          16 :                                          aResult);
    3680             : }
    3681             : 
    3682             : bool
    3683          14 : StyleAnimationValue::UncomputeValue(nsCSSPropertyID aProperty,
    3684             :                                     const StyleAnimationValue& aComputedValue,
    3685             :                                     nsCSSValue& aSpecifiedValue)
    3686             : {
    3687          14 :   Unit unit = aComputedValue.GetUnit();
    3688          14 :   switch (unit) {
    3689             :     case eUnit_Normal:
    3690           0 :       aSpecifiedValue.SetNormalValue();
    3691           0 :       break;
    3692             :     case eUnit_Auto:
    3693           0 :       aSpecifiedValue.SetAutoValue();
    3694           0 :       break;
    3695             :     case eUnit_None:
    3696           0 :       aSpecifiedValue.SetNoneValue();
    3697           0 :       break;
    3698             :     case eUnit_Enumerated:
    3699             :     case eUnit_Visibility:
    3700             :       aSpecifiedValue.
    3701           0 :         SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Enumerated);
    3702           0 :       break;
    3703             :     case eUnit_Integer:
    3704             :       aSpecifiedValue.
    3705           0 :         SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Integer);
    3706           0 :       break;
    3707             :     case eUnit_Coord:
    3708           0 :       aSpecifiedValue.SetIntegerCoordValue(aComputedValue.GetCoordValue());
    3709           0 :       break;
    3710             :     case eUnit_Percent:
    3711           0 :       aSpecifiedValue.SetPercentValue(aComputedValue.GetPercentValue());
    3712           0 :       break;
    3713             :     case eUnit_Float:
    3714             :       aSpecifiedValue.
    3715          14 :         SetFloatValue(aComputedValue.GetFloatValue(), eCSSUnit_Number);
    3716          14 :       break;
    3717             :     case eUnit_CurrentColor:
    3718           0 :       aSpecifiedValue.SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
    3719           0 :       break;
    3720             :     case eUnit_Calc:
    3721             :     case eUnit_Color:
    3722             :     case eUnit_ObjectPosition:
    3723             :     case eUnit_URL:
    3724             :     case eUnit_DiscreteCSSValue: {
    3725           0 :       nsCSSValue* val = aComputedValue.GetCSSValueValue();
    3726             :       // Sanity-check that the underlying unit in the nsCSSValue is what we
    3727             :       // expect for our StyleAnimationValue::Unit:
    3728           0 :       MOZ_ASSERT((unit == eUnit_Calc && val->GetUnit() == eCSSUnit_Calc) ||
    3729             :                  (unit == eUnit_Color &&
    3730             :                   nsCSSValue::IsNumericColorUnit(val->GetUnit())) ||
    3731             :                  (unit == eUnit_ObjectPosition &&
    3732             :                   val->GetUnit() == eCSSUnit_Array) ||
    3733             :                  (unit == eUnit_URL && val->GetUnit() == eCSSUnit_URL) ||
    3734             :                  unit == eUnit_DiscreteCSSValue,
    3735             :                  "unexpected unit");
    3736           0 :       aSpecifiedValue = *val;
    3737           0 :       break;
    3738             :     }
    3739             :     case eUnit_ComplexColor: {
    3740             :       aSpecifiedValue.SetComplexColorValue(
    3741           0 :         do_AddRef(aComputedValue.mValue.mComplexColor));
    3742           0 :       break;
    3743             :     }
    3744             :     case eUnit_CSSValuePair: {
    3745             :       // Rule node processing expects pair values to be collapsed to a
    3746             :       // single value if both halves would be equal, for most but not
    3747             :       // all properties.  At present, all animatable properties that
    3748             :       // use pairs do expect collapsing.
    3749           0 :       const nsCSSValuePair* pair = aComputedValue.GetCSSValuePairValue();
    3750           0 :       if (pair->mXValue == pair->mYValue) {
    3751           0 :         aSpecifiedValue = pair->mXValue;
    3752             :       } else {
    3753           0 :         aSpecifiedValue.SetPairValue(pair);
    3754             :       }
    3755           0 :     } break;
    3756             :     case eUnit_CSSValueTriplet: {
    3757             :       // Rule node processing expects triplet values to be collapsed to a
    3758             :       // single value if both halves would be equal, for most but not
    3759             :       // all properties.  At present, all animatable properties that
    3760             :       // use pairs do expect collapsing.
    3761           0 :       const nsCSSValueTriplet* triplet = aComputedValue.GetCSSValueTripletValue();
    3762           0 :       if (triplet->mXValue == triplet->mYValue && triplet->mYValue == triplet->mZValue) {
    3763           0 :         aSpecifiedValue = triplet->mXValue;
    3764             :       } else {
    3765           0 :         aSpecifiedValue.SetTripletValue(triplet);
    3766             :       }
    3767           0 :     } break;
    3768             :     case eUnit_CSSRect: {
    3769           0 :       nsCSSRect& rect = aSpecifiedValue.SetRectValue();
    3770           0 :       rect = *aComputedValue.GetCSSRectValue();
    3771           0 :     } break;
    3772             :     case eUnit_Dasharray:
    3773             :     case eUnit_Shadow:
    3774             :     case eUnit_Filter:
    3775             :     case eUnit_BackgroundPositionCoord:
    3776             :       {
    3777           0 :         nsCSSValueList* computedList = aComputedValue.GetCSSValueListValue();
    3778           0 :         if (computedList) {
    3779           0 :           aSpecifiedValue.SetDependentListValue(computedList);
    3780             :         } else {
    3781           0 :           aSpecifiedValue.SetNoneValue();
    3782             :         }
    3783             :       }
    3784           0 :       break;
    3785             :     case eUnit_Shape: {
    3786           0 :       nsCSSValue::Array* computedArray = aComputedValue.GetCSSValueArrayValue();
    3787           0 :       aSpecifiedValue.SetArrayValue(computedArray, eCSSUnit_Array);
    3788           0 :       break;
    3789             :     }
    3790             :     case eUnit_Transform:
    3791             :       aSpecifiedValue.
    3792           0 :         SetSharedListValue(aComputedValue.GetCSSValueSharedListValue());
    3793           0 :       break;
    3794             :     case eUnit_CSSValuePairList:
    3795             :       aSpecifiedValue.
    3796           0 :         SetDependentPairListValue(aComputedValue.GetCSSValuePairListValue());
    3797           0 :       break;
    3798             :     default:
    3799           0 :       return false;
    3800             :   }
    3801          14 :   return true;
    3802             : }
    3803             : 
    3804             : bool
    3805           4 : StyleAnimationValue::UncomputeValue(nsCSSPropertyID aProperty,
    3806             :                                     StyleAnimationValue&& aComputedValue,
    3807             :                                     nsCSSValue& aSpecifiedValue)
    3808             : {
    3809           4 :   Unit unit = aComputedValue.GetUnit();
    3810           4 :   switch (unit) {
    3811             :     case eUnit_Dasharray:
    3812             :     case eUnit_Shadow:
    3813             :     case eUnit_Filter:
    3814             :     case eUnit_BackgroundPositionCoord:
    3815             :       {
    3816             :         UniquePtr<nsCSSValueList> computedList =
    3817           0 :           aComputedValue.TakeCSSValueListValue();
    3818           0 :         if (computedList) {
    3819           0 :           aSpecifiedValue.AdoptListValue(Move(computedList));
    3820             :         } else {
    3821           0 :           aSpecifiedValue.SetNoneValue();
    3822             :         }
    3823             :       }
    3824           0 :       break;
    3825             :     case eUnit_CSSValuePairList:
    3826             :       {
    3827             :         UniquePtr<nsCSSValuePairList> computedList =
    3828           0 :           aComputedValue.TakeCSSValuePairListValue();
    3829           0 :         MOZ_ASSERT(computedList, "Pair list should never be null");
    3830           0 :         aSpecifiedValue.AdoptPairListValue(Move(computedList));
    3831             :       }
    3832           0 :       break;
    3833             :     default:
    3834           4 :       return UncomputeValue(aProperty, aComputedValue, aSpecifiedValue);
    3835             :   }
    3836           0 :   return true;
    3837             : }
    3838             : 
    3839             : bool
    3840           0 : StyleAnimationValue::UncomputeValue(nsCSSPropertyID aProperty,
    3841             :                                     const StyleAnimationValue& aComputedValue,
    3842             :                                     nsAString& aSpecifiedValue)
    3843             : {
    3844           0 :   aSpecifiedValue.Truncate(); // Clear outparam, if it's not already empty
    3845             : 
    3846           0 :   if (aComputedValue.GetUnit() == eUnit_UnparsedString) {
    3847           0 :     aComputedValue.GetStringValue(aSpecifiedValue);
    3848           0 :     return true;
    3849             :   }
    3850           0 :   nsCSSValue val;
    3851           0 :   if (!StyleAnimationValue::UncomputeValue(aProperty, aComputedValue, val)) {
    3852           0 :     return false;
    3853             :   }
    3854             : 
    3855           0 :   val.AppendToString(aProperty, aSpecifiedValue, nsCSSValue::eNormalized);
    3856           0 :   return true;
    3857             : }
    3858             : 
    3859             : template<typename T>
    3860             : inline const T&
    3861         508 : StyleDataAtOffset(const void* aStyleStruct, ptrdiff_t aOffset)
    3862             : {
    3863             :   return *reinterpret_cast<const T*>(
    3864         508 :     reinterpret_cast<const uint8_t*>(aStyleStruct) + aOffset);
    3865             : }
    3866             : 
    3867             : static bool
    3868          40 : StyleCoordToValue(const nsStyleCoord& aCoord, StyleAnimationValue& aValue)
    3869             : {
    3870          40 :   switch (aCoord.GetUnit()) {
    3871             :     case eStyleUnit_Normal:
    3872           0 :       aValue.SetNormalValue();
    3873           0 :       break;
    3874             :     case eStyleUnit_Auto:
    3875           0 :       aValue.SetAutoValue();
    3876           0 :       break;
    3877             :     case eStyleUnit_None:
    3878           8 :       aValue.SetNoneValue();
    3879           8 :       break;
    3880             :     case eStyleUnit_Percent:
    3881           0 :       aValue.SetPercentValue(aCoord.GetPercentValue());
    3882           0 :       break;
    3883             :     case eStyleUnit_Factor:
    3884           0 :       aValue.SetFloatValue(aCoord.GetFactorValue());
    3885           0 :       break;
    3886             :     case eStyleUnit_Coord:
    3887          32 :       aValue.SetCoordValue(aCoord.GetCoordValue());
    3888          32 :       break;
    3889             :     case eStyleUnit_Enumerated:
    3890           0 :       aValue.SetIntValue(aCoord.GetIntValue(),
    3891           0 :                          StyleAnimationValue::eUnit_Enumerated);
    3892           0 :       break;
    3893             :     case eStyleUnit_Integer:
    3894           0 :       aValue.SetIntValue(aCoord.GetIntValue(),
    3895           0 :                          StyleAnimationValue::eUnit_Integer);
    3896           0 :       break;
    3897             :     case eStyleUnit_Calc: {
    3898           0 :       nsAutoPtr<nsCSSValue> val(new nsCSSValue);
    3899           0 :       CalcValueToCSSValue(aCoord.GetCalcValue(), *val);
    3900           0 :       aValue.SetAndAdoptCSSValueValue(val.forget(),
    3901           0 :                                       StyleAnimationValue::eUnit_Calc);
    3902           0 :       break;
    3903             :     }
    3904             :     default:
    3905           0 :       return false;
    3906             :   }
    3907          40 :   return true;
    3908             : }
    3909             : 
    3910             : static bool
    3911           0 : StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue)
    3912             : {
    3913           0 :   switch (aCoord.GetUnit()) {
    3914             :     case eStyleUnit_Coord:
    3915           0 :       aCSSValue.SetIntegerCoordValue(aCoord.GetCoordValue());
    3916           0 :       break;
    3917             :     case eStyleUnit_Factor:
    3918           0 :       aCSSValue.SetFloatValue(aCoord.GetFactorValue(), eCSSUnit_Number);
    3919           0 :       break;
    3920             :     case eStyleUnit_Percent:
    3921           0 :       aCSSValue.SetPercentValue(aCoord.GetPercentValue());
    3922           0 :       break;
    3923             :     case eStyleUnit_Calc:
    3924           0 :       CalcValueToCSSValue(aCoord.GetCalcValue(), aCSSValue);
    3925           0 :       break;
    3926             :     case eStyleUnit_Degree:
    3927           0 :       aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Degree);
    3928           0 :       break;
    3929             :     case eStyleUnit_Grad:
    3930           0 :       aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Grad);
    3931           0 :       break;
    3932             :     case eStyleUnit_Radian:
    3933           0 :       aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Radian);
    3934           0 :       break;
    3935             :     case eStyleUnit_Turn:
    3936           0 :       aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Turn);
    3937           0 :       break;
    3938             :     default:
    3939           0 :       MOZ_ASSERT(false, "unexpected unit");
    3940             :       return false;
    3941             :   }
    3942           0 :   return true;
    3943             : }
    3944             : 
    3945             : static void
    3946           0 : SetPositionValue(const Position& aPos, nsCSSValue& aCSSValue)
    3947             : {
    3948           0 :   RefPtr<nsCSSValue::Array> posArray = nsCSSValue::Array::Create(4);
    3949           0 :   aCSSValue.SetArrayValue(posArray.get(), eCSSUnit_Array);
    3950             : 
    3951             :   // NOTE: Array entries #0 and #2 here are intentionally left untouched, with
    3952             :   // eCSSUnit_Null.  The purpose of these entries in our specified-style
    3953             :   // <position> representation is to store edge names.  But for values
    3954             :   // extracted from computed style (which is what we're dealing with here),
    3955             :   // we'll just have a normalized "x,y" position, with no edge names needed.
    3956           0 :   nsCSSValue& xValue = posArray->Item(1);
    3957           0 :   nsCSSValue& yValue = posArray->Item(3);
    3958             : 
    3959           0 :   CalcValueToCSSValue(&aPos.mXPosition, xValue);
    3960           0 :   CalcValueToCSSValue(&aPos.mYPosition, yValue);
    3961           0 : }
    3962             : 
    3963             : static void
    3964           0 : SetPositionCoordValue(const Position::Coord& aPosCoord,
    3965             :                       nsCSSValue& aCSSValue)
    3966             : {
    3967           0 :   RefPtr<nsCSSValue::Array> posArray = nsCSSValue::Array::Create(2);
    3968           0 :   aCSSValue.SetArrayValue(posArray.get(), eCSSUnit_Array);
    3969             : 
    3970             :   // NOTE: Array entry #0 here is intentionally left untouched, with
    3971             :   // eCSSUnit_Null.  The purpose of this entry in our specified-style
    3972             :   // <position-coord> representation is to store edge names.  But for values
    3973             :   // extracted from computed style (which is what we're dealing with here),
    3974             :   // we'll just have a normalized "x"/"y" position, with no edge names needed.
    3975           0 :   nsCSSValue& value = posArray->Item(1);
    3976             : 
    3977           0 :   CalcValueToCSSValue(&aPosCoord, value);
    3978           0 : }
    3979             : 
    3980             : /*
    3981             :  * Assign |aOutput = aInput|, except with any non-pixel lengths
    3982             :  * replaced with the equivalent in pixels, and any non-canonical calc()
    3983             :  * expressions replaced with canonical ones.
    3984             :  */
    3985             : static void
    3986           0 : SubstitutePixelValues(nsStyleContext* aStyleContext,
    3987             :                       const nsCSSValue& aInput, nsCSSValue& aOutput)
    3988             : {
    3989           0 :   if (aInput.IsCalcUnit()) {
    3990           0 :     RuleNodeCacheConditions conditions;
    3991             :     nsRuleNode::ComputedCalc c =
    3992           0 :       nsRuleNode::SpecifiedCalcToComputedCalc(aInput, aStyleContext->AsGecko(),
    3993             :                                               aStyleContext->PresContext(),
    3994           0 :                                               conditions);
    3995             :     nsStyleCoord::CalcValue c2;
    3996           0 :     c2.mLength = c.mLength;
    3997           0 :     c2.mPercent = c.mPercent;
    3998           0 :     c2.mHasPercent = true; // doesn't matter for transform translate
    3999           0 :     CalcValueToCSSValue(&c2, aOutput);
    4000           0 :   } else if (aInput.UnitHasArrayValue()) {
    4001           0 :     const nsCSSValue::Array *inputArray = aInput.GetArrayValue();
    4002             :     RefPtr<nsCSSValue::Array> outputArray =
    4003           0 :       nsCSSValue::Array::Create(inputArray->Count());
    4004           0 :     for (size_t i = 0, i_end = inputArray->Count(); i < i_end; ++i) {
    4005           0 :       SubstitutePixelValues(aStyleContext,
    4006           0 :                             inputArray->Item(i), outputArray->Item(i));
    4007             :     }
    4008           0 :     aOutput.SetArrayValue(outputArray, aInput.GetUnit());
    4009           0 :   } else if (aInput.IsLengthUnit() &&
    4010           0 :              aInput.GetUnit() != eCSSUnit_Pixel) {
    4011           0 :     RuleNodeCacheConditions conditions;
    4012           0 :     nscoord len = nsRuleNode::CalcLength(aInput, aStyleContext->AsGecko(),
    4013             :                                          aStyleContext->PresContext(),
    4014           0 :                                          conditions);
    4015           0 :     aOutput.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(len),
    4016           0 :                           eCSSUnit_Pixel);
    4017             :   } else {
    4018           0 :     aOutput = aInput;
    4019             :   }
    4020           0 : }
    4021             : 
    4022             : static void
    4023           0 : ExtractImageLayerPositionXList(const nsStyleImageLayers& aLayer,
    4024             :                                StyleAnimationValue& aComputedValue)
    4025             : {
    4026           0 :   MOZ_ASSERT(aLayer.mPositionXCount > 0, "unexpected count");
    4027             : 
    4028           0 :   nsAutoPtr<nsCSSValueList> result;
    4029           0 :   nsCSSValueList **resultTail = getter_Transfers(result);
    4030           0 :   for (uint32_t i = 0, i_end = aLayer.mPositionXCount; i != i_end; ++i) {
    4031           0 :     nsCSSValueList *item = new nsCSSValueList;
    4032           0 :     *resultTail = item;
    4033           0 :     resultTail = &item->mNext;
    4034           0 :     SetPositionCoordValue(aLayer.mLayers[i].mPosition.mXPosition,
    4035           0 :                           item->mValue);
    4036             :   }
    4037             : 
    4038           0 :   aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
    4039           0 :     StyleAnimationValue::eUnit_BackgroundPositionCoord);
    4040           0 : }
    4041             : 
    4042             : static void
    4043           0 : ExtractImageLayerPositionYList(const nsStyleImageLayers& aLayer,
    4044             :                                StyleAnimationValue& aComputedValue)
    4045             : {
    4046           0 :   MOZ_ASSERT(aLayer.mPositionYCount > 0, "unexpected count");
    4047             : 
    4048           0 :   nsAutoPtr<nsCSSValueList> result;
    4049           0 :   nsCSSValueList **resultTail = getter_Transfers(result);
    4050           0 :   for (uint32_t i = 0, i_end = aLayer.mPositionYCount; i != i_end; ++i) {
    4051           0 :     nsCSSValueList *item = new nsCSSValueList;
    4052           0 :     *resultTail = item;
    4053           0 :     resultTail = &item->mNext;
    4054           0 :     SetPositionCoordValue(aLayer.mLayers[i].mPosition.mYPosition,
    4055           0 :                           item->mValue);
    4056             :   }
    4057             : 
    4058           0 :   aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
    4059           0 :     StyleAnimationValue::eUnit_BackgroundPositionCoord);
    4060           0 : }
    4061             : 
    4062             : static void
    4063           0 : ExtractImageLayerSizePairList(const nsStyleImageLayers& aLayer,
    4064             :                               StyleAnimationValue& aComputedValue)
    4065             : {
    4066           0 :   MOZ_ASSERT(aLayer.mSizeCount > 0, "unexpected count");
    4067             : 
    4068           0 :   nsAutoPtr<nsCSSValuePairList> result;
    4069           0 :   nsCSSValuePairList **resultTail = getter_Transfers(result);
    4070           0 :   for (uint32_t i = 0, i_end = aLayer.mSizeCount; i != i_end; ++i) {
    4071           0 :     nsCSSValuePairList *item = new nsCSSValuePairList;
    4072           0 :     *resultTail = item;
    4073           0 :     resultTail = &item->mNext;
    4074             : 
    4075           0 :     const nsStyleImageLayers::Size &size = aLayer.mLayers[i].mSize;
    4076           0 :     switch (size.mWidthType) {
    4077             :       case nsStyleImageLayers::Size::eContain:
    4078             :       case nsStyleImageLayers::Size::eCover:
    4079           0 :         item->mXValue.SetIntValue(size.mWidthType,
    4080           0 :                                   eCSSUnit_Enumerated);
    4081           0 :         break;
    4082             :       case nsStyleImageLayers::Size::eAuto:
    4083           0 :         item->mXValue.SetAutoValue();
    4084           0 :         break;
    4085             :       case nsStyleImageLayers::Size::eLengthPercentage:
    4086             :         // XXXbz is there a good reason we can't just
    4087             :         // CalcValueToCSSValue(&size.mWidth, item->mXValue) here?
    4088           0 :         if (!size.mWidth.mHasPercent &&
    4089             :             // negative values must have come from calc()
    4090           0 :             size.mWidth.mLength >= 0) {
    4091           0 :           MOZ_ASSERT(size.mWidth.mPercent == 0.0f,
    4092             :                      "Shouldn't have mPercent");
    4093           0 :           item->mXValue.SetIntegerCoordValue(size.mWidth.mLength);
    4094           0 :         } else if (size.mWidth.mLength == 0 &&
    4095             :                    // negative values must have come from calc()
    4096           0 :                    size.mWidth.mPercent >= 0.0f) {
    4097           0 :           item->mXValue.SetPercentValue(size.mWidth.mPercent);
    4098             :         } else {
    4099           0 :           CalcValueToCSSValue(&size.mWidth, item->mXValue);
    4100             :         }
    4101           0 :         break;
    4102             :     }
    4103             : 
    4104           0 :     switch (size.mHeightType) {
    4105             :       case nsStyleImageLayers::Size::eContain:
    4106             :       case nsStyleImageLayers::Size::eCover:
    4107             :         // leave it null
    4108           0 :         break;
    4109             :       case nsStyleImageLayers::Size::eAuto:
    4110           0 :         item->mYValue.SetAutoValue();
    4111           0 :         break;
    4112             :       case nsStyleImageLayers::Size::eLengthPercentage:
    4113             :         // XXXbz is there a good reason we can't just
    4114             :         // CalcValueToCSSValue(&size.mHeight, item->mYValue) here?
    4115           0 :         if (!size.mHeight.mHasPercent &&
    4116             :             // negative values must have come from calc()
    4117           0 :             size.mHeight.mLength >= 0) {
    4118           0 :           MOZ_ASSERT(size.mHeight.mPercent == 0.0f,
    4119             :                      "Shouldn't have mPercent");
    4120           0 :           item->mYValue.SetIntegerCoordValue(size.mHeight.mLength);
    4121           0 :         } else if (size.mHeight.mLength == 0 &&
    4122             :                    // negative values must have come from calc()
    4123           0 :                    size.mHeight.mPercent >= 0.0f) {
    4124           0 :           item->mYValue.SetPercentValue(size.mHeight.mPercent);
    4125             :         } else {
    4126           0 :           CalcValueToCSSValue(&size.mHeight, item->mYValue);
    4127             :         }
    4128           0 :         break;
    4129             :     }
    4130             :   }
    4131             : 
    4132           0 :   aComputedValue.SetAndAdoptCSSValuePairListValue(result.forget());
    4133           0 : }
    4134             : 
    4135             : static bool
    4136           0 : StyleClipBasicShapeToCSSArray(const StyleShapeSource& aClipPath,
    4137             :                               nsCSSValue::Array* aResult)
    4138             : {
    4139           0 :   MOZ_ASSERT(aResult->Count() == 2,
    4140             :              "Expected array to be presized for a function and the sizing-box");
    4141             : 
    4142           0 :   const StyleBasicShape* shape = aClipPath.GetBasicShape();
    4143           0 :   nsCSSKeyword functionName = shape->GetShapeTypeName();
    4144           0 :   RefPtr<nsCSSValue::Array> functionArray;
    4145           0 :   switch (shape->GetShapeType()) {
    4146             :     case StyleBasicShapeType::Circle:
    4147             :     case StyleBasicShapeType::Ellipse: {
    4148           0 :       const nsTArray<nsStyleCoord>& coords = shape->Coordinates();
    4149           0 :       MOZ_ASSERT(coords.Length() == ShapeArgumentCount(functionName) - 1,
    4150             :                  "Unexpected radii count");
    4151             :       // The "+1" is for the center point:
    4152           0 :       functionArray = aResult->Item(0).InitFunction(functionName,
    4153           0 :                                                     coords.Length() + 1);
    4154           0 :       for (size_t i = 0; i < coords.Length(); ++i) {
    4155           0 :         if (coords[i].GetUnit() == eStyleUnit_Enumerated) {
    4156           0 :           functionArray->Item(i + 1).SetIntValue(coords[i].GetIntValue(),
    4157           0 :                                                  eCSSUnit_Enumerated);
    4158           0 :         } else if (!StyleCoordToCSSValue(coords[i],
    4159             :                                          functionArray->Item(i + 1))) {
    4160           0 :           return false;
    4161             :         }
    4162             :       }
    4163             :       // Set functionArray's last item to the circle or ellipse's center point:
    4164           0 :       SetPositionValue(shape->GetPosition(),
    4165           0 :                        functionArray->Item(functionArray->Count() - 1));
    4166           0 :       break;
    4167             :     }
    4168             :     case StyleBasicShapeType::Polygon: {
    4169             :       functionArray =
    4170           0 :         aResult->Item(0).InitFunction(functionName,
    4171           0 :                                       ShapeArgumentCount(functionName));
    4172           0 :       functionArray->Item(1).SetEnumValue(shape->GetFillRule());
    4173           0 :       nsCSSValuePairList* list = functionArray->Item(2).SetPairListValue();
    4174           0 :       const nsTArray<nsStyleCoord>& coords = shape->Coordinates();
    4175           0 :       MOZ_ASSERT((coords.Length() % 2) == 0);
    4176           0 :       for (size_t i = 0; i < coords.Length(); i += 2) {
    4177           0 :         if (i > 0) {
    4178           0 :           list->mNext = new nsCSSValuePairList;
    4179           0 :           list = list->mNext;
    4180             :         }
    4181           0 :         if (!StyleCoordToCSSValue(coords[i], list->mXValue) ||
    4182           0 :             !StyleCoordToCSSValue(coords[i + 1], list->mYValue)) {
    4183           0 :           return false;
    4184             :         }
    4185             :       }
    4186           0 :       break;
    4187             :     }
    4188             :     case StyleBasicShapeType::Inset: {
    4189           0 :       const nsTArray<nsStyleCoord>& coords = shape->Coordinates();
    4190           0 :       MOZ_ASSERT(coords.Length() == ShapeArgumentCount(functionName) - 1,
    4191             :                  "Unexpected offset count");
    4192             :       functionArray =
    4193           0 :         aResult->Item(0).InitFunction(functionName, coords.Length() + 1);
    4194           0 :       for (size_t i = 0; i < coords.Length(); ++i) {
    4195           0 :         if (!StyleCoordToCSSValue(coords[i], functionArray->Item(i + 1))) {
    4196           0 :           return false;
    4197             :         }
    4198             :       }
    4199           0 :       RefPtr<nsCSSValue::Array> radiusArray = nsCSSValue::Array::Create(4);
    4200           0 :       const nsStyleCorners& radii = shape->GetRadius();
    4201           0 :       NS_FOR_CSS_FULL_CORNERS(corner) {
    4202           0 :         auto pair = MakeUnique<nsCSSValuePair>();
    4203           0 :         if (!StyleCoordToCSSValue(radii.Get(FullToHalfCorner(corner, false)),
    4204           0 :                                   pair->mXValue) ||
    4205           0 :             !StyleCoordToCSSValue(radii.Get(FullToHalfCorner(corner, true)),
    4206           0 :                                   pair->mYValue)) {
    4207           0 :           return false;
    4208             :         }
    4209           0 :         radiusArray->Item(corner).SetPairValue(pair.get());
    4210             :       }
    4211             :       // Set the last item in functionArray to the radius array:
    4212           0 :       functionArray->Item(functionArray->Count() - 1).
    4213           0 :                        SetArrayValue(radiusArray, eCSSUnit_Array);
    4214           0 :       break;
    4215             :     }
    4216             :     default:
    4217           0 :       MOZ_ASSERT_UNREACHABLE("Unknown shape type");
    4218             :       return false;
    4219             :   }
    4220           0 :   aResult->Item(1).SetEnumValue(aClipPath.GetReferenceBox());
    4221           0 :   return true;
    4222             : }
    4223             : 
    4224             : static void
    4225           0 : SetFallbackValue(nsCSSValuePair* aPair, const nsStyleSVGPaint& aPaint)
    4226             : {
    4227           0 :   if (aPaint.GetFallbackType() == eStyleSVGFallbackType_Color) {
    4228           0 :     aPair->mYValue.SetColorValue(aPaint.GetFallbackColor());
    4229             :   } else {
    4230           0 :     aPair->mYValue.SetNoneValue();
    4231             :   }
    4232           0 : }
    4233             : 
    4234             : bool
    4235         526 : StyleAnimationValue::ExtractComputedValue(nsCSSPropertyID aProperty,
    4236             :                                           nsStyleContext* aStyleContext,
    4237             :                                           StyleAnimationValue& aComputedValue)
    4238             : {
    4239         526 :   MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands,
    4240             :              "bad property");
    4241             :   const void* styleStruct =
    4242         526 :     aStyleContext->AsGecko()->StyleData(nsCSSProps::kSIDTable[aProperty]);
    4243         526 :   ptrdiff_t ssOffset = nsCSSProps::kStyleStructOffsetTable[aProperty];
    4244         526 :   nsStyleAnimType animType = nsCSSProps::kAnimTypeTable[aProperty];
    4245         526 :   MOZ_ASSERT(0 <= ssOffset ||
    4246             :              animType == eStyleAnimType_Custom ||
    4247             :              animType == eStyleAnimType_Discrete,
    4248             :              "all animation types other than Custom and Discrete must " \
    4249             :              "specify a style struct offset to extract values from");
    4250         526 :   switch (animType) {
    4251             :     case eStyleAnimType_Custom:
    4252           0 :       switch (aProperty) {
    4253             :         // For border-width, ignore the border-image business (which
    4254             :         // only exists until we update our implementation to the current
    4255             :         // spec) and use GetComputedBorder
    4256             : 
    4257             :         #define BORDER_WIDTH_CASE(prop_, side_)                               \
    4258             :         case prop_:                                                           \
    4259             :           aComputedValue.SetCoordValue(                                       \
    4260             :             static_cast<const nsStyleBorder*>(styleStruct)->                  \
    4261             :               GetComputedBorder().side_);                                     \
    4262             :           break;
    4263           0 :         BORDER_WIDTH_CASE(eCSSProperty_border_bottom_width, bottom)
    4264           0 :         BORDER_WIDTH_CASE(eCSSProperty_border_left_width, left)
    4265           0 :         BORDER_WIDTH_CASE(eCSSProperty_border_right_width, right)
    4266           0 :         BORDER_WIDTH_CASE(eCSSProperty_border_top_width, top)
    4267             :         #undef BORDER_WIDTH_CASE
    4268             : 
    4269             :         case eCSSProperty_column_rule_width:
    4270           0 :           aComputedValue.SetCoordValue(
    4271             :             static_cast<const nsStyleColumn*>(styleStruct)->
    4272           0 :               GetComputedColumnRuleWidth());
    4273           0 :           break;
    4274             : 
    4275             :         case eCSSProperty_column_count: {
    4276             :           const nsStyleColumn *styleColumn =
    4277           0 :             static_cast<const nsStyleColumn*>(styleStruct);
    4278           0 :           if (styleColumn->mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO) {
    4279           0 :             aComputedValue.SetAutoValue();
    4280             :           } else {
    4281           0 :             aComputedValue.SetIntValue(styleColumn->mColumnCount,
    4282           0 :                                        eUnit_Integer);
    4283             :           }
    4284           0 :           break;
    4285             :         }
    4286             : 
    4287             :         case eCSSProperty_order: {
    4288             :           const nsStylePosition *stylePosition =
    4289           0 :             static_cast<const nsStylePosition*>(styleStruct);
    4290           0 :           aComputedValue.SetIntValue(stylePosition->mOrder,
    4291           0 :                                      eUnit_Integer);
    4292           0 :           break;
    4293             :         }
    4294             : 
    4295             :         case eCSSProperty_border_spacing: {
    4296             :           const nsStyleTableBorder *styleTableBorder =
    4297           0 :             static_cast<const nsStyleTableBorder*>(styleStruct);
    4298           0 :           nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
    4299           0 :           pair->mXValue.SetIntegerCoordValue(styleTableBorder->mBorderSpacingCol);
    4300           0 :           pair->mYValue.SetIntegerCoordValue(styleTableBorder->mBorderSpacingRow);
    4301           0 :           aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
    4302           0 :                                                       eUnit_CSSValuePair);
    4303           0 :           break;
    4304             :         }
    4305             : 
    4306             :         case eCSSProperty_transform_origin: {
    4307             :           const nsStyleDisplay *styleDisplay =
    4308           0 :             static_cast<const nsStyleDisplay*>(styleStruct);
    4309           0 :           nsAutoPtr<nsCSSValueTriplet> triplet(new nsCSSValueTriplet);
    4310           0 :           if (!StyleCoordToCSSValue(styleDisplay->mTransformOrigin[0],
    4311           0 :                                     triplet->mXValue) ||
    4312           0 :               !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[1],
    4313           0 :                                     triplet->mYValue) ||
    4314           0 :               !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[2],
    4315           0 :                                     triplet->mZValue)) {
    4316           0 :             return false;
    4317             :           }
    4318           0 :           aComputedValue.SetAndAdoptCSSValueTripletValue(triplet.forget(),
    4319           0 :                                                          eUnit_CSSValueTriplet);
    4320           0 :           break;
    4321             :         }
    4322             : 
    4323             :         case eCSSProperty_perspective_origin: {
    4324             :           const nsStyleDisplay *styleDisplay =
    4325           0 :             static_cast<const nsStyleDisplay*>(styleStruct);
    4326           0 :           nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
    4327           0 :           if (!StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[0],
    4328           0 :                                     pair->mXValue) ||
    4329           0 :               !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[1],
    4330           0 :                                     pair->mYValue)) {
    4331           0 :             return false;
    4332             :           }
    4333           0 :           aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
    4334           0 :                                                       eUnit_CSSValuePair);
    4335           0 :           break;
    4336             :         }
    4337             : 
    4338             :         case eCSSProperty__moz_window_transform_origin: {
    4339             :           const nsStyleUIReset *styleUIReset =
    4340           0 :             static_cast<const nsStyleUIReset*>(styleStruct);
    4341           0 :           nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
    4342           0 :           if (!StyleCoordToCSSValue(styleUIReset->mWindowTransformOrigin[0],
    4343           0 :                                     pair->mXValue) ||
    4344           0 :               !StyleCoordToCSSValue(styleUIReset->mWindowTransformOrigin[1],
    4345           0 :                                     pair->mYValue)) {
    4346           0 :             return false;
    4347             :           }
    4348           0 :           aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
    4349           0 :                                                       eUnit_CSSValuePair);
    4350           0 :           break;
    4351             :         }
    4352             : 
    4353             :         case eCSSProperty_stroke_dasharray: {
    4354           0 :           const nsStyleSVG *svg = static_cast<const nsStyleSVG*>(styleStruct);
    4355           0 :           if (!svg->mStrokeDasharray.IsEmpty()) {
    4356           0 :             nsAutoPtr<nsCSSValueList> result;
    4357           0 :             nsCSSValueList **resultTail = getter_Transfers(result);
    4358           0 :             for (uint32_t i = 0, i_end = svg->mStrokeDasharray.Length();
    4359           0 :                  i != i_end; ++i) {
    4360           0 :               nsCSSValueList *item = new nsCSSValueList;
    4361           0 :               *resultTail = item;
    4362           0 :               resultTail = &item->mNext;
    4363             : 
    4364           0 :               const nsStyleCoord &coord = svg->mStrokeDasharray[i];
    4365           0 :               nsCSSValue &value = item->mValue;
    4366           0 :               switch (coord.GetUnit()) {
    4367             :                 case eStyleUnit_Coord:
    4368             :                   // Number means the same thing as length; we want to
    4369             :                   // animate them the same way.  Normalize both to number
    4370             :                   // since it has more accuracy (float vs nscoord).
    4371           0 :                   value.SetFloatValue(nsPresContext::
    4372             :                     AppUnitsToFloatCSSPixels(coord.GetCoordValue()),
    4373           0 :                     eCSSUnit_Number);
    4374           0 :                   break;
    4375             :                 case eStyleUnit_Factor:
    4376           0 :                   value.SetFloatValue(coord.GetFactorValue(),
    4377           0 :                                       eCSSUnit_Number);
    4378           0 :                   break;
    4379             :                 case eStyleUnit_Percent:
    4380           0 :                   value.SetPercentValue(coord.GetPercentValue());
    4381           0 :                   break;
    4382             :                 default:
    4383           0 :                   MOZ_ASSERT(false, "unexpected unit");
    4384             :                   return false;
    4385             :               }
    4386             :             }
    4387           0 :             aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
    4388           0 :                                                         eUnit_Dasharray);
    4389           0 :           } else if (svg->StrokeDasharrayFromObject()) {
    4390             :             // An empty dasharray with StrokeDasharrayFromObject() == true
    4391             :             // corresponds to the "context-value" keyword.
    4392             :             aComputedValue.SetIntValue(NS_STYLE_STROKE_PROP_CONTEXT_VALUE,
    4393           0 :                                        eUnit_Enumerated);
    4394             :           } else {
    4395             :             // Otherwise, an empty dasharray corresponds to the "none" keyword.
    4396           0 :             aComputedValue.SetNoneValue();
    4397             :           }
    4398           0 :           break;
    4399             :         }
    4400             : 
    4401             :         case eCSSProperty_font_stretch: {
    4402             :           int16_t stretch =
    4403           0 :             static_cast<const nsStyleFont*>(styleStruct)->mFont.stretch;
    4404             :           static_assert(NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED == -4 &&
    4405             :                         NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED == 4,
    4406             :                         "font stretch constants not as expected");
    4407           0 :           if (stretch < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED ||
    4408             :               stretch > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
    4409           0 :             return false;
    4410             :           }
    4411           0 :           aComputedValue.SetIntValue(stretch, eUnit_Enumerated);
    4412           0 :           return true;
    4413             :         }
    4414             : 
    4415             :         case eCSSProperty_font_weight: {
    4416             :           uint16_t weight =
    4417           0 :             static_cast<const nsStyleFont*>(styleStruct)->mFont.weight;
    4418           0 :           if (weight % 100 != 0) {
    4419           0 :             return false;
    4420             :           }
    4421           0 :           aComputedValue.SetIntValue(weight, eUnit_Integer);
    4422           0 :           return true;
    4423             :         }
    4424             : 
    4425             :         case eCSSProperty__moz_image_region: {
    4426             :           const nsStyleList *list =
    4427           0 :             static_cast<const nsStyleList*>(styleStruct);
    4428           0 :           const nsRect &srect = list->mImageRegion;
    4429           0 :           if (srect.IsEmpty()) {
    4430           0 :             aComputedValue.SetAutoValue();
    4431           0 :             break;
    4432             :           }
    4433             : 
    4434           0 :           nsCSSRect *vrect = new nsCSSRect;
    4435           0 :           vrect->mLeft.SetIntegerCoordValue(srect.x);
    4436           0 :           vrect->mTop.SetIntegerCoordValue(srect.y);
    4437           0 :           vrect->mRight.SetIntegerCoordValue(srect.XMost());
    4438           0 :           vrect->mBottom.SetIntegerCoordValue(srect.YMost());
    4439           0 :           aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
    4440           0 :           break;
    4441             :         }
    4442             : 
    4443             :         case eCSSProperty_clip: {
    4444             :           const nsStyleEffects* effects =
    4445           0 :             static_cast<const nsStyleEffects*>(styleStruct);
    4446           0 :           if (!(effects->mClipFlags & NS_STYLE_CLIP_RECT)) {
    4447           0 :             aComputedValue.SetAutoValue();
    4448             :           } else {
    4449           0 :             nsCSSRect *vrect = new nsCSSRect;
    4450           0 :             const nsRect &srect = effects->mClip;
    4451           0 :             if (effects->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) {
    4452           0 :               vrect->mTop.SetAutoValue();
    4453             :             } else {
    4454           0 :               vrect->mTop.SetIntegerCoordValue(srect.y);
    4455             :             }
    4456           0 :             if (effects->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) {
    4457           0 :               vrect->mRight.SetAutoValue();
    4458             :             } else {
    4459           0 :               vrect->mRight.SetIntegerCoordValue(srect.XMost());
    4460             :             }
    4461           0 :             if (effects->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) {
    4462           0 :               vrect->mBottom.SetAutoValue();
    4463             :             } else {
    4464           0 :               vrect->mBottom.SetIntegerCoordValue(srect.YMost());
    4465             :             }
    4466           0 :             if (effects->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) {
    4467           0 :               vrect->mLeft.SetAutoValue();
    4468             :             } else {
    4469           0 :               vrect->mLeft.SetIntegerCoordValue(srect.x);
    4470             :             }
    4471           0 :             aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
    4472             :           }
    4473           0 :           break;
    4474             :         }
    4475             : 
    4476             :         case eCSSProperty_object_position: {
    4477             :           const nsStylePosition* stylePos =
    4478           0 :             static_cast<const nsStylePosition*>(styleStruct);
    4479             : 
    4480           0 :           nsAutoPtr<nsCSSValue> val(new nsCSSValue);
    4481           0 :           SetPositionValue(stylePos->mObjectPosition, *val);
    4482             : 
    4483           0 :           aComputedValue.SetAndAdoptCSSValueValue(val.forget(),
    4484           0 :                                                   eUnit_ObjectPosition);
    4485           0 :           break;
    4486             :         }
    4487             : 
    4488             :         case eCSSProperty_background_position_x: {
    4489             :           const nsStyleImageLayers& layers =
    4490           0 :             static_cast<const nsStyleBackground*>(styleStruct)->mImage;
    4491           0 :           ExtractImageLayerPositionXList(layers, aComputedValue);
    4492           0 :           break;
    4493             :         }
    4494             :         case eCSSProperty_background_position_y: {
    4495             :           const nsStyleImageLayers& layers =
    4496           0 :             static_cast<const nsStyleBackground*>(styleStruct)->mImage;
    4497           0 :           ExtractImageLayerPositionYList(layers, aComputedValue);
    4498           0 :           break;
    4499             :         }
    4500             : #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
    4501             :         case eCSSProperty_mask_position_x: {
    4502             :           const nsStyleImageLayers& layers =
    4503           0 :             static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
    4504           0 :           ExtractImageLayerPositionXList(layers, aComputedValue);
    4505           0 :           break;
    4506             :         }
    4507             :         case eCSSProperty_mask_position_y: {
    4508             :           const nsStyleImageLayers& layers =
    4509           0 :             static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
    4510           0 :           ExtractImageLayerPositionYList(layers, aComputedValue);
    4511             : 
    4512           0 :           break;
    4513             :         }
    4514             : #endif
    4515             :         case eCSSProperty_background_size: {
    4516             :           const nsStyleImageLayers& layers =
    4517           0 :             static_cast<const nsStyleBackground*>(styleStruct)->mImage;
    4518           0 :           ExtractImageLayerSizePairList(layers, aComputedValue);
    4519           0 :           break;
    4520             :         }
    4521             : #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
    4522             :         case eCSSProperty_mask_size: {
    4523             :           const nsStyleImageLayers& layers =
    4524           0 :             static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
    4525           0 :           ExtractImageLayerSizePairList(layers, aComputedValue);
    4526           0 :           break;
    4527             :         }
    4528             : #endif
    4529             : 
    4530             :         case eCSSProperty_clip_path: {
    4531             :           const nsStyleSVGReset* svgReset =
    4532           0 :             static_cast<const nsStyleSVGReset*>(styleStruct);
    4533           0 :           const StyleShapeSource& clipPath = svgReset->mClipPath;
    4534           0 :           const StyleShapeSourceType type = clipPath.GetType();
    4535             : 
    4536           0 :           if (type == StyleShapeSourceType::URL) {
    4537           0 :             auto result = MakeUnique<nsCSSValue>();
    4538           0 :             result->SetURLValue(clipPath.GetURL());
    4539           0 :             aComputedValue.SetAndAdoptCSSValueValue(result.release(), eUnit_URL);
    4540           0 :           } else if (type == StyleShapeSourceType::Box) {
    4541           0 :             aComputedValue.SetEnumValue(clipPath.GetReferenceBox());
    4542           0 :           } else if (type == StyleShapeSourceType::Shape) {
    4543           0 :             RefPtr<nsCSSValue::Array> result = nsCSSValue::Array::Create(2);
    4544           0 :             if (!StyleClipBasicShapeToCSSArray(clipPath, result)) {
    4545           0 :               return false;
    4546             :             }
    4547           0 :             aComputedValue.SetCSSValueArrayValue(result, eUnit_Shape);
    4548             : 
    4549             :           } else {
    4550           0 :             MOZ_ASSERT(type == StyleShapeSourceType::None, "unknown type");
    4551           0 :             aComputedValue.SetNoneValue();
    4552             :           }
    4553           0 :           break;
    4554             :         }
    4555             : 
    4556             :         case eCSSProperty_filter: {
    4557             :           const nsStyleEffects* effects =
    4558           0 :             static_cast<const nsStyleEffects*>(styleStruct);
    4559           0 :           const nsTArray<nsStyleFilter>& filters = effects->mFilters;
    4560           0 :           nsAutoPtr<nsCSSValueList> result;
    4561           0 :           nsCSSValueList **resultTail = getter_Transfers(result);
    4562           0 :           for (uint32_t i = 0; i < filters.Length(); ++i) {
    4563           0 :             nsCSSValueList *item = new nsCSSValueList;
    4564           0 :             *resultTail = item;
    4565           0 :             resultTail = &item->mNext;
    4566           0 :             const nsStyleFilter& filter = filters[i];
    4567           0 :             int32_t type = filter.GetType();
    4568           0 :             if (type == NS_STYLE_FILTER_URL) {
    4569           0 :               item->mValue.SetURLValue(filter.GetURL());
    4570             :             } else {
    4571             :               nsCSSKeyword functionName =
    4572             :                 nsCSSProps::ValueToKeywordEnum(type,
    4573           0 :                   nsCSSProps::kFilterFunctionKTable);
    4574             :               nsCSSValue::Array* filterArray =
    4575           0 :                 item->mValue.InitFunction(functionName, 1);
    4576           0 :               if (type >= NS_STYLE_FILTER_BLUR && type <= NS_STYLE_FILTER_HUE_ROTATE) {
    4577           0 :                 if (!StyleCoordToCSSValue(
    4578             :                       filter.GetFilterParameter(),
    4579             :                       filterArray->Item(1))) {
    4580           0 :                   return false;
    4581             :                 }
    4582           0 :               } else if (type == NS_STYLE_FILTER_DROP_SHADOW) {
    4583           0 :                 nsCSSValueList* shadowResult = filterArray->Item(1).SetListValue();
    4584           0 :                 nsAutoPtr<nsCSSValueList> tmpShadowValue;
    4585           0 :                 nsCSSValueList **tmpShadowResultTail = getter_Transfers(tmpShadowValue);
    4586           0 :                 nsCSSShadowArray* shadowArray = filter.GetDropShadow();
    4587           0 :                 MOZ_ASSERT(shadowArray->Length() == 1,
    4588             :                            "expected exactly one shadow");
    4589           0 :                 AppendCSSShadowValue(shadowArray->ShadowAt(0),
    4590           0 :                                      tmpShadowResultTail, aProperty);
    4591           0 :                 *shadowResult = *tmpShadowValue;
    4592             :               } else {
    4593             :                 // We checked all possible nsStyleFilter types but
    4594             :                 // NS_STYLE_FILTER_NULL before. We should never enter this path.
    4595           0 :                 NS_NOTREACHED("no other filter functions defined");
    4596           0 :                 return false;
    4597             :               }
    4598             :             }
    4599             :           }
    4600             : 
    4601           0 :           aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
    4602           0 :                                                       eUnit_Filter);
    4603           0 :           break;
    4604             :         }
    4605             : 
    4606             :         case eCSSProperty_transform: {
    4607             :           const nsStyleDisplay *display =
    4608           0 :             static_cast<const nsStyleDisplay*>(styleStruct);
    4609           0 :           nsAutoPtr<nsCSSValueList> result;
    4610           0 :           if (display->mSpecifiedTransform) {
    4611             :             // Clone, and convert all lengths (not percents) to pixels.
    4612           0 :             nsCSSValueList **resultTail = getter_Transfers(result);
    4613           0 :             for (const nsCSSValueList *l = display->mSpecifiedTransform->mHead;
    4614           0 :                  l; l = l->mNext) {
    4615           0 :               nsCSSValueList *clone = new nsCSSValueList;
    4616           0 :               *resultTail = clone;
    4617           0 :               resultTail = &clone->mNext;
    4618             : 
    4619           0 :               SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue);
    4620             :             }
    4621             :           } else {
    4622           0 :             result = new nsCSSValueList();
    4623           0 :             result->mValue.SetNoneValue();
    4624             :           }
    4625             : 
    4626             :           aComputedValue.SetTransformValue(
    4627           0 :               new nsCSSValueSharedList(result.forget()));
    4628           0 :           break;
    4629             :         }
    4630             : 
    4631             :         case eCSSProperty__moz_window_transform: {
    4632             :           const nsStyleUIReset *uiReset =
    4633           0 :             static_cast<const nsStyleUIReset*>(styleStruct);
    4634           0 :           nsAutoPtr<nsCSSValueList> result;
    4635           0 :           if (uiReset->mSpecifiedWindowTransform) {
    4636             :             // Clone, and convert all lengths (not percents) to pixels.
    4637           0 :             nsCSSValueList **resultTail = getter_Transfers(result);
    4638           0 :             for (const nsCSSValueList *l = uiReset->mSpecifiedWindowTransform->mHead;
    4639           0 :                  l; l = l->mNext) {
    4640           0 :               nsCSSValueList *clone = new nsCSSValueList;
    4641           0 :               *resultTail = clone;
    4642           0 :               resultTail = &clone->mNext;
    4643             : 
    4644           0 :               SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue);
    4645             :             }
    4646             :           } else {
    4647           0 :             result = new nsCSSValueList();
    4648           0 :             result->mValue.SetNoneValue();
    4649             :           }
    4650             : 
    4651             :           aComputedValue.SetTransformValue(
    4652           0 :               new nsCSSValueSharedList(result.forget()));
    4653           0 :           break;
    4654             :         }
    4655             : 
    4656             :         case eCSSProperty_font_variation_settings: {
    4657           0 :           auto font = static_cast<const nsStyleFont*>(styleStruct);
    4658           0 :           UniquePtr<nsCSSValuePairList> result;
    4659           0 :           if (!font->mFont.fontVariationSettings.IsEmpty()) {
    4660             :             // Make a new list that clones the current settings
    4661           0 :             nsCSSValuePairList* tail = nullptr;
    4662           0 :             for (auto v : font->mFont.fontVariationSettings) {
    4663           0 :               auto clone = MakeUnique<nsCSSValuePairList>();
    4664             :               // OpenType font tags are stored in nsFont as 32-bit unsigned
    4665             :               // values, but represented in CSS as 4-character ASCII strings,
    4666             :               // beginning with the high byte of the value. So to clone the
    4667             :               // tag here, we append each of its 4 bytes to a string.
    4668           0 :               nsAutoString tagString;
    4669           0 :               tagString.Append(char(v.mTag >> 24));
    4670           0 :               tagString.Append(char(v.mTag >> 16));
    4671           0 :               tagString.Append(char(v.mTag >> 8));
    4672           0 :               tagString.Append(char(v.mTag));
    4673           0 :               clone->mXValue.SetStringValue(tagString, eCSSUnit_String);
    4674           0 :               clone->mYValue.SetFloatValue(v.mValue, eCSSUnit_Number);
    4675           0 :               AppendToCSSValuePairList(result, Move(clone), &tail);
    4676             :             }
    4677           0 :             aComputedValue.SetAndAdoptCSSValuePairListValue(result.release());
    4678             :           } else {
    4679           0 :             aComputedValue.SetNormalValue();
    4680             :           }
    4681           0 :           break;
    4682             :         }
    4683             : 
    4684             :         default:
    4685           0 :           MOZ_ASSERT(false, "missing property implementation");
    4686             :           return false;
    4687             :       };
    4688           0 :       return true;
    4689             :     case eStyleAnimType_Coord: {
    4690             :       const nsStyleCoord& coord =
    4691          40 :         StyleDataAtOffset<nsStyleCoord>(styleStruct, ssOffset);
    4692          40 :       if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_NUMBERS_ARE_PIXELS) &&
    4693           0 :           coord.GetUnit() == eStyleUnit_Coord) {
    4694             :         // For SVG properties where number means the same thing as length,
    4695             :         // we want to animate them the same way.  Normalize both to number
    4696             :         // since it has more accuracy (float vs nscoord).
    4697           0 :         aComputedValue.SetFloatValue(nsPresContext::
    4698           0 :           AppUnitsToFloatCSSPixels(coord.GetCoordValue()));
    4699           0 :         return true;
    4700             :       }
    4701          40 :       return StyleCoordToValue(coord, aComputedValue);
    4702             :     }
    4703             :     case eStyleAnimType_Sides_Top:
    4704             :     case eStyleAnimType_Sides_Right:
    4705             :     case eStyleAnimType_Sides_Bottom:
    4706             :     case eStyleAnimType_Sides_Left: {
    4707             :       static_assert(
    4708             :        eSideTop    == eStyleAnimType_Sides_Top   -eStyleAnimType_Sides_Top &&
    4709             :        eSideRight  == eStyleAnimType_Sides_Right -eStyleAnimType_Sides_Top &&
    4710             :        eSideBottom == eStyleAnimType_Sides_Bottom-eStyleAnimType_Sides_Top &&
    4711             :        eSideLeft   == eStyleAnimType_Sides_Left  -eStyleAnimType_Sides_Top,
    4712             :        "box side constants out of sync with animation side constants");
    4713             : 
    4714             :       const nsStyleCoord &coord =
    4715           0 :         StyleDataAtOffset<nsStyleSides>(styleStruct, ssOffset).
    4716           0 :           Get(mozilla::Side(animType - eStyleAnimType_Sides_Top));
    4717           0 :       return StyleCoordToValue(coord, aComputedValue);
    4718             :     }
    4719             :     case eStyleAnimType_Corner_TopLeft:
    4720             :     case eStyleAnimType_Corner_TopRight:
    4721             :     case eStyleAnimType_Corner_BottomRight:
    4722             :     case eStyleAnimType_Corner_BottomLeft: {
    4723             :       static_assert(
    4724             :        eCornerTopLeft     == eStyleAnimType_Corner_TopLeft -
    4725             :                              eStyleAnimType_Corner_TopLeft        &&
    4726             :        eCornerTopRight    == eStyleAnimType_Corner_TopRight -
    4727             :                              eStyleAnimType_Corner_TopLeft        &&
    4728             :        eCornerBottomRight == eStyleAnimType_Corner_BottomRight -
    4729             :                              eStyleAnimType_Corner_TopLeft        &&
    4730             :        eCornerBottomLeft  == eStyleAnimType_Corner_BottomLeft -
    4731             :                              eStyleAnimType_Corner_TopLeft,
    4732             :        "box corner constants out of sync with animation corner constants");
    4733             : 
    4734             :       const nsStyleCorners& corners =
    4735           0 :         StyleDataAtOffset<nsStyleCorners>(styleStruct, ssOffset);
    4736           0 :       Corner fullCorner = Corner(animType - eStyleAnimType_Corner_TopLeft);
    4737             :       const nsStyleCoord &horiz =
    4738           0 :         corners.Get(FullToHalfCorner(fullCorner, false));
    4739             :       const nsStyleCoord &vert =
    4740           0 :         corners.Get(FullToHalfCorner(fullCorner, true));
    4741           0 :       nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
    4742           0 :       if (!StyleCoordToCSSValue(horiz, pair->mXValue) ||
    4743           0 :           !StyleCoordToCSSValue(vert, pair->mYValue)) {
    4744           0 :         return false;
    4745             :       }
    4746           0 :       aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
    4747           0 :                                                   eUnit_CSSValuePair);
    4748           0 :       return true;
    4749             :     }
    4750             :     case eStyleAnimType_nscoord:
    4751           0 :       aComputedValue.SetCoordValue(
    4752           0 :         StyleDataAtOffset<nscoord>(styleStruct, ssOffset));
    4753           0 :       return true;
    4754             :     case eStyleAnimType_float:
    4755          50 :       aComputedValue.SetFloatValue(
    4756         100 :         StyleDataAtOffset<float>(styleStruct, ssOffset));
    4757          50 :       if (aProperty == eCSSProperty_font_size_adjust &&
    4758           0 :           aComputedValue.GetFloatValue() == -1.0f) {
    4759             :         // In nsStyleFont, we set mFont.sizeAdjust to -1.0 to represent
    4760             :         // font-size-adjust: none.  Here, we have to treat this as a keyword
    4761             :         // instead of a float value, to make sure we don't end up doing
    4762             :         // interpolation with it.
    4763           0 :         aComputedValue.SetNoneValue();
    4764             :       }
    4765          50 :       return true;
    4766             :     case eStyleAnimType_Color:
    4767           0 :       aComputedValue.SetColorValue(
    4768           0 :         StyleDataAtOffset<nscolor>(styleStruct, ssOffset));
    4769           0 :       return true;
    4770             :     case eStyleAnimType_ComplexColor: {
    4771         350 :       auto& color = StyleDataAtOffset<StyleComplexColor>(styleStruct, ssOffset);
    4772         350 :       if (color.mIsAuto) {
    4773           0 :         aComputedValue.SetAutoValue();
    4774             :       } else {
    4775         350 :         aComputedValue.SetComplexColorValue(color);
    4776             :       }
    4777         350 :       return true;
    4778             :     }
    4779             :     case eStyleAnimType_PaintServer: {
    4780             :       const nsStyleSVGPaint& paint =
    4781           0 :         StyleDataAtOffset<nsStyleSVGPaint>(styleStruct, ssOffset);
    4782           0 :       switch (paint.Type()) {
    4783             :         case eStyleSVGPaintType_Color:
    4784           0 :           aComputedValue.SetColorValue(paint.GetColor());
    4785           0 :           return true;
    4786             :         case eStyleSVGPaintType_Server: {
    4787           0 :           css::URLValue* url = paint.GetPaintServer();
    4788           0 :           if (!url) {
    4789           0 :             NS_WARNING("Null paint server");
    4790           0 :             return false;
    4791             :           }
    4792           0 :           if (paint.GetFallbackType() != eStyleSVGFallbackType_NotSet) {
    4793           0 :             nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
    4794           0 :             pair->mXValue.SetURLValue(url);
    4795           0 :             SetFallbackValue(pair, paint);
    4796           0 :             aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
    4797           0 :                                                         eUnit_CSSValuePair);
    4798             :           } else {
    4799           0 :             auto result = MakeUnique<nsCSSValue>();
    4800           0 :             result->SetURLValue(url);
    4801           0 :             aComputedValue.SetAndAdoptCSSValueValue(
    4802           0 :               result.release(), eUnit_URL);
    4803             :           }
    4804           0 :           return true;
    4805             :         }
    4806             :         case eStyleSVGPaintType_ContextFill:
    4807             :         case eStyleSVGPaintType_ContextStroke: {
    4808           0 :           int32_t value = paint.Type() == eStyleSVGPaintType_ContextFill ?
    4809           0 :                             NS_COLOR_CONTEXT_FILL : NS_COLOR_CONTEXT_STROKE;
    4810           0 :           if (paint.GetFallbackType() != eStyleSVGFallbackType_NotSet) {
    4811           0 :             nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
    4812           0 :             pair->mXValue.SetIntValue(value, eCSSUnit_Enumerated);
    4813           0 :             SetFallbackValue(pair, paint);
    4814           0 :             aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
    4815           0 :                                                         eUnit_CSSValuePair);
    4816             :           } else {
    4817           0 :             aComputedValue.SetIntValue(value, eUnit_Enumerated);
    4818             :           }
    4819           0 :           return true;
    4820             :         }
    4821             :         default:
    4822           0 :           MOZ_ASSERT(paint.Type() == eStyleSVGPaintType_None,
    4823             :                      "Unexpected SVG paint type");
    4824           0 :           aComputedValue.SetNoneValue();
    4825           0 :           return true;
    4826             :       }
    4827             :     }
    4828             :     case eStyleAnimType_Shadow: {
    4829             :       const nsCSSShadowArray* shadowArray =
    4830          68 :         StyleDataAtOffset<RefPtr<nsCSSShadowArray>>(styleStruct, ssOffset);
    4831          68 :       if (!shadowArray) {
    4832          68 :         aComputedValue.SetAndAdoptCSSValueListValue(nullptr, eUnit_Shadow);
    4833          68 :         return true;
    4834             :       }
    4835           0 :       nsAutoPtr<nsCSSValueList> result;
    4836           0 :       nsCSSValueList **resultTail = getter_Transfers(result);
    4837           0 :       for (uint32_t i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
    4838           0 :         AppendCSSShadowValue(shadowArray->ShadowAt(i), resultTail, aProperty);
    4839             :       }
    4840           0 :       aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
    4841           0 :                                                   eUnit_Shadow);
    4842           0 :       return true;
    4843             :     }
    4844             :     case eStyleAnimType_Discrete: {
    4845          18 :       if (aProperty == eCSSProperty_visibility) {
    4846          18 :         aComputedValue.SetIntValue(
    4847          18 :           static_cast<const nsStyleVisibility*>(styleStruct)->mVisible,
    4848          18 :           eUnit_Visibility);
    4849          18 :         return true;
    4850             :       }
    4851           0 :       if (aStyleContext->IsServo()) {
    4852           0 :         NS_ERROR("stylo: extracting discretely animated values not supported");
    4853           0 :         return false;
    4854             :       }
    4855           0 :       auto cssValue = MakeUnique<nsCSSValue>(eCSSUnit_Unset);
    4856           0 :       aStyleContext->RuleNode()->GetDiscretelyAnimatedCSSValue(aProperty,
    4857           0 :                                                                cssValue.get());
    4858           0 :       aComputedValue.SetAndAdoptCSSValueValue(cssValue.release(),
    4859           0 :                                               eUnit_DiscreteCSSValue);
    4860           0 :       return true;
    4861             :     }
    4862             :     case eStyleAnimType_None:
    4863           0 :       NS_NOTREACHED("shouldn't use on non-animatable properties");
    4864             :   }
    4865           0 :   return false;
    4866             : }
    4867             : 
    4868             : gfxSize
    4869           0 : StyleAnimationValue::GetScaleValue(const nsIFrame* aForFrame) const
    4870             : {
    4871           0 :   MOZ_ASSERT(GetUnit() == StyleAnimationValue::eUnit_Transform);
    4872             : 
    4873           0 :   nsCSSValueSharedList* list = GetCSSValueSharedListValue();
    4874           0 :   return nsStyleTransformMatrix::GetScaleValue(list, aForFrame);
    4875             : }
    4876             : 
    4877           0 : StyleAnimationValue::StyleAnimationValue(int32_t aInt, Unit aUnit,
    4878           0 :                                          IntegerConstructorType)
    4879             : {
    4880           0 :   NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
    4881           0 :   mUnit = aUnit;
    4882           0 :   mValue.mInt = aInt;
    4883           0 : }
    4884             : 
    4885           0 : StyleAnimationValue::StyleAnimationValue(nscoord aLength, CoordConstructorType)
    4886             : {
    4887           0 :   mUnit = eUnit_Coord;
    4888           0 :   mValue.mCoord = aLength;
    4889           0 : }
    4890             : 
    4891           0 : StyleAnimationValue::StyleAnimationValue(float aPercent,
    4892           0 :                                          PercentConstructorType)
    4893             : {
    4894           0 :   mUnit = eUnit_Percent;
    4895           0 :   mValue.mFloat = aPercent;
    4896           0 :   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
    4897           0 : }
    4898             : 
    4899           0 : StyleAnimationValue::StyleAnimationValue(float aFloat, FloatConstructorType)
    4900             : {
    4901           0 :   mUnit = eUnit_Float;
    4902           0 :   mValue.mFloat = aFloat;
    4903           0 :   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
    4904           0 : }
    4905             : 
    4906           0 : StyleAnimationValue::StyleAnimationValue(nscolor aColor, ColorConstructorType)
    4907             : {
    4908           0 :   mUnit = eUnit_Color;
    4909           0 :   mValue.mCSSValue = new nsCSSValue();
    4910           0 :   mValue.mCSSValue->SetColorValue(aColor);
    4911           0 : }
    4912             : 
    4913             : StyleAnimationValue&
    4914         289 : StyleAnimationValue::operator=(const StyleAnimationValue& aOther)
    4915             : {
    4916         289 :   if (this == &aOther) {
    4917           0 :     return *this;
    4918             :   }
    4919             : 
    4920         289 :   FreeValue();
    4921             : 
    4922         289 :   mUnit = aOther.mUnit;
    4923         289 :   switch (mUnit) {
    4924             :     case eUnit_Null:
    4925             :     case eUnit_Normal:
    4926             :     case eUnit_Auto:
    4927             :     case eUnit_None:
    4928             :     case eUnit_CurrentColor:
    4929         215 :       break;
    4930             :     case eUnit_Enumerated:
    4931             :     case eUnit_Visibility:
    4932             :     case eUnit_Integer:
    4933           0 :       mValue.mInt = aOther.mValue.mInt;
    4934           0 :       break;
    4935             :     case eUnit_Coord:
    4936           0 :       mValue.mCoord = aOther.mValue.mCoord;
    4937           0 :       break;
    4938             :     case eUnit_Percent:
    4939             :     case eUnit_Float:
    4940          74 :       mValue.mFloat = aOther.mValue.mFloat;
    4941          74 :       MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
    4942          74 :       break;
    4943             :     case eUnit_Calc:
    4944             :     case eUnit_Color:
    4945             :     case eUnit_ObjectPosition:
    4946             :     case eUnit_URL:
    4947             :     case eUnit_DiscreteCSSValue:
    4948           0 :       MOZ_ASSERT(IsCSSValueUnit(mUnit),
    4949             :                  "This clause is for handling nsCSSValue-backed units");
    4950           0 :       MOZ_ASSERT(aOther.mValue.mCSSValue, "values may not be null");
    4951           0 :       mValue.mCSSValue = new nsCSSValue(*aOther.mValue.mCSSValue);
    4952           0 :       break;
    4953             :     case eUnit_CSSValuePair:
    4954           0 :       MOZ_ASSERT(aOther.mValue.mCSSValuePair,
    4955             :                  "value pairs may not be null");
    4956           0 :       mValue.mCSSValuePair = new nsCSSValuePair(*aOther.mValue.mCSSValuePair);
    4957           0 :       break;
    4958             :     case eUnit_CSSValueTriplet:
    4959           0 :       MOZ_ASSERT(aOther.mValue.mCSSValueTriplet,
    4960             :                  "value triplets may not be null");
    4961           0 :       mValue.mCSSValueTriplet = new nsCSSValueTriplet(*aOther.mValue.mCSSValueTriplet);
    4962           0 :       break;
    4963             :     case eUnit_CSSRect:
    4964           0 :       MOZ_ASSERT(aOther.mValue.mCSSRect, "rects may not be null");
    4965           0 :       mValue.mCSSRect = new nsCSSRect(*aOther.mValue.mCSSRect);
    4966           0 :       break;
    4967             :     case eUnit_Dasharray:
    4968             :     case eUnit_Shadow:
    4969             :     case eUnit_Filter:
    4970             :     case eUnit_BackgroundPositionCoord:
    4971           0 :       MOZ_ASSERT(mUnit == eUnit_Shadow || mUnit == eUnit_Filter ||
    4972             :                  aOther.mValue.mCSSValueList,
    4973             :                  "value lists other than shadows and filters may not be null");
    4974           0 :       if (aOther.mValue.mCSSValueList) {
    4975           0 :         mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
    4976             :       } else {
    4977           0 :         mValue.mCSSValueList = nullptr;
    4978             :       }
    4979           0 :       break;
    4980             :     case eUnit_Shape:
    4981           0 :       MOZ_ASSERT(aOther.mValue.mCSSValueArray,
    4982             :                  "value arrays may not be null");
    4983           0 :       mValue.mCSSValueArray = aOther.mValue.mCSSValueArray;
    4984           0 :       mValue.mCSSValueArray->AddRef();
    4985           0 :       break;
    4986             :     case eUnit_Transform:
    4987           0 :       mValue.mCSSValueSharedList = aOther.mValue.mCSSValueSharedList;
    4988           0 :       mValue.mCSSValueSharedList->AddRef();
    4989           0 :       break;
    4990             :     case eUnit_CSSValuePairList:
    4991           0 :       MOZ_ASSERT(aOther.mValue.mCSSValuePairList,
    4992             :                  "value pair lists may not be null");
    4993           0 :       mValue.mCSSValuePairList = aOther.mValue.mCSSValuePairList->Clone();
    4994           0 :       break;
    4995             :     case eUnit_UnparsedString:
    4996           0 :       MOZ_ASSERT(aOther.mValue.mString, "expecting non-null string");
    4997           0 :       mValue.mString = aOther.mValue.mString;
    4998           0 :       mValue.mString->AddRef();
    4999           0 :       break;
    5000             :     case eUnit_ComplexColor:
    5001           0 :       MOZ_ASSERT(aOther.mValue.mComplexColor);
    5002           0 :       mValue.mComplexColor = aOther.mValue.mComplexColor;
    5003           0 :       mValue.mComplexColor->AddRef();
    5004           0 :       break;
    5005             :   }
    5006             : 
    5007         289 :   return *this;
    5008             : }
    5009             : 
    5010             : void
    5011           0 : StyleAnimationValue::SetNormalValue()
    5012             : {
    5013           0 :   FreeValue();
    5014           0 :   mUnit = eUnit_Normal;
    5015           0 : }
    5016             : 
    5017             : void
    5018           0 : StyleAnimationValue::SetAutoValue()
    5019             : {
    5020           0 :   FreeValue();
    5021           0 :   mUnit = eUnit_Auto;
    5022           0 : }
    5023             : 
    5024             : void
    5025           8 : StyleAnimationValue::SetNoneValue()
    5026             : {
    5027           8 :   FreeValue();
    5028           8 :   mUnit = eUnit_None;
    5029           8 : }
    5030             : 
    5031             : void
    5032          18 : StyleAnimationValue::SetIntValue(int32_t aInt, Unit aUnit)
    5033             : {
    5034          18 :   NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
    5035          18 :   FreeValue();
    5036          18 :   mUnit = aUnit;
    5037          18 :   mValue.mInt = aInt;
    5038          18 : }
    5039             : 
    5040             : void
    5041          32 : StyleAnimationValue::SetCoordValue(nscoord aLength)
    5042             : {
    5043          32 :   FreeValue();
    5044          32 :   mUnit = eUnit_Coord;
    5045          32 :   mValue.mCoord = aLength;
    5046          32 : }
    5047             : 
    5048             : void
    5049           0 : StyleAnimationValue::SetPercentValue(float aPercent)
    5050             : {
    5051           0 :   FreeValue();
    5052           0 :   mUnit = eUnit_Percent;
    5053           0 :   mValue.mFloat = aPercent;
    5054           0 :   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
    5055           0 : }
    5056             : 
    5057             : void
    5058          60 : StyleAnimationValue::SetFloatValue(float aFloat)
    5059             : {
    5060          60 :   FreeValue();
    5061          60 :   mUnit = eUnit_Float;
    5062          60 :   mValue.mFloat = aFloat;
    5063          60 :   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
    5064          60 : }
    5065             : 
    5066             : void
    5067          94 : StyleAnimationValue::SetColorValue(nscolor aColor)
    5068             : {
    5069          94 :   FreeValue();
    5070          94 :   mUnit = eUnit_Color;
    5071          94 :   mValue.mCSSValue = new nsCSSValue();
    5072          94 :   mValue.mCSSValue->SetColorValue(aColor);
    5073          94 : }
    5074             : 
    5075             : void
    5076         256 : StyleAnimationValue::SetCurrentColorValue()
    5077             : {
    5078         256 :   FreeValue();
    5079         256 :   mUnit = eUnit_CurrentColor;
    5080         256 : }
    5081             : 
    5082             : void
    5083         350 : StyleAnimationValue::SetComplexColorValue(const StyleComplexColor& aColor)
    5084             : {
    5085         350 :   if (aColor.mIsAuto) {
    5086           0 :     SetAutoValue();
    5087         350 :   } else if (aColor.IsCurrentColor()) {
    5088         256 :     SetCurrentColorValue();
    5089          94 :   } else if (aColor.IsNumericColor()) {
    5090          94 :     SetColorValue(aColor.mColor);
    5091             :   } else {
    5092           0 :     SetComplexColorValue(do_AddRef(new ComplexColorValue(aColor)));
    5093             :   }
    5094         350 : }
    5095             : 
    5096             : void
    5097           0 : StyleAnimationValue::SetComplexColorValue(
    5098             :   already_AddRefed<ComplexColorValue> aValue)
    5099             : {
    5100           0 :   FreeValue();
    5101           0 :   mUnit = eUnit_ComplexColor;
    5102           0 :   mValue.mComplexColor = aValue.take();
    5103           0 : }
    5104             : 
    5105             : void
    5106           0 : StyleAnimationValue::SetUnparsedStringValue(const nsString& aString)
    5107             : {
    5108           0 :   FreeValue();
    5109           0 :   mUnit = eUnit_UnparsedString;
    5110           0 :   mValue.mString = nsCSSValue::BufferFromString(aString).take();
    5111           0 : }
    5112             : 
    5113             : void
    5114           0 : StyleAnimationValue::SetAndAdoptCSSValueValue(nsCSSValue *aValue,
    5115             :                                               Unit aUnit)
    5116             : {
    5117           0 :   FreeValue();
    5118           0 :   MOZ_ASSERT(IsCSSValueUnit(aUnit), "bad unit");
    5119           0 :   MOZ_ASSERT(aValue != nullptr, "values may not be null");
    5120           0 :   mUnit = aUnit;
    5121           0 :   mValue.mCSSValue = aValue; // take ownership
    5122           0 : }
    5123             : 
    5124             : void
    5125           0 : StyleAnimationValue::SetAndAdoptCSSValuePairValue(nsCSSValuePair *aValuePair,
    5126             :                                                   Unit aUnit)
    5127             : {
    5128           0 :   FreeValue();
    5129           0 :   MOZ_ASSERT(IsCSSValuePairUnit(aUnit), "bad unit");
    5130           0 :   MOZ_ASSERT(aValuePair != nullptr, "value pairs may not be null");
    5131           0 :   mUnit = aUnit;
    5132           0 :   mValue.mCSSValuePair = aValuePair; // take ownership
    5133           0 : }
    5134             : 
    5135             : void
    5136           0 : StyleAnimationValue::SetAndAdoptCSSValueTripletValue(
    5137             :                        nsCSSValueTriplet *aValueTriplet, Unit aUnit)
    5138             : {
    5139           0 :   FreeValue();
    5140           0 :   MOZ_ASSERT(IsCSSValueTripletUnit(aUnit), "bad unit");
    5141           0 :   MOZ_ASSERT(aValueTriplet != nullptr, "value pairs may not be null");
    5142           0 :   mUnit = aUnit;
    5143           0 :   mValue.mCSSValueTriplet = aValueTriplet; // take ownership
    5144           0 : }
    5145             : 
    5146             : void
    5147           0 : StyleAnimationValue::SetAndAdoptCSSRectValue(nsCSSRect *aRect, Unit aUnit)
    5148             : {
    5149           0 :   FreeValue();
    5150           0 :   MOZ_ASSERT(IsCSSRectUnit(aUnit), "bad unit");
    5151           0 :   MOZ_ASSERT(aRect != nullptr, "value pairs may not be null");
    5152           0 :   mUnit = aUnit;
    5153           0 :   mValue.mCSSRect = aRect; // take ownership
    5154           0 : }
    5155             : 
    5156             : void
    5157           0 : StyleAnimationValue::SetCSSValueArrayValue(nsCSSValue::Array* aValue,
    5158             :                                            Unit aUnit)
    5159             : {
    5160           0 :   FreeValue();
    5161           0 :   MOZ_ASSERT(IsCSSValueArrayUnit(aUnit), "bad unit");
    5162           0 :   MOZ_ASSERT(aValue != nullptr,
    5163             :              "not currently expecting any arrays to be null");
    5164           0 :   mUnit = aUnit;
    5165           0 :   mValue.mCSSValueArray = aValue;
    5166           0 :   mValue.mCSSValueArray->AddRef();
    5167           0 : }
    5168             : 
    5169             : void
    5170          68 : StyleAnimationValue::SetAndAdoptCSSValueListValue(nsCSSValueList *aValueList,
    5171             :                                                   Unit aUnit)
    5172             : {
    5173          68 :   FreeValue();
    5174          68 :   MOZ_ASSERT(IsCSSValueListUnit(aUnit), "bad unit");
    5175          68 :   MOZ_ASSERT(aUnit == eUnit_Shadow || aUnit == eUnit_Filter ||
    5176             :              aValueList != nullptr,
    5177             :              "value lists other than shadows and filters may not be null");
    5178          68 :   mUnit = aUnit;
    5179          68 :   mValue.mCSSValueList = aValueList; // take ownership
    5180          68 : }
    5181             : 
    5182             : void
    5183           0 : StyleAnimationValue::SetTransformValue(nsCSSValueSharedList* aList)
    5184             : {
    5185           0 :   FreeValue();
    5186           0 :   mUnit = eUnit_Transform;
    5187           0 :   mValue.mCSSValueSharedList = aList;
    5188           0 :   mValue.mCSSValueSharedList->AddRef();
    5189           0 : }
    5190             : 
    5191             : void
    5192           0 : StyleAnimationValue::SetAndAdoptCSSValuePairListValue(
    5193             :                        nsCSSValuePairList *aValuePairList)
    5194             : {
    5195           0 :   FreeValue();
    5196           0 :   MOZ_ASSERT(aValuePairList, "may not be null");
    5197           0 :   mUnit = eUnit_CSSValuePairList;
    5198           0 :   mValue.mCSSValuePairList = aValuePairList; // take ownership
    5199           0 : }
    5200             : 
    5201             : void
    5202        1933 : StyleAnimationValue::FreeValue()
    5203             : {
    5204        1933 :   if (IsCSSValueUnit(mUnit)) {
    5205          94 :     delete mValue.mCSSValue;
    5206        1839 :   } else if (IsCSSValueListUnit(mUnit)) {
    5207          68 :     delete mValue.mCSSValueList;
    5208        1771 :   } else if (IsCSSValueSharedListValue(mUnit)) {
    5209           0 :     mValue.mCSSValueSharedList->Release();
    5210        1771 :   } else if (IsCSSValuePairUnit(mUnit)) {
    5211           0 :     delete mValue.mCSSValuePair;
    5212        1771 :   } else if (IsCSSValueTripletUnit(mUnit)) {
    5213           0 :     delete mValue.mCSSValueTriplet;
    5214        1771 :   } else if (IsCSSRectUnit(mUnit)) {
    5215           0 :     delete mValue.mCSSRect;
    5216        1771 :   } else if (IsCSSValuePairListUnit(mUnit)) {
    5217           0 :     delete mValue.mCSSValuePairList;
    5218        1771 :   } else if (IsCSSValueArrayUnit(mUnit)) {
    5219           0 :     mValue.mCSSValueArray->Release();
    5220        1771 :   } else if (IsStringUnit(mUnit)) {
    5221           0 :     MOZ_ASSERT(mValue.mString, "expecting non-null string");
    5222           0 :     mValue.mString->Release();
    5223        1771 :   } else if (mUnit == eUnit_ComplexColor) {
    5224           0 :     mValue.mComplexColor->Release();
    5225             :   }
    5226        1933 : }
    5227             : 
    5228             : bool
    5229         280 : StyleAnimationValue::operator==(const StyleAnimationValue& aOther) const
    5230             : {
    5231         280 :   if (mUnit != aOther.mUnit) {
    5232           0 :     return false;
    5233             :   }
    5234             : 
    5235         280 :   switch (mUnit) {
    5236             :     case eUnit_Null:
    5237             :     case eUnit_Normal:
    5238             :     case eUnit_Auto:
    5239             :     case eUnit_None:
    5240             :     case eUnit_CurrentColor:
    5241         132 :       return true;
    5242             :     case eUnit_Enumerated:
    5243             :     case eUnit_Visibility:
    5244             :     case eUnit_Integer:
    5245           9 :       return mValue.mInt == aOther.mValue.mInt;
    5246             :     case eUnit_Coord:
    5247          16 :       return mValue.mCoord == aOther.mValue.mCoord;
    5248             :     case eUnit_Percent:
    5249             :     case eUnit_Float:
    5250          42 :       return mValue.mFloat == aOther.mValue.mFloat;
    5251             :     case eUnit_Calc:
    5252             :     case eUnit_Color:
    5253             :     case eUnit_ObjectPosition:
    5254             :     case eUnit_URL:
    5255             :     case eUnit_DiscreteCSSValue:
    5256          47 :       MOZ_ASSERT(IsCSSValueUnit(mUnit),
    5257             :                  "This clause is for handling nsCSSValue-backed units");
    5258          47 :       return *mValue.mCSSValue == *aOther.mValue.mCSSValue;
    5259             :     case eUnit_CSSValuePair:
    5260           0 :       return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
    5261             :     case eUnit_CSSValueTriplet:
    5262           0 :       return *mValue.mCSSValueTriplet == *aOther.mValue.mCSSValueTriplet;
    5263             :     case eUnit_CSSRect:
    5264           0 :       return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
    5265             :     case eUnit_Dasharray:
    5266             :     case eUnit_Shadow:
    5267             :     case eUnit_Filter:
    5268             :     case eUnit_BackgroundPositionCoord:
    5269          34 :       return nsCSSValueList::Equal(mValue.mCSSValueList,
    5270          68 :                                    aOther.mValue.mCSSValueList);
    5271             :     case eUnit_Shape:
    5272           0 :       return *mValue.mCSSValueArray == *aOther.mValue.mCSSValueArray;
    5273             :     case eUnit_Transform:
    5274           0 :       return *mValue.mCSSValueSharedList == *aOther.mValue.mCSSValueSharedList;
    5275             :     case eUnit_CSSValuePairList:
    5276           0 :       return nsCSSValuePairList::Equal(mValue.mCSSValuePairList,
    5277           0 :                                        aOther.mValue.mCSSValuePairList);
    5278             :     case eUnit_UnparsedString:
    5279           0 :       return (NS_strcmp(GetStringBufferValue(),
    5280           0 :                         aOther.GetStringBufferValue()) == 0);
    5281             :     case eUnit_ComplexColor:
    5282           0 :       return *mValue.mComplexColor == *aOther.mValue.mComplexColor;
    5283             :   }
    5284             : 
    5285           0 :   NS_NOTREACHED("incomplete case");
    5286           0 :   return false;
    5287             : }
    5288             : 
    5289             : 
    5290             : // AnimationValue Implementation
    5291             : 
    5292             : bool
    5293         280 : AnimationValue::operator==(const AnimationValue& aOther) const
    5294             : {
    5295             :   // It is possible to compare an empty AnimationValue with others, so both
    5296             :   // mServo and mGecko could be null while comparing.
    5297         280 :   MOZ_ASSERT(!mServo || mGecko.IsNull());
    5298         280 :   if (mServo && aOther.mServo) {
    5299           0 :     return Servo_AnimationValue_DeepEqual(mServo, aOther.mServo);
    5300             :   }
    5301         560 :   return !mServo && !aOther.mServo &&
    5302         560 :          mGecko == aOther.mGecko;
    5303             : }
    5304             : 
    5305             : bool
    5306         260 : AnimationValue::operator!=(const AnimationValue& aOther) const
    5307             : {
    5308         260 :   return !operator==(aOther);
    5309             : }
    5310             : 
    5311             : float
    5312           0 : AnimationValue::GetOpacity() const
    5313             : {
    5314           0 :   MOZ_ASSERT(!mServo != mGecko.IsNull());
    5315           0 :   return mServo ? Servo_AnimationValue_GetOpacity(mServo)
    5316           0 :                 : mGecko.GetFloatValue();
    5317             : }
    5318             : 
    5319             : gfxSize
    5320           0 : AnimationValue::GetScaleValue(const nsIFrame* aFrame) const
    5321             : {
    5322           0 :   MOZ_ASSERT(!mServo != mGecko.IsNull());
    5323           0 :   if (mServo) {
    5324           0 :     RefPtr<nsCSSValueSharedList> list;
    5325           0 :     Servo_AnimationValue_GetTransform(mServo, &list);
    5326           0 :     return nsStyleTransformMatrix::GetScaleValue(list, aFrame);
    5327             :   }
    5328           0 :   return mGecko.GetScaleValue(aFrame);
    5329             : }
    5330             : 
    5331             : void
    5332           0 : AnimationValue::SerializeSpecifiedValue(nsCSSPropertyID aProperty,
    5333             :                                         nsAString& aString) const
    5334             : {
    5335           0 :   MOZ_ASSERT(!mServo != mGecko.IsNull());
    5336           0 :   if (mServo) {
    5337           0 :     Servo_AnimationValue_Serialize(mServo, aProperty, &aString);
    5338           0 :     return;
    5339             :   }
    5340             : 
    5341             :   DebugOnly<bool> uncomputeResult =
    5342           0 :     StyleAnimationValue::UncomputeValue(aProperty, mGecko, aString);
    5343           0 :   MOZ_ASSERT(uncomputeResult, "failed to uncompute StyleAnimationValue");
    5344             : }
    5345             : 
    5346             : bool
    5347           6 : AnimationValue::IsInterpolableWith(nsCSSPropertyID aProperty,
    5348             :                                    const AnimationValue& aToValue) const
    5349             : {
    5350           6 :   if (IsNull() || aToValue.IsNull()) {
    5351           0 :     return false;
    5352             :   }
    5353             : 
    5354           6 :   MOZ_ASSERT(!mServo != mGecko.IsNull());
    5355           6 :   MOZ_ASSERT(mGecko.IsNull() == aToValue.mGecko.IsNull() &&
    5356             :              !mServo == !aToValue.mServo,
    5357             :              "Animation values should have the same style engine");
    5358             : 
    5359           6 :   if (mServo) {
    5360           0 :     return Servo_AnimationValues_IsInterpolable(mServo, aToValue.mServo);
    5361             :   }
    5362             : 
    5363             :   // If this is ever a performance problem, we could add a
    5364             :   // StyleAnimationValue::IsInterpolatable method, but it seems fine for now.
    5365          12 :   StyleAnimationValue dummy;
    5366           6 :   return StyleAnimationValue::Interpolate(
    5367           6 :            aProperty, mGecko, aToValue.mGecko, 0.5, dummy);
    5368             : }
    5369             : 
    5370             : double
    5371           0 : AnimationValue::ComputeDistance(nsCSSPropertyID aProperty,
    5372             :                                 const AnimationValue& aOther,
    5373             :                                 nsStyleContext* aStyleContext) const
    5374             : {
    5375           0 :   if (IsNull() || aOther.IsNull()) {
    5376           0 :     return 0.0;
    5377             :   }
    5378             : 
    5379           0 :   MOZ_ASSERT(!mServo != mGecko.IsNull());
    5380           0 :   MOZ_ASSERT(mGecko.IsNull() == aOther.mGecko.IsNull() &&
    5381             :              !mServo == !aOther.mServo,
    5382             :              "Animation values should have the same style engine");
    5383             : 
    5384           0 :   if (mServo) {
    5385           0 :     return Servo_AnimationValues_ComputeDistance(mServo, aOther.mServo);
    5386             :   }
    5387             : 
    5388           0 :   double distance = 0.0;
    5389           0 :   return StyleAnimationValue::ComputeDistance(aProperty,
    5390             :                                               mGecko,
    5391             :                                               aOther.mGecko,
    5392             :                                               aStyleContext,
    5393             :                                               distance)
    5394           0 :          ? distance
    5395           0 :          : 0.0;
    5396             : }
    5397             : 
    5398             : /* static */ AnimationValue
    5399           0 : AnimationValue::FromString(nsCSSPropertyID aProperty,
    5400             :                            const nsAString& aValue,
    5401             :                            Element* aElement)
    5402             : {
    5403           0 :   MOZ_ASSERT(aElement);
    5404             : 
    5405           0 :   AnimationValue result;
    5406             : 
    5407           0 :   nsCOMPtr<nsIDocument> doc = aElement->GetComposedDoc();
    5408           0 :   if (!doc) {
    5409           0 :     return result;
    5410             :   }
    5411             : 
    5412           0 :   nsCOMPtr<nsIPresShell> shell = doc->GetShell();
    5413           0 :   if (!shell) {
    5414           0 :     return result;
    5415             :   }
    5416             : 
    5417             :   // GetStyleContext() flushes style, so we shouldn't assume that any
    5418             :   // non-owning references we have are still valid.
    5419             :   RefPtr<nsStyleContext> styleContext =
    5420           0 :     nsComputedDOMStyle::GetStyleContext(aElement, nullptr, shell);
    5421             : 
    5422           0 :   if (auto servoContext = styleContext->GetAsServo()) {
    5423           0 :     nsPresContext* presContext = shell->GetPresContext();
    5424           0 :     if (!presContext) {
    5425           0 :       return result;
    5426             :     }
    5427             : 
    5428             :     RefPtr<RawServoDeclarationBlock> declarations =
    5429           0 :       KeyframeUtils::ParseProperty(aProperty, aValue, doc);
    5430             : 
    5431           0 :     if (!declarations) {
    5432           0 :       return result;
    5433             :     }
    5434             : 
    5435             :     const ServoComputedValues* computedValues =
    5436           0 :       servoContext->ComputedValues();
    5437           0 :     result.mServo = presContext->StyleSet()
    5438             :                                ->AsServo()
    5439           0 :                                ->ComputeAnimationValue(aElement,
    5440             :                                                        declarations,
    5441           0 :                                                        computedValues);
    5442           0 :     return result;
    5443             :   }
    5444             : 
    5445           0 :   if (!StyleAnimationValue::ComputeValue(aProperty, aElement, styleContext,
    5446             :                                          aValue, false /* |aUseSVGMode| */,
    5447             :                                          result.mGecko)) {
    5448           0 :     MOZ_ASSERT(result.IsNull());
    5449             :   }
    5450           0 :   return result;
    5451             : }

Generated by: LCOV version 1.13