layout/style/nsStyleAnimation.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /* Utilities for animation of computed style values */
     8 #include "mozilla/ArrayUtils.h"
     9 #include "mozilla/MathAlgorithms.h"
    11 #include "nsStyleAnimation.h"
    12 #include "nsStyleTransformMatrix.h"
    13 #include "nsCOMArray.h"
    14 #include "nsIStyleRule.h"
    15 #include "mozilla/css/StyleRule.h"
    16 #include "nsString.h"
    17 #include "nsStyleContext.h"
    18 #include "nsStyleSet.h"
    19 #include "nsComputedDOMStyle.h"
    20 #include "nsCSSParser.h"
    21 #include "mozilla/css/Declaration.h"
    22 #include "mozilla/dom/Element.h"
    23 #include "mozilla/FloatingPoint.h"
    24 #include "mozilla/Likely.h"
    25 #include "gfxMatrix.h"
    26 #include "gfxQuaternion.h"
    27 #include "nsIDocument.h"
    29 using namespace mozilla;
    31 // HELPER METHODS
    32 // --------------
    33 /*
    34  * Given two units, this method returns a common unit that they can both be
    35  * converted into, if possible.  This is intended to facilitate
    36  * interpolation, distance-computation, and addition between "similar" units.
    37  *
    38  * The ordering of the arguments should not affect the output of this method.
    39  *
    40  * If there's no sensible common unit, this method returns eUnit_Null.
    41  *
    42  * @param   aFirstUnit One unit to resolve.
    43  * @param   aFirstUnit The other unit to resolve.
    44  * @return  A "common" unit that both source units can be converted into, or
    45  *          eUnit_Null if that's not possible.
    46  */
    47 static
    48 nsStyleAnimation::Unit
    49 GetCommonUnit(nsCSSProperty aProperty,
    50               nsStyleAnimation::Unit aFirstUnit,
    51               nsStyleAnimation::Unit aSecondUnit)
    52 {
    53   if (aFirstUnit != aSecondUnit) {
    54     if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
    55         (aFirstUnit == nsStyleAnimation::eUnit_Coord ||
    56          aFirstUnit == nsStyleAnimation::eUnit_Percent ||
    57          aFirstUnit == nsStyleAnimation::eUnit_Calc) &&
    58         (aSecondUnit == nsStyleAnimation::eUnit_Coord ||
    59          aSecondUnit == nsStyleAnimation::eUnit_Percent ||
    60          aSecondUnit == nsStyleAnimation::eUnit_Calc)) {
    61       // We can use calc() as the common unit.
    62       return nsStyleAnimation::eUnit_Calc;
    63     }
    64     return nsStyleAnimation::eUnit_Null;
    65   }
    66   return aFirstUnit;
    67 }
    69 static
    70 nsCSSUnit
    71 GetCommonUnit(nsCSSProperty aProperty,
    72               nsCSSUnit aFirstUnit,
    73               nsCSSUnit aSecondUnit)
    74 {
    75   if (aFirstUnit != aSecondUnit) {
    76     if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
    77         (aFirstUnit == eCSSUnit_Pixel ||
    78          aFirstUnit == eCSSUnit_Percent ||
    79          aFirstUnit == eCSSUnit_Calc) &&
    80         (aSecondUnit == eCSSUnit_Pixel ||
    81          aSecondUnit == eCSSUnit_Percent ||
    82          aSecondUnit == eCSSUnit_Calc)) {
    83       // We can use calc() as the common unit.
    84       return eCSSUnit_Calc;
    85     }
    86     return eCSSUnit_Null;
    87   }
    88   return aFirstUnit;
    89 }
    91 static nsCSSKeyword
    92 ToPrimitive(nsCSSKeyword aKeyword)
    93 {
    94   switch (aKeyword) {
    95     case eCSSKeyword_translatex:
    96     case eCSSKeyword_translatey:
    97     case eCSSKeyword_translatez:
    98     case eCSSKeyword_translate:
    99       return eCSSKeyword_translate3d;
   100     case eCSSKeyword_scalex:
   101     case eCSSKeyword_scaley:
   102     case eCSSKeyword_scalez:
   103     case eCSSKeyword_scale:
   104       return eCSSKeyword_scale3d;
   105     default:
   106       return aKeyword;
   107   }
   108 }
   110 static already_AddRefed<nsCSSValue::Array>
   111 AppendFunction(nsCSSKeyword aTransformFunction)
   112 {
   113   uint32_t nargs;
   114   switch (aTransformFunction) {
   115     case eCSSKeyword_matrix3d:
   116       nargs = 16;
   117       break;
   118     case eCSSKeyword_matrix:
   119       nargs = 6;
   120       break;
   121     case eCSSKeyword_rotate3d:
   122       nargs = 4;
   123       break;
   124     case eCSSKeyword_interpolatematrix:
   125     case eCSSKeyword_translate3d:
   126     case eCSSKeyword_scale3d:
   127       nargs = 3;
   128       break;
   129     case eCSSKeyword_translate:
   130     case eCSSKeyword_skew:
   131     case eCSSKeyword_scale:
   132       nargs = 2;
   133       break;
   134     default:
   135       NS_ERROR("must be a transform function");
   136     case eCSSKeyword_translatex:
   137     case eCSSKeyword_translatey:
   138     case eCSSKeyword_translatez:
   139     case eCSSKeyword_scalex:
   140     case eCSSKeyword_scaley:
   141     case eCSSKeyword_scalez:
   142     case eCSSKeyword_skewx:
   143     case eCSSKeyword_skewy:
   144     case eCSSKeyword_rotate:
   145     case eCSSKeyword_rotatex:
   146     case eCSSKeyword_rotatey:
   147     case eCSSKeyword_rotatez:
   148     case eCSSKeyword_perspective:
   149       nargs = 1;
   150       break;
   151   }
   153   nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(nargs + 1);
   154   arr->Item(0).SetIntValue(aTransformFunction, eCSSUnit_Enumerated);
   156   return arr.forget();
   157 }
   159 static already_AddRefed<nsCSSValue::Array>
   160 ToPrimitive(nsCSSValue::Array* aArray)
   161 {
   162   nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(aArray);
   163   nsCSSKeyword primitive = ToPrimitive(tfunc);
   164   nsRefPtr<nsCSSValue::Array> arr = AppendFunction(primitive);
   166   // FIXME: This would produce fewer calc() expressions if the
   167   // zero were of compatible type (length vs. percent) when
   168   // needed.
   170   nsCSSValue zero(0.0f, eCSSUnit_Pixel);
   171   nsCSSValue one(1.0f, eCSSUnit_Number);
   172   switch(tfunc) {
   173     case eCSSKeyword_translate:
   174     {
   175       NS_ABORT_IF_FALSE(aArray->Count() == 2 || aArray->Count() == 3,
   176                         "unexpected count");
   177       arr->Item(1) = aArray->Item(1);
   178       arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : zero;
   179       arr->Item(3) = zero;
   180       break;
   181     }
   182     case eCSSKeyword_translatex:
   183     {
   184       NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count");
   185       arr->Item(1) = aArray->Item(1);
   186       arr->Item(2) = zero;
   187       arr->Item(3) = zero;
   188       break;
   189     }
   190     case eCSSKeyword_translatey:
   191     {
   192       NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count");
   193       arr->Item(1) = zero;
   194       arr->Item(2) = aArray->Item(1);
   195       arr->Item(3) = zero;
   196       break;
   197     }
   198     case eCSSKeyword_translatez:
   199     {
   200       NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count");
   201       arr->Item(1) = zero;
   202       arr->Item(2) = zero;
   203       arr->Item(3) = aArray->Item(1);
   204       break;
   205     }
   206     case eCSSKeyword_scale:
   207     {
   208       NS_ABORT_IF_FALSE(aArray->Count() == 2 || aArray->Count() == 3,
   209                         "unexpected count");
   210       arr->Item(1) = aArray->Item(1);
   211       arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : aArray->Item(1);
   212       arr->Item(3) = one;
   213       break;
   214     }
   215     case eCSSKeyword_scalex:
   216     {
   217       NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count");
   218       arr->Item(1) = aArray->Item(1);
   219       arr->Item(2) = one;
   220       arr->Item(3) = one;
   221       break;
   222     }
   223     case eCSSKeyword_scaley:
   224     {
   225       NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count");
   226       arr->Item(1) = one;
   227       arr->Item(2) = aArray->Item(1);
   228       arr->Item(3) = one;
   229       break;
   230     }
   231     case eCSSKeyword_scalez:
   232     {
   233       NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count");
   234       arr->Item(1) = one;
   235       arr->Item(2) = one;
   236       arr->Item(3) = aArray->Item(1);
   237       break;
   238     }
   239     default:
   240       arr = aArray;
   241   }
   242   return arr.forget();
   243 }
   245 inline void
   246 nscoordToCSSValue(nscoord aCoord, nsCSSValue& aCSSValue)
   247 {
   248   aCSSValue.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(aCoord),
   249                           eCSSUnit_Pixel);
   250 }
   252 static void
   253 AppendCSSShadowValue(const nsCSSShadowItem *aShadow,
   254                      nsCSSValueList **&aResultTail)
   255 {
   256   NS_ABORT_IF_FALSE(aShadow, "shadow expected");
   258   // X, Y, Radius, Spread, Color, Inset
   259   nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(6);
   260   nscoordToCSSValue(aShadow->mXOffset, arr->Item(0));
   261   nscoordToCSSValue(aShadow->mYOffset, arr->Item(1));
   262   nscoordToCSSValue(aShadow->mRadius, arr->Item(2));
   263   // NOTE: This code sometimes stores mSpread: 0 even when
   264   // the parser would be required to leave it null.
   265   nscoordToCSSValue(aShadow->mSpread, arr->Item(3));
   266   if (aShadow->mHasColor) {
   267     arr->Item(4).SetColorValue(aShadow->mColor);
   268   }
   269   if (aShadow->mInset) {
   270     arr->Item(5).SetIntValue(NS_STYLE_BOX_SHADOW_INSET,
   271                              eCSSUnit_Enumerated);
   272   }
   274   nsCSSValueList *resultItem = new nsCSSValueList;
   275   resultItem->mValue.SetArrayValue(arr, eCSSUnit_Array);
   276   *aResultTail = resultItem;
   277   aResultTail = &resultItem->mNext;
   278 }
   280 // Like nsStyleCoord::Calc, but with length in float pixels instead of nscoord.
   281 struct CalcValue {
   282   float mLength, mPercent;
   283   bool mHasPercent;
   284 };
   286 // Requires a canonical calc() value that we generated.
   287 static CalcValue
   288 ExtractCalcValueInternal(const nsCSSValue& aValue)
   289 {
   290   NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Calc, "unexpected unit");
   291   nsCSSValue::Array *arr = aValue.GetArrayValue();
   292   NS_ABORT_IF_FALSE(arr->Count() == 1, "unexpected length");
   294   const nsCSSValue &topval = arr->Item(0);
   295   CalcValue result;
   296   if (topval.GetUnit() == eCSSUnit_Pixel) {
   297     result.mLength = topval.GetFloatValue();
   298     result.mPercent = 0.0f;
   299     result.mHasPercent = false;
   300   } else {
   301     NS_ABORT_IF_FALSE(topval.GetUnit() == eCSSUnit_Calc_Plus,
   302                       "unexpected unit");
   303     nsCSSValue::Array *arr2 = topval.GetArrayValue();
   304     const nsCSSValue &len = arr2->Item(0);
   305     const nsCSSValue &pct = arr2->Item(1);
   306     NS_ABORT_IF_FALSE(len.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
   307     NS_ABORT_IF_FALSE(pct.GetUnit() == eCSSUnit_Percent, "unexpected unit");
   308     result.mLength = len.GetFloatValue();
   309     result.mPercent = pct.GetPercentValue();
   310     result.mHasPercent = true;
   311   }
   313   return result;
   314 }
   316 // Requires a canonical calc() value that we generated.
   317 static CalcValue
   318 ExtractCalcValue(const nsStyleAnimation::Value& aValue)
   319 {
   320   CalcValue result;
   321   if (aValue.GetUnit() == nsStyleAnimation::eUnit_Coord) {
   322     result.mLength =
   323       nsPresContext::AppUnitsToFloatCSSPixels(aValue.GetCoordValue());
   324     result.mPercent = 0.0f;
   325     result.mHasPercent = false;
   326     return result;
   327   }
   328   if (aValue.GetUnit() == nsStyleAnimation::eUnit_Percent) {
   329     result.mLength = 0.0f;
   330     result.mPercent = aValue.GetPercentValue();
   331     result.mHasPercent = true;
   332     return result;
   333   }
   334   NS_ABORT_IF_FALSE(aValue.GetUnit() == nsStyleAnimation::eUnit_Calc,
   335                     "unexpected unit");
   336   nsCSSValue *val = aValue.GetCSSValueValue();
   337   return ExtractCalcValueInternal(*val);
   338 }
   340 static CalcValue
   341 ExtractCalcValue(const nsCSSValue& aValue)
   342 {
   343   CalcValue result;
   344   if (aValue.GetUnit() == eCSSUnit_Pixel) {
   345     result.mLength = aValue.GetFloatValue();
   346     result.mPercent = 0.0f;
   347     result.mHasPercent = false;
   348     return result;
   349   }
   350   if (aValue.GetUnit() == eCSSUnit_Percent) {
   351     result.mLength = 0.0f;
   352     result.mPercent = aValue.GetPercentValue();
   353     result.mHasPercent = true;
   354     return result;
   355   }
   356   return ExtractCalcValueInternal(aValue);
   357 }
   359 static void
   360 SetCalcValue(const nsStyleCoord::Calc* aCalc, nsCSSValue& aValue)
   361 {
   362   nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
   363   if (!aCalc->mHasPercent) {
   364     nscoordToCSSValue(aCalc->mLength, arr->Item(0));
   365   } else {
   366     nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
   367     arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
   368     nscoordToCSSValue(aCalc->mLength, arr2->Item(0));
   369     arr2->Item(1).SetPercentValue(aCalc->mPercent);
   370   }
   372   aValue.SetArrayValue(arr, eCSSUnit_Calc);
   373 }
   375 static void
   376 SetCalcValue(const CalcValue& aCalc, nsCSSValue& aValue)
   377 {
   378   nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
   379   if (!aCalc.mHasPercent) {
   380     arr->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel);
   381   } else {
   382     nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
   383     arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
   384     arr2->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel);
   385     arr2->Item(1).SetPercentValue(aCalc.mPercent);
   386   }
   388   aValue.SetArrayValue(arr, eCSSUnit_Calc);
   389 }
   391 static already_AddRefed<nsStringBuffer>
   392 GetURIAsUtf16StringBuffer(nsIURI* aUri)
   393 {
   394   nsAutoCString utf8String;
   395   nsresult rv = aUri->GetSpec(utf8String);
   396   NS_ENSURE_SUCCESS(rv, nullptr);
   398   return nsCSSValue::BufferFromString(NS_ConvertUTF8toUTF16(utf8String));
   399 }
   401 // CLASS METHODS
   402 // -------------
   404 bool
   405 nsStyleAnimation::ComputeDistance(nsCSSProperty aProperty,
   406                                   const Value& aStartValue,
   407                                   const Value& aEndValue,
   408                                   double& aDistance)
   409 {
   410   Unit commonUnit =
   411     GetCommonUnit(aProperty, aStartValue.GetUnit(), aEndValue.GetUnit());
   413   switch (commonUnit) {
   414     case eUnit_Null:
   415     case eUnit_Auto:
   416     case eUnit_None:
   417     case eUnit_Normal:
   418     case eUnit_UnparsedString:
   419       return false;
   421     case eUnit_Enumerated:
   422       switch (aProperty) {
   423         case eCSSProperty_font_stretch: {
   424           // just like eUnit_Integer.
   425           int32_t startInt = aStartValue.GetIntValue();
   426           int32_t endInt = aEndValue.GetIntValue();
   427           aDistance = Abs(endInt - startInt);
   428           return true;
   429         }
   430         default:
   431           return false;
   432       }
   433    case eUnit_Visibility: {
   434       int32_t startEnum = aStartValue.GetIntValue();
   435       int32_t endEnum = aEndValue.GetIntValue();
   436       if (startEnum == endEnum) {
   437         aDistance = 0;
   438         return true;
   439       }
   440       if ((startEnum == NS_STYLE_VISIBILITY_VISIBLE) ==
   441           (endEnum == NS_STYLE_VISIBILITY_VISIBLE)) {
   442         return false;
   443       }
   444       aDistance = 1;
   445       return true;
   446     }
   447     case eUnit_Integer: {
   448       int32_t startInt = aStartValue.GetIntValue();
   449       int32_t endInt = aEndValue.GetIntValue();
   450       aDistance = Abs(double(endInt) - double(startInt));
   451       return true;
   452     }
   453     case eUnit_Coord: {
   454       nscoord startCoord = aStartValue.GetCoordValue();
   455       nscoord endCoord = aEndValue.GetCoordValue();
   456       aDistance = Abs(double(endCoord) - double(startCoord));
   457       return true;
   458     }
   459     case eUnit_Percent: {
   460       float startPct = aStartValue.GetPercentValue();
   461       float endPct = aEndValue.GetPercentValue();
   462       aDistance = Abs(double(endPct) - double(startPct));
   463       return true;
   464     }
   465     case eUnit_Float: {
   466       // Special case for flex-grow and flex-shrink: animations are
   467       // disallowed between 0 and other values.
   468       if ((aProperty == eCSSProperty_flex_grow ||
   469            aProperty == eCSSProperty_flex_shrink) &&
   470           (aStartValue.GetFloatValue() == 0.0f ||
   471            aEndValue.GetFloatValue() == 0.0f) &&
   472           aStartValue.GetFloatValue() != aEndValue.GetFloatValue()) {
   473         return false;
   474       }
   476       float startFloat = aStartValue.GetFloatValue();
   477       float endFloat = aEndValue.GetFloatValue();
   478       aDistance = Abs(double(endFloat) - double(startFloat));
   479       return true;
   480     }
   481     case eUnit_Color: {
   482       // http://www.w3.org/TR/smil-animation/#animateColorElement says
   483       // that we should use Euclidean RGB cube distance.  However, we
   484       // have to extend that to RGBA.  For now, we'll just use the
   485       // Euclidean distance in the (part of the) 4-cube of premultiplied
   486       // colors.
   487       // FIXME (spec): The CSS transitions spec doesn't say whether
   488       // colors are premultiplied, but things work better when they are,
   489       // so use premultiplication.  Spec issue is still open per
   490       // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
   491       nscolor startColor = aStartValue.GetColorValue();
   492       nscolor endColor = aEndValue.GetColorValue();
   494       // Get a color component on a 0-1 scale, which is much easier to
   495       // deal with when working with alpha.
   496       #define GET_COMPONENT(component_, color_) \
   497         (NS_GET_##component_(color_) * (1.0 / 255.0))
   499       double startA = GET_COMPONENT(A, startColor);
   500       double startR = GET_COMPONENT(R, startColor) * startA;
   501       double startG = GET_COMPONENT(G, startColor) * startA;
   502       double startB = GET_COMPONENT(B, startColor) * startA;
   503       double endA = GET_COMPONENT(A, endColor);
   504       double endR = GET_COMPONENT(R, endColor) * endA;
   505       double endG = GET_COMPONENT(G, endColor) * endA;
   506       double endB = GET_COMPONENT(B, endColor) * endA;
   508       #undef GET_COMPONENT
   510       double diffA = startA - endA;
   511       double diffR = startR - endR;
   512       double diffG = startG - endG;
   513       double diffB = startB - endB;
   514       aDistance = sqrt(diffA * diffA + diffR * diffR +
   515                        diffG * diffG + diffB * diffB);
   516       return true;
   517     }
   518     case eUnit_Calc: {
   519       CalcValue v1 = ExtractCalcValue(aStartValue);
   520       CalcValue v2 = ExtractCalcValue(aEndValue);
   521       float difflen = v2.mLength - v1.mLength;
   522       float diffpct = v2.mPercent - v1.mPercent;
   523       aDistance = sqrt(difflen * difflen + diffpct * diffpct);
   524       return true;
   525     }
   526     case eUnit_CSSValuePair: {
   527       const nsCSSValuePair *pair1 = aStartValue.GetCSSValuePairValue();
   528       const nsCSSValuePair *pair2 = aEndValue.GetCSSValuePairValue();
   529       nsCSSUnit unit[2];
   530       unit[0] = GetCommonUnit(aProperty, pair1->mXValue.GetUnit(),
   531                               pair2->mXValue.GetUnit());
   532       unit[1] = GetCommonUnit(aProperty, pair1->mYValue.GetUnit(),
   533                               pair2->mYValue.GetUnit());
   534       if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
   535           unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) {
   536         return false;
   537       }
   539       double squareDistance = 0.0;
   540       static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
   541         &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
   542       };
   543       for (uint32_t i = 0; i < 2; ++i) {
   544         nsCSSValue nsCSSValuePair::*member = pairValues[i];
   545         double diffsquared;
   546         switch (unit[i]) {
   547           case eCSSUnit_Pixel: {
   548             float diff = (pair1->*member).GetFloatValue() -
   549                          (pair2->*member).GetFloatValue();
   550             diffsquared = diff * diff;
   551             break;
   552           }
   553           case eCSSUnit_Percent: {
   554             float diff = (pair1->*member).GetPercentValue() -
   555                          (pair2->*member).GetPercentValue();
   556             diffsquared = diff * diff;
   557             break;
   558           }
   559           case eCSSUnit_Calc: {
   560             CalcValue v1 = ExtractCalcValue(pair1->*member);
   561             CalcValue v2 = ExtractCalcValue(pair2->*member);
   562             float difflen = v2.mLength - v1.mLength;
   563             float diffpct = v2.mPercent - v1.mPercent;
   564             diffsquared = difflen * difflen + diffpct * diffpct;
   565             break;
   566           }
   567           default:
   568             NS_ABORT_IF_FALSE(false, "unexpected unit");
   569             return false;
   570         }
   571         squareDistance += diffsquared;
   572       }
   574       aDistance = sqrt(squareDistance);
   575       return true;
   576     }
   577     case eUnit_CSSValueTriplet: {
   578       const nsCSSValueTriplet *triplet1 = aStartValue.GetCSSValueTripletValue();
   579       const nsCSSValueTriplet *triplet2 = aEndValue.GetCSSValueTripletValue();
   580       nsCSSUnit unit[3];
   581       unit[0] = GetCommonUnit(aProperty, triplet1->mXValue.GetUnit(),
   582                               triplet2->mXValue.GetUnit());
   583       unit[1] = GetCommonUnit(aProperty, triplet1->mYValue.GetUnit(),
   584                               triplet2->mYValue.GetUnit());
   585       unit[2] = GetCommonUnit(aProperty, triplet1->mZValue.GetUnit(),
   586                               triplet2->mZValue.GetUnit());
   587       if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
   588           unit[2] == eCSSUnit_Null) {
   589         return false;
   590       }
   592       double squareDistance = 0.0;
   593       static nsCSSValue nsCSSValueTriplet::* const pairValues[3] = {
   594         &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
   595       };
   596       for (uint32_t i = 0; i < 3; ++i) {
   597         nsCSSValue nsCSSValueTriplet::*member = pairValues[i];
   598         double diffsquared;
   599         switch (unit[i]) {
   600           case eCSSUnit_Pixel: {
   601             float diff = (triplet1->*member).GetFloatValue() -
   602                          (triplet2->*member).GetFloatValue();
   603             diffsquared = diff * diff;
   604             break;
   605           }
   606           case eCSSUnit_Percent: {
   607             float diff = (triplet1->*member).GetPercentValue() -
   608                          (triplet2->*member).GetPercentValue();
   609              diffsquared = diff * diff;
   610              break;
   611           }
   612           case eCSSUnit_Calc: {
   613             CalcValue v1 = ExtractCalcValue(triplet1->*member);
   614             CalcValue v2 = ExtractCalcValue(triplet2->*member);
   615             float difflen = v2.mLength - v1.mLength;
   616             float diffpct = v2.mPercent - v1.mPercent;
   617             diffsquared = difflen * difflen + diffpct * diffpct;
   618             break;
   619           }
   620           case eCSSUnit_Null:
   621             diffsquared = 0;
   622             break;
   623           default:
   624             NS_ABORT_IF_FALSE(false, "unexpected unit");
   625             return false;
   626         }
   627         squareDistance += diffsquared;
   628       }
   630       aDistance = sqrt(squareDistance);
   631       return true;
   632     }
   633     case eUnit_CSSRect: {
   634       const nsCSSRect *rect1 = aStartValue.GetCSSRectValue();
   635       const nsCSSRect *rect2 = aEndValue.GetCSSRectValue();
   636       if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
   637           rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
   638           rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
   639           rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
   640         // At least until we have calc()
   641         return false;
   642       }
   644       double squareDistance = 0.0;
   645       for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) {
   646         nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
   647         NS_ABORT_IF_FALSE((rect1->*member).GetUnit() ==
   648                             (rect2->*member).GetUnit(),
   649                           "should have returned above");
   650         double diff;
   651         switch ((rect1->*member).GetUnit()) {
   652           case eCSSUnit_Pixel:
   653             diff = (rect1->*member).GetFloatValue() -
   654                    (rect2->*member).GetFloatValue();
   655             break;
   656           case eCSSUnit_Auto:
   657             diff = 0;
   658             break;
   659           default:
   660             NS_ABORT_IF_FALSE(false, "unexpected unit");
   661             return false;
   662         }
   663         squareDistance += diff * diff;
   664       }
   666       aDistance = sqrt(squareDistance);
   667       return true;
   668     }
   669     case eUnit_Dasharray: {
   670       // NOTE: This produces results on substantially different scales
   671       // for length values and percentage values, which might even be
   672       // mixed in the same property value.  This means the result isn't
   673       // particularly useful for paced animation.
   675       // Call AddWeighted to make us lists of the same length.
   676       Value normValue1, normValue2;
   677       if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
   678                        normValue1) ||
   679           !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
   680                        normValue2)) {
   681         return false;
   682       }
   684       double squareDistance = 0.0;
   685       const nsCSSValueList *list1 = normValue1.GetCSSValueListValue();
   686       const nsCSSValueList *list2 = normValue2.GetCSSValueListValue();
   688       NS_ABORT_IF_FALSE(!list1 == !list2, "lists should be same length");
   689       while (list1) {
   690         const nsCSSValue &val1 = list1->mValue;
   691         const nsCSSValue &val2 = list2->mValue;
   693         NS_ABORT_IF_FALSE(val1.GetUnit() == val2.GetUnit(),
   694                           "unit match should be assured by AddWeighted");
   695         double diff;
   696         switch (val1.GetUnit()) {
   697           case eCSSUnit_Percent:
   698             diff = val1.GetPercentValue() - val2.GetPercentValue();
   699             break;
   700           case eCSSUnit_Number:
   701             diff = val1.GetFloatValue() - val2.GetFloatValue();
   702             break;
   703           default:
   704             NS_ABORT_IF_FALSE(false, "unexpected unit");
   705             return false;
   706         }
   707         squareDistance += diff * diff;
   709         list1 = list1->mNext;
   710         list2 = list2->mNext;
   711         NS_ABORT_IF_FALSE(!list1 == !list2, "lists should be same length");
   712       }
   714       aDistance = sqrt(squareDistance);
   715       return true;
   716     }
   717     case eUnit_Shadow: {
   718       // Call AddWeighted to make us lists of the same length.
   719       Value normValue1, normValue2;
   720       if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
   721                        normValue1) ||
   722           !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
   723                        normValue2)) {
   724         return false;
   725       }
   727       const nsCSSValueList *shadow1 = normValue1.GetCSSValueListValue();
   728       const nsCSSValueList *shadow2 = normValue2.GetCSSValueListValue();
   730       double squareDistance = 0.0;
   731       NS_ABORT_IF_FALSE(!shadow1 == !shadow2, "lists should be same length");
   732       while (shadow1) {
   733         nsCSSValue::Array *array1 = shadow1->mValue.GetArrayValue();
   734         nsCSSValue::Array *array2 = shadow2->mValue.GetArrayValue();
   735         for (size_t i = 0; i < 4; ++i) {
   736           NS_ABORT_IF_FALSE(array1->Item(i).GetUnit() == eCSSUnit_Pixel,
   737                             "unexpected unit");
   738           NS_ABORT_IF_FALSE(array2->Item(i).GetUnit() == eCSSUnit_Pixel,
   739                             "unexpected unit");
   740           double diff = array1->Item(i).GetFloatValue() -
   741                         array2->Item(i).GetFloatValue();
   742           squareDistance += diff * diff;
   743         }
   745         const nsCSSValue &color1 = array1->Item(4);
   746         const nsCSSValue &color2 = array2->Item(4);
   747 #ifdef DEBUG
   748         {
   749           const nsCSSValue &inset1 = array1->Item(5);
   750           const nsCSSValue &inset2 = array2->Item(5);
   751           // There are only two possible states of the inset value:
   752           //  (1) GetUnit() == eCSSUnit_Null
   753           //  (2) GetUnit() == eCSSUnit_Enumerated &&
   754           //      GetIntValue() == NS_STYLE_BOX_SHADOW_INSET
   755           NS_ABORT_IF_FALSE(((color1.IsNumericColorUnit() &&
   756                               color2.IsNumericColorUnit()) ||
   757                              (color1.GetUnit() == color2.GetUnit())) &&
   758                             inset1 == inset2,
   759                             "AddWeighted should have failed");
   760         }
   761 #endif
   763         if (color1.GetUnit() != eCSSUnit_Null) {
   764           nsStyleAnimation::Value color1Value
   765             (color1.GetColorValue(), nsStyleAnimation::Value::ColorConstructor);
   766           nsStyleAnimation::Value color2Value
   767             (color2.GetColorValue(), nsStyleAnimation::Value::ColorConstructor);
   768           double colorDistance;
   770         #ifdef DEBUG
   771           bool ok =
   772         #endif
   773             nsStyleAnimation::ComputeDistance(eCSSProperty_color,
   774                                               color1Value, color2Value,
   775                                               colorDistance);
   776           NS_ABORT_IF_FALSE(ok, "should not fail");
   777           squareDistance += colorDistance * colorDistance;
   778         }
   780         shadow1 = shadow1->mNext;
   781         shadow2 = shadow2->mNext;
   782         NS_ABORT_IF_FALSE(!shadow1 == !shadow2, "lists should be same length");
   783       }
   784       aDistance = sqrt(squareDistance);
   785       return true;
   786     }
   787     case eUnit_Filter:
   788       // FIXME: Support paced animations for filter function interpolation.
   789     case eUnit_Transform: {
   790       return false;
   791     }
   792     case eUnit_BackgroundPosition: {
   793       const nsCSSValueList *position1 = aStartValue.GetCSSValueListValue();
   794       const nsCSSValueList *position2 = aEndValue.GetCSSValueListValue();
   796       double squareDistance = 0.0;
   797       NS_ABORT_IF_FALSE(!position1 == !position2, "lists should be same length");
   799       while (position1 && position2) {
   800         NS_ASSERTION(position1->mValue.GetUnit() == eCSSUnit_Array &&
   801                      position2->mValue.GetUnit() == eCSSUnit_Array,
   802                      "Expected two arrays");
   804         CalcValue calcVal[4];
   806         nsCSSValue::Array* bgArray = position1->mValue.GetArrayValue();
   807         NS_ABORT_IF_FALSE(bgArray->Count() == 4, "Invalid background-position");
   808         NS_ASSERTION(bgArray->Item(0).GetUnit() == eCSSUnit_Null &&
   809                      bgArray->Item(2).GetUnit() == eCSSUnit_Null,
   810                      "Invalid list used");
   811         for (int i = 0; i < 2; ++i) {
   812           NS_ABORT_IF_FALSE(bgArray->Item(i*2+1).GetUnit() != eCSSUnit_Null,
   813                             "Invalid background-position");
   814           calcVal[i] = ExtractCalcValue(bgArray->Item(i*2+1));
   815         }
   817         bgArray = position2->mValue.GetArrayValue();
   818         NS_ABORT_IF_FALSE(bgArray->Count() == 4, "Invalid background-position");
   819         NS_ASSERTION(bgArray->Item(0).GetUnit() == eCSSUnit_Null &&
   820                      bgArray->Item(2).GetUnit() == eCSSUnit_Null,
   821                      "Invalid list used");
   822         for (int i = 0; i < 2; ++i) {
   823           NS_ABORT_IF_FALSE(bgArray->Item(i*2+1).GetUnit() != eCSSUnit_Null,
   824                             "Invalid background-position");
   825           calcVal[i+2] = ExtractCalcValue(bgArray->Item(i*2+1));
   826         }
   828         for (int i = 0; i < 2; ++i) {
   829           float difflen = calcVal[i+2].mLength - calcVal[i].mLength;
   830           float diffpct = calcVal[i+2].mPercent - calcVal[i].mPercent;
   831           squareDistance += difflen * difflen + diffpct * diffpct;
   832         }
   834         position1 = position1->mNext;
   835         position2 = position2->mNext;
   836       }
   837       // fail if lists differ in length.
   838       if (position1 || position2) {
   839         return false;
   840       }
   842       aDistance = sqrt(squareDistance);
   843       return true;
   844     }
   845     case eUnit_CSSValuePairList: {
   846       const nsCSSValuePairList *list1 = aStartValue.GetCSSValuePairListValue();
   847       const nsCSSValuePairList *list2 = aEndValue.GetCSSValuePairListValue();
   848       double squareDistance = 0.0;
   849       do {
   850         static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
   851           &nsCSSValuePairList::mXValue,
   852           &nsCSSValuePairList::mYValue,
   853         };
   854         for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) {
   855           const nsCSSValue &v1 = list1->*(pairListValues[i]);
   856           const nsCSSValue &v2 = list2->*(pairListValues[i]);
   857           nsCSSUnit unit =
   858             GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
   859           if (unit == eCSSUnit_Null) {
   860             return false;
   861           }
   862           double diffsquared = 0.0;
   863           switch (unit) {
   864             case eCSSUnit_Pixel: {
   865               float diff = v1.GetFloatValue() - v2.GetFloatValue();
   866               diffsquared = diff * diff;
   867               break;
   868             }
   869             case eCSSUnit_Percent: {
   870               float diff = v1.GetPercentValue() - v2.GetPercentValue();
   871               diffsquared = diff * diff;
   872               break;
   873             }
   874             case eCSSUnit_Calc: {
   875               CalcValue val1 = ExtractCalcValue(v1);
   876               CalcValue val2 = ExtractCalcValue(v2);
   877               float difflen = val2.mLength - val1.mLength;
   878               float diffpct = val2.mPercent - val1.mPercent;
   879               diffsquared = difflen * difflen + diffpct * diffpct;
   880               break;
   881             }
   882             default:
   883               if (v1 != v2) {
   884                 return false;
   885               }
   886               break;
   887           }
   888           squareDistance += diffsquared;
   889         }
   890         list1 = list1->mNext;
   891         list2 = list2->mNext;
   892       } while (list1 && list2);
   893       if (list1 || list2) {
   894         // We can't interpolate lists of different lengths.
   895         return false;
   896       }
   897       aDistance = sqrt(squareDistance);
   898       return true;
   899     }
   900   }
   902   NS_ABORT_IF_FALSE(false, "Can't compute distance using the given common unit");
   903   return false;
   904 }
   906 #define MAX_PACKED_COLOR_COMPONENT 255
   908 inline uint8_t ClampColor(double aColor)
   909 {
   910   if (aColor >= MAX_PACKED_COLOR_COMPONENT)
   911     return MAX_PACKED_COLOR_COMPONENT;
   912   if (aColor <= 0.0)
   913     return 0;
   914   return NSToIntRound(aColor);
   915 }
   917 // Ensure that a float/double value isn't NaN by returning zero instead
   918 // (NaN doesn't have a sign) as a general restriction for floating point
   919 // values in RestrictValue.
   920 template<typename T>
   921 MOZ_ALWAYS_INLINE T
   922 EnsureNotNan(T aValue)
   923 {
   924   return aValue;
   925 }
   926 template<>
   927 MOZ_ALWAYS_INLINE float
   928 EnsureNotNan(float aValue)
   929 {
   930   // This would benefit from a MOZ_FLOAT_IS_NaN if we had one.
   931   return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0;
   932 }
   933 template<>
   934 MOZ_ALWAYS_INLINE double
   935 EnsureNotNan(double aValue)
   936 {
   937   return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0;
   938 }
   940 template <typename T>
   941 T
   942 RestrictValue(uint32_t aRestrictions, T aValue)
   943 {
   944   T result = EnsureNotNan(aValue);
   945   switch (aRestrictions) {
   946     case 0:
   947       break;
   948     case CSS_PROPERTY_VALUE_NONNEGATIVE:
   949       if (result < 0) {
   950         result = 0;
   951       }
   952       break;
   953     case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
   954       if (result < 1) {
   955         result = 1;
   956       }
   957       break;
   958     default:
   959       NS_ABORT_IF_FALSE(false, "bad value restriction");
   960       break;
   961   }
   962   return result;
   963 }
   965 template <typename T>
   966 T
   967 RestrictValue(nsCSSProperty aProperty, T aValue)
   968 {
   969   return RestrictValue(nsCSSProps::ValueRestrictions(aProperty), aValue);
   970 }
   972 static inline void
   973 AddCSSValuePixel(double aCoeff1, const nsCSSValue &aValue1,
   974                  double aCoeff2, const nsCSSValue &aValue2,
   975                  nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
   976 {
   977   NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
   978   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
   979   aResult.SetFloatValue(RestrictValue(aValueRestrictions,
   980                                       aCoeff1 * aValue1.GetFloatValue() +
   981                                       aCoeff2 * aValue2.GetFloatValue()),
   982                         eCSSUnit_Pixel);
   983 }
   985 static inline void
   986 AddCSSValueNumber(double aCoeff1, const nsCSSValue &aValue1,
   987                   double aCoeff2, const nsCSSValue &aValue2,
   988                   nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
   989 {
   990   NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
   991   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
   992   aResult.SetFloatValue(RestrictValue(aValueRestrictions,
   993                                       aCoeff1 * aValue1.GetFloatValue() +
   994                                       aCoeff2 * aValue2.GetFloatValue()),
   995                         eCSSUnit_Number);
   996 }
   998 static inline void
   999 AddCSSValuePercent(double aCoeff1, const nsCSSValue &aValue1,
  1000                    double aCoeff2, const nsCSSValue &aValue2,
  1001                    nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
  1003   NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Percent, "unexpected unit");
  1004   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Percent, "unexpected unit");
  1005   aResult.SetPercentValue(RestrictValue(aValueRestrictions,
  1006                                         aCoeff1 * aValue1.GetPercentValue() +
  1007                                         aCoeff2 * aValue2.GetPercentValue()));
  1010 // Add two canonical-form calc values (eUnit_Calc) to make another
  1011 // canonical-form calc value.
  1012 static void
  1013 AddCSSValueCanonicalCalc(double aCoeff1, const nsCSSValue &aValue1,
  1014                          double aCoeff2, const nsCSSValue &aValue2,
  1015                          nsCSSValue &aResult)
  1017   CalcValue v1 = ExtractCalcValue(aValue1);
  1018   CalcValue v2 = ExtractCalcValue(aValue2);
  1019   CalcValue result;
  1020   result.mLength = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
  1021   result.mPercent = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
  1022   result.mHasPercent = v1.mHasPercent || v2.mHasPercent;
  1023   MOZ_ASSERT(result.mHasPercent || result.mPercent == 0.0f,
  1024              "can't have a nonzero percentage part without having percentages");
  1025   SetCalcValue(result, aResult);
  1028 static void
  1029 AddCSSValueAngle(double aCoeff1, const nsCSSValue &aValue1,
  1030                  double aCoeff2, const nsCSSValue &aValue2,
  1031                  nsCSSValue &aResult)
  1033   aResult.SetFloatValue(aCoeff1 * aValue1.GetAngleValueInRadians() +
  1034                         aCoeff2 * aValue2.GetAngleValueInRadians(),
  1035                         eCSSUnit_Radian);
  1038 static bool
  1039 AddCSSValuePixelPercentCalc(const uint32_t aValueRestrictions,
  1040                             const nsCSSUnit aCommonUnit,
  1041                             double aCoeff1, const nsCSSValue &aValue1,
  1042                             double aCoeff2, const nsCSSValue &aValue2,
  1043                             nsCSSValue &aResult)
  1045   switch (aCommonUnit) {
  1046     case eCSSUnit_Pixel:
  1047       AddCSSValuePixel(aCoeff1, aValue1,
  1048                        aCoeff2, aValue2,
  1049                        aResult, aValueRestrictions);
  1050       break;
  1051     case eCSSUnit_Percent:
  1052       AddCSSValuePercent(aCoeff1, aValue1,
  1053                          aCoeff2, aValue2,
  1054                          aResult, aValueRestrictions);
  1055       break;
  1056     case eCSSUnit_Calc:
  1057       AddCSSValueCanonicalCalc(aCoeff1, aValue1,
  1058                                aCoeff2, aValue2,
  1059                                aResult);
  1060       break;
  1061     default:
  1062       return false;
  1065   return true;
  1068 static inline float
  1069 GetNumberOrPercent(const nsCSSValue &aValue)
  1071   nsCSSUnit unit = aValue.GetUnit();
  1072   NS_ABORT_IF_FALSE(unit == eCSSUnit_Number || unit == eCSSUnit_Percent,
  1073                     "unexpected unit");
  1074   return (unit == eCSSUnit_Number) ?
  1075     aValue.GetFloatValue() : aValue.GetPercentValue();
  1078 static inline void
  1079 AddCSSValuePercentNumber(const uint32_t aValueRestrictions,
  1080                          double aCoeff1, const nsCSSValue &aValue1,
  1081                          double aCoeff2, const nsCSSValue &aValue2,
  1082                          nsCSSValue &aResult, float aInitialVal)
  1084   float n1 = GetNumberOrPercent(aValue1);
  1085   float n2 = GetNumberOrPercent(aValue2);
  1087   // Rather than interpolating aValue1 and aValue2 directly, we
  1088   // interpolate their *distances from aInitialVal* (the initial value,
  1089   // which is either 1 or 0 for "filter" functions).  This matters in
  1090   // cases where aInitialVal is nonzero and the coefficients don't add
  1091   // up to 1.  For example, if initialVal is 1, aCoeff1 is 0.5, and
  1092   // aCoeff2 is 0, then we'll return the value halfway between 1 and
  1093   // aValue1, rather than the value halfway between 0 and aValue1.
  1094   // Note that we do something similar in AddTransformScale().
  1095   float result = (n1 - aInitialVal) * aCoeff1 + (n2 - aInitialVal) * aCoeff2;
  1096   aResult.SetFloatValue(RestrictValue(aValueRestrictions, result + aInitialVal),
  1097                         eCSSUnit_Number);
  1100 static bool
  1101 AddShadowItems(double aCoeff1, const nsCSSValue &aValue1,
  1102                double aCoeff2, const nsCSSValue &aValue2,
  1103                nsCSSValueList **&aResultTail)
  1105   // X, Y, Radius, Spread, Color, Inset
  1106   NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Array,
  1107                     "wrong unit");
  1108   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Array,
  1109                     "wrong unit");
  1110   nsCSSValue::Array *array1 = aValue1.GetArrayValue();
  1111   nsCSSValue::Array *array2 = aValue2.GetArrayValue();
  1112   nsRefPtr<nsCSSValue::Array> resultArray = nsCSSValue::Array::Create(6);
  1114   for (size_t i = 0; i < 4; ++i) {
  1115     AddCSSValuePixel(aCoeff1, array1->Item(i), aCoeff2, array2->Item(i),
  1116                      resultArray->Item(i),
  1117                      // blur radius must be nonnegative
  1118                      (i == 2) ? CSS_PROPERTY_VALUE_NONNEGATIVE : 0);
  1121   const nsCSSValue& color1 = array1->Item(4);
  1122   const nsCSSValue& color2 = array2->Item(4);
  1123   const nsCSSValue& inset1 = array1->Item(5);
  1124   const nsCSSValue& inset2 = array2->Item(5);
  1125   if (color1.GetUnit() != color2.GetUnit() ||
  1126       inset1.GetUnit() != inset2.GetUnit()) {
  1127     // We don't know how to animate between color and no-color, or
  1128     // between inset and not-inset.
  1129     return false;
  1132   if (color1.GetUnit() != eCSSUnit_Null) {
  1133     nsStyleAnimation::Value color1Value
  1134       (color1.GetColorValue(), nsStyleAnimation::Value::ColorConstructor);
  1135     nsStyleAnimation::Value color2Value
  1136       (color2.GetColorValue(), nsStyleAnimation::Value::ColorConstructor);
  1137     nsStyleAnimation::Value resultColorValue;
  1138   #ifdef DEBUG
  1139     bool ok =
  1140   #endif
  1141       nsStyleAnimation::AddWeighted(eCSSProperty_color, aCoeff1, color1Value,
  1142                                     aCoeff2, color2Value, resultColorValue);
  1143     NS_ABORT_IF_FALSE(ok, "should not fail");
  1144     resultArray->Item(4).SetColorValue(resultColorValue.GetColorValue());
  1147   NS_ABORT_IF_FALSE(inset1 == inset2, "should match");
  1148   resultArray->Item(5) = inset1;
  1150   nsCSSValueList *resultItem = new nsCSSValueList;
  1151   if (!resultItem) {
  1152     return false;
  1154   resultItem->mValue.SetArrayValue(resultArray, eCSSUnit_Array);
  1155   *aResultTail = resultItem;
  1156   aResultTail = &resultItem->mNext;
  1157   return true;
  1160 static void
  1161 AddTransformTranslate(double aCoeff1, const nsCSSValue &aValue1,
  1162                       double aCoeff2, const nsCSSValue &aValue2,
  1163                       nsCSSValue &aResult)
  1165   NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Percent ||
  1166                     aValue1.GetUnit() == eCSSUnit_Pixel ||
  1167                     aValue1.IsCalcUnit(),
  1168                     "unexpected unit");
  1169   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Percent ||
  1170                     aValue2.GetUnit() == eCSSUnit_Pixel ||
  1171                     aValue2.IsCalcUnit(),
  1172                     "unexpected unit");
  1174   if (aValue1.GetUnit() != aValue2.GetUnit() || aValue1.IsCalcUnit()) {
  1175     // different units; create a calc() expression
  1176     AddCSSValueCanonicalCalc(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
  1177   } else if (aValue1.GetUnit() == eCSSUnit_Percent) {
  1178     // both percent
  1179     AddCSSValuePercent(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
  1180   } else {
  1181     // both pixels
  1182     AddCSSValuePixel(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
  1186 static void
  1187 AddTransformScale(double aCoeff1, const nsCSSValue &aValue1,
  1188                   double aCoeff2, const nsCSSValue &aValue2,
  1189                   nsCSSValue &aResult)
  1191   // Handle scale, and the two matrix components where identity is 1, by
  1192   // subtracting 1, multiplying by the coefficients, and then adding 1
  1193   // back.  This gets the right AddWeighted behavior and gets us the
  1194   // interpolation-against-identity behavior for free.
  1195   NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
  1196   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
  1198   float v1 = aValue1.GetFloatValue() - 1.0f,
  1199         v2 = aValue2.GetFloatValue() - 1.0f;
  1200   float result = v1 * aCoeff1 + v2 * aCoeff2;
  1201   aResult.SetFloatValue(result + 1.0f, eCSSUnit_Number);
  1204 /* static */ already_AddRefed<nsCSSValue::Array>
  1205 nsStyleAnimation::AppendTransformFunction(nsCSSKeyword aTransformFunction,
  1206                                           nsCSSValueList**& aListTail)
  1208   nsRefPtr<nsCSSValue::Array> arr = AppendFunction(aTransformFunction);
  1209   nsCSSValueList *item = new nsCSSValueList;
  1210   item->mValue.SetArrayValue(arr, eCSSUnit_Function);
  1212   *aListTail = item;
  1213   aListTail = &item->mNext;
  1215   return arr.forget();
  1218 /*
  1219  * The relevant section of the transitions specification:
  1220  * http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
  1221  * defers all of the details to the 2-D and 3-D transforms specifications.
  1222  * For the 2-D transforms specification (all that's relevant for us, right
  1223  * now), the relevant section is:
  1224  * http://dev.w3.org/csswg/css3-2d-transforms/#animation
  1225  * This, in turn, refers to the unmatrix program in Graphics Gems,
  1226  * available from http://tog.acm.org/resources/GraphicsGems/ , and in
  1227  * particular as the file GraphicsGems/gemsii/unmatrix.c
  1228  * in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
  1230  * The unmatrix reference is for general 3-D transform matrices (any of the
  1231  * 16 components can have any value).
  1233  * For CSS 2-D transforms, we have a 2-D matrix with the bottom row constant:
  1235  * [ A C E ]
  1236  * [ B D F ]
  1237  * [ 0 0 1 ]
  1239  * For that case, I believe the algorithm in unmatrix reduces to:
  1241  *  (1) If A * D - B * C == 0, the matrix is singular.  Fail.
  1243  *  (2) Set translation components (Tx and Ty) to the translation parts of
  1244  *      the matrix (E and F) and then ignore them for the rest of the time.
  1245  *      (For us, E and F each actually consist of three constants:  a
  1246  *      length, a multiplier for the width, and a multiplier for the
  1247  *      height.  This actually requires its own decomposition, but I'll
  1248  *      keep that separate.)
  1250  *  (3) Let the X scale (Sx) be sqrt(A^2 + B^2).  Then divide both A and B
  1251  *      by it.
  1253  *  (4) Let the XY shear (K) be A * C + B * D.  From C, subtract A times
  1254  *      the XY shear.  From D, subtract B times the XY shear.
  1256  *  (5) Let the Y scale (Sy) be sqrt(C^2 + D^2).  Divide C, D, and the XY
  1257  *      shear (K) by it.
  1259  *  (6) At this point, A * D - B * C is either 1 or -1.  If it is -1,
  1260  *      negate the XY shear (K), the X scale (Sx), and A, B, C, and D.
  1261  *      (Alternatively, we could negate the XY shear (K) and the Y scale
  1262  *      (Sy).)
  1264  *  (7) Let the rotation be R = atan2(B, A).
  1266  * Then the resulting decomposed transformation is:
  1268  *   translate(Tx, Ty) rotate(R) skewX(atan(K)) scale(Sx, Sy)
  1270  * An interesting result of this is that all of the simple transform
  1271  * functions (i.e., all functions other than matrix()), in isolation,
  1272  * decompose back to themselves except for:
  1273  *   'skewY(φ)', which is 'matrix(1, tan(φ), 0, 1, 0, 0)', which decomposes
  1274  *   to 'rotate(φ) skewX(φ) scale(sec(φ), cos(φ))' since (ignoring the
  1275  *   alternate sign possibilities that would get fixed in step 6):
  1276  *     In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
  1277  *     Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
  1278  *     In step 4, the XY shear is sin(φ).
  1279  *     Thus, after step 4, C = -cos(φ)sin(φ) and D = 1 - sin²(φ) = cos²(φ).
  1280  *     Thus, in step 5, the Y scale is sqrt(cos²(φ)(sin²(φ) + cos²(φ)) = cos(φ).
  1281  *     Thus, after step 5, C = -sin(φ), D = cos(φ), and the XY shear is tan(φ).
  1282  *     Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
  1283  *     In step 7, the rotation is thus φ.
  1285  *   skew(θ, φ), which is matrix(1, tan(φ), tan(θ), 1, 0, 0), which decomposes
  1286  *   to 'rotate(φ) skewX(θ + φ) scale(sec(φ), cos(φ))' since (ignoring
  1287  *   the alternate sign possibilities that would get fixed in step 6):
  1288  *     In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
  1289  *     Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
  1290  *     In step 4, the XY shear is cos(φ)tan(θ) + sin(φ).
  1291  *     Thus, after step 4,
  1292  *     C = tan(θ) - cos(φ)(cos(φ)tan(θ) + sin(φ)) = tan(θ)sin²(φ) - cos(φ)sin(φ)
  1293  *     D = 1 - sin(φ)(cos(φ)tan(θ) + sin(φ)) = cos²(φ) - sin(φ)cos(φ)tan(θ)
  1294  *     Thus, in step 5, the Y scale is sqrt(C² + D²) =
  1295  *     sqrt(tan²(θ)(sin⁴(φ) + sin²(φ)cos²(φ)) -
  1296  *          2 tan(θ)(sin³(φ)cos(φ) + sin(φ)cos³(φ)) +
  1297  *          (sin²(φ)cos²(φ) + cos⁴(φ))) =
  1298  *     sqrt(tan²(θ)sin²(φ) - 2 tan(θ)sin(φ)cos(φ) + cos²(φ)) =
  1299  *     cos(φ) - tan(θ)sin(φ) (taking the negative of the obvious solution so
  1300  *     we avoid flipping in step 6).
  1301  *     After step 5, C = -sin(φ) and D = cos(φ), and the XY shear is
  1302  *     (cos(φ)tan(θ) + sin(φ)) / (cos(φ) - tan(θ)sin(φ)) =
  1303  *     (dividing both numerator and denominator by cos(φ))
  1304  *     (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)) = tan(θ + φ).
  1305  *     (See http://en.wikipedia.org/wiki/List_of_trigonometric_identities .)
  1306  *     Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
  1307  *     In step 7, the rotation is thus φ.
  1309  *     To check this result, we can multiply things back together:
  1311  *     [ cos(φ) -sin(φ) ] [ 1 tan(θ + φ) ] [ sec(φ)    0   ]
  1312  *     [ sin(φ)  cos(φ) ] [ 0      1     ] [   0    cos(φ) ]
  1314  *     [ cos(φ)      cos(φ)tan(θ + φ) - sin(φ) ] [ sec(φ)    0   ]
  1315  *     [ sin(φ)      sin(φ)tan(θ + φ) + cos(φ) ] [   0    cos(φ) ]
  1317  *     but since tan(θ + φ) = (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)),
  1318  *     cos(φ)tan(θ + φ) - sin(φ)
  1319  *      = cos(φ)(tan(θ) + tan(φ)) - sin(φ) + sin(φ)tan(θ)tan(φ)
  1320  *      = cos(φ)tan(θ) + sin(φ) - sin(φ) + sin(φ)tan(θ)tan(φ)
  1321  *      = cos(φ)tan(θ) + sin(φ)tan(θ)tan(φ)
  1322  *      = tan(θ) (cos(φ) + sin(φ)tan(φ))
  1323  *      = tan(θ) sec(φ) (cos²(φ) + sin²(φ))
  1324  *      = tan(θ) sec(φ)
  1325  *     and
  1326  *     sin(φ)tan(θ + φ) + cos(φ)
  1327  *      = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ)
  1328  *      = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ)
  1329  *      = sec(φ) (sin²(φ) + cos²(φ))
  1330  *      = sec(φ)
  1331  *     so the above is:
  1332  *     [ cos(φ)  tan(θ) sec(φ) ] [ sec(φ)    0   ]
  1333  *     [ sin(φ)     sec(φ)     ] [   0    cos(φ) ]
  1335  *     [    1   tan(θ) ]
  1336  *     [ tan(φ)    1   ]
  1337  */
  1339 /*
  1340  * Decompose2DMatrix implements the above decomposition algorithm.
  1341  */
  1343 #define XYSHEAR 0
  1344 #define XZSHEAR 1
  1345 #define YZSHEAR 2
  1347 static bool
  1348 Decompose2DMatrix(const gfxMatrix &aMatrix, gfxPoint3D &aScale,
  1349                   float aShear[3], gfxQuaternion &aRotate,
  1350                   gfxPoint3D &aTranslate)
  1352   float A = aMatrix.xx,
  1353         B = aMatrix.yx,
  1354         C = aMatrix.xy,
  1355         D = aMatrix.yy;
  1356   if (A * D == B * C) {
  1357     // singular matrix
  1358     return false;
  1361   float scaleX = sqrt(A * A + B * B);
  1362   A /= scaleX;
  1363   B /= scaleX;
  1365   float XYshear = A * C + B * D;
  1366   C -= A * XYshear;
  1367   D -= B * XYshear;
  1369   float scaleY = sqrt(C * C + D * D);
  1370   C /= scaleY;
  1371   D /= scaleY;
  1372   XYshear /= scaleY;
  1374   // A*D - B*C should now be 1 or -1
  1375   NS_ASSERTION(0.99 < Abs(A*D - B*C) && Abs(A*D - B*C) < 1.01,
  1376                "determinant should now be 1 or -1");
  1377   if (A * D < B * C) {
  1378     A = -A;
  1379     B = -B;
  1380     C = -C;
  1381     D = -D;
  1382     XYshear = -XYshear;
  1383     scaleX = -scaleX;
  1386   float rotate = atan2f(B, A);
  1387   aRotate = gfxQuaternion(0, 0, sin(rotate/2), cos(rotate/2));
  1388   aShear[XYSHEAR] = XYshear;
  1389   aScale.x = scaleX;
  1390   aScale.y = scaleY;
  1391   aTranslate.x = aMatrix.x0;
  1392   aTranslate.y = aMatrix.y0;
  1393   return true;
  1396 /**
  1397  * Implementation of the unmatrix algorithm, specified by:
  1399  * http://dev.w3.org/csswg/css3-2d-transforms/#unmatrix
  1401  * This, in turn, refers to the unmatrix program in Graphics Gems,
  1402  * available from http://tog.acm.org/resources/GraphicsGems/ , and in
  1403  * particular as the file GraphicsGems/gemsii/unmatrix.c
  1404  * in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
  1405  */
  1406 static bool
  1407 Decompose3DMatrix(const gfx3DMatrix &aMatrix, gfxPoint3D &aScale,
  1408                   float aShear[3], gfxQuaternion &aRotate,
  1409                   gfxPoint3D &aTranslate, gfxPointH3D &aPerspective)
  1411   gfx3DMatrix local = aMatrix;
  1413   if (local[3][3] == 0) {
  1414     return false;
  1416   /* Normalize the matrix */
  1417   local.Normalize();
  1419   /**
  1420    * perspective is used to solve for perspective, but it also provides
  1421    * an easy way to test for singularity of the upper 3x3 component.
  1422    */
  1423   gfx3DMatrix perspective = local;
  1424   gfxPointH3D empty(0, 0, 0, 1);
  1425   perspective.SetTransposedVector(3, empty);
  1427   if (perspective.Determinant() == 0.0) {
  1428     return false;
  1431   /* First, isolate perspective. */
  1432   if (local[0][3] != 0 || local[1][3] != 0 ||
  1433       local[2][3] != 0) {
  1434     /* aPerspective is the right hand side of the equation. */
  1435     aPerspective = local.TransposedVector(3);
  1437     /**
  1438      * Solve the equation by inverting perspective and multiplying
  1439      * aPerspective by the inverse.
  1440      */
  1441     perspective.Invert();
  1442     aPerspective = perspective.TransposeTransform4D(aPerspective);
  1444     /* Clear the perspective partition */
  1445     local.SetTransposedVector(3, empty);
  1446   } else {
  1447     aPerspective = gfxPointH3D(0, 0, 0, 1);
  1450   /* Next take care of translation */
  1451   for (int i = 0; i < 3; i++) {
  1452     aTranslate[i] = local[3][i];
  1453     local[3][i] = 0;
  1456   /* Now get scale and shear. */
  1458   /* Compute X scale factor and normalize first row. */
  1459   aScale.x = local[0].Length();
  1460   local[0] /= aScale.x;
  1462   /* Compute XY shear factor and make 2nd local orthogonal to 1st. */
  1463   aShear[XYSHEAR] = local[0].DotProduct(local[1]);
  1464   local[1] -= local[0] * aShear[XYSHEAR];
  1466   /* Now, compute Y scale and normalize 2nd local. */
  1467   aScale.y = local[1].Length();
  1468   local[1] /= aScale.y;
  1469   aShear[XYSHEAR] /= aScale.y;
  1471   /* Compute XZ and YZ shears, make 3rd local orthogonal */
  1472   aShear[XZSHEAR] = local[0].DotProduct(local[2]);
  1473   local[2] -= local[0] * aShear[XZSHEAR];
  1474   aShear[YZSHEAR] = local[1].DotProduct(local[2]);
  1475   local[2] -= local[1] * aShear[YZSHEAR];
  1477   /* Next, get Z scale and normalize 3rd local. */
  1478   aScale.z = local[2].Length();
  1479   local[2] /= aScale.z;
  1481   aShear[XZSHEAR] /= aScale.z;
  1482   aShear[YZSHEAR] /= aScale.z;
  1484   /**
  1485    * At this point, the matrix (in locals) is orthonormal.
  1486    * Check for a coordinate system flip.  If the determinant
  1487    * is -1, then negate the matrix and the scaling factors.
  1488    */
  1489   if (local[0].DotProduct(local[1].CrossProduct(local[2])) < 0) {
  1490     aScale *= -1;
  1491     for (int i = 0; i < 3; i++) {
  1492       local[i] *= -1;
  1496   /* Now, get the rotations out */
  1497   aRotate = gfxQuaternion(local);
  1499   return true;
  1502 template<typename T>
  1503 T InterpolateNumerically(const T& aOne, const T& aTwo, double aCoeff)
  1505   return aOne + (aTwo - aOne) * aCoeff;
  1509 /* static */ gfx3DMatrix
  1510 nsStyleAnimation::InterpolateTransformMatrix(const gfx3DMatrix &aMatrix1,
  1511                                              const gfx3DMatrix &aMatrix2,
  1512                                              double aProgress)
  1514   // Decompose both matrices
  1516   // TODO: What do we do if one of these returns false (singular matrix)
  1518   gfxPoint3D scale1(1, 1, 1), translate1;
  1519   gfxPointH3D perspective1(0, 0, 0, 1);
  1520   gfxQuaternion rotate1;
  1521   float shear1[3] = { 0.0f, 0.0f, 0.0f};
  1523   gfxPoint3D scale2(1, 1, 1), translate2;
  1524   gfxPointH3D perspective2(0, 0, 0, 1);
  1525   gfxQuaternion rotate2;
  1526   float shear2[3] = { 0.0f, 0.0f, 0.0f};
  1528   gfxMatrix matrix2d1, matrix2d2;
  1529   if (aMatrix1.Is2D(&matrix2d1) && aMatrix2.Is2D(&matrix2d2)) {
  1530     Decompose2DMatrix(matrix2d1, scale1, shear1, rotate1, translate1);
  1531     Decompose2DMatrix(matrix2d2, scale2, shear2, rotate2, translate2);
  1532   } else {
  1533     Decompose3DMatrix(aMatrix1, scale1, shear1,
  1534                       rotate1, translate1, perspective1);
  1535     Decompose3DMatrix(aMatrix2, scale2, shear2,
  1536                       rotate2, translate2, perspective2);
  1539   // Interpolate each of the pieces
  1540   gfx3DMatrix result;
  1542   gfxPointH3D perspective =
  1543     InterpolateNumerically(perspective1, perspective2, aProgress);
  1544   result.SetTransposedVector(3, perspective);
  1546   gfxPoint3D translate =
  1547     InterpolateNumerically(translate1, translate2, aProgress);
  1548   result.Translate(translate);
  1550   gfxQuaternion q3 = rotate1.Slerp(rotate2, aProgress);
  1551   gfx3DMatrix rotate = q3.ToMatrix();
  1552   if (!rotate.IsIdentity()) {
  1553       result = rotate * result;
  1556   // TODO: Would it be better to interpolate these as angles? How do we convert back to angles?
  1557   float yzshear =
  1558     InterpolateNumerically(shear1[YZSHEAR], shear2[YZSHEAR], aProgress);
  1559   if (yzshear != 0.0) {
  1560     result.SkewYZ(yzshear);
  1563   float xzshear =
  1564     InterpolateNumerically(shear1[XZSHEAR], shear2[XZSHEAR], aProgress);
  1565   if (xzshear != 0.0) {
  1566     result.SkewXZ(xzshear);
  1569   float xyshear =
  1570     InterpolateNumerically(shear1[XYSHEAR], shear2[XYSHEAR], aProgress);
  1571   if (xyshear != 0.0) {
  1572     result.SkewXY(xyshear);
  1575   gfxPoint3D scale =
  1576     InterpolateNumerically(scale1, scale2, aProgress);
  1577   if (scale != gfxPoint3D(1.0, 1.0, 1.0)) {
  1578     result.Scale(scale.x, scale.y, scale.z);
  1581   return result;
  1584 static nsCSSValueList*
  1585 AddDifferentTransformLists(double aCoeff1, const nsCSSValueList* aList1,
  1586                            double aCoeff2, const nsCSSValueList* aList2)
  1588   nsAutoPtr<nsCSSValueList> result;
  1589   nsCSSValueList **resultTail = getter_Transfers(result);
  1591   nsRefPtr<nsCSSValue::Array> arr;
  1592   arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_interpolatematrix, resultTail);
  1594   // FIXME: We should change the other transform code to also only
  1595   // take a single progress value, as having values that don't
  1596   // sum to 1 doesn't make sense for these.
  1597   if (aList1 == aList2) {
  1598     arr->Item(1).Reset();
  1599   } else {
  1600     aList1->CloneInto(arr->Item(1).SetListValue());
  1603   aList2->CloneInto(arr->Item(2).SetListValue());
  1604   arr->Item(3).SetPercentValue(aCoeff2);
  1606   return result.forget();
  1609 static bool
  1610 TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2)
  1612   return ToPrimitive(func1) == ToPrimitive(func2);
  1615 static bool
  1616 AddFilterFunctionImpl(double aCoeff1, const nsCSSValueList* aList1,
  1617                       double aCoeff2, const nsCSSValueList* aList2,
  1618                       nsCSSValueList**& aResultTail)
  1620   // AddFilterFunction should be our only caller, and it should ensure that both
  1621   // args are non-null.
  1622   NS_ABORT_IF_FALSE(aList1, "expected filter list");
  1623   NS_ABORT_IF_FALSE(aList2, "expected filter list");
  1624   NS_ABORT_IF_FALSE(aList1->mValue.GetUnit() == eCSSUnit_Function,
  1625                     "expected function");
  1626   NS_ABORT_IF_FALSE(aList2->mValue.GetUnit() == eCSSUnit_Function,
  1627                     "expected function");
  1628   nsRefPtr<nsCSSValue::Array> a1 = aList1->mValue.GetArrayValue(),
  1629                               a2 = aList2->mValue.GetArrayValue();
  1630   nsCSSKeyword filterFunction = a1->Item(0).GetKeywordValue();
  1631   if (filterFunction != a2->Item(0).GetKeywordValue())
  1632     return false; // Can't add two filters of different types.
  1634   nsAutoPtr<nsCSSValueList> resultListEntry(new nsCSSValueList);
  1635   nsCSSValue::Array* result =
  1636     resultListEntry->mValue.InitFunction(filterFunction, 1);
  1638   // "hue-rotate" is the only filter-function that accepts negative values, and
  1639   // we don't use this "restrictions" variable in its clause below.
  1640   const uint32_t restrictions = CSS_PROPERTY_VALUE_NONNEGATIVE;
  1641   const nsCSSValue& funcArg1 = a1->Item(1);
  1642   const nsCSSValue& funcArg2 = a2->Item(1);
  1643   nsCSSValue& resultArg = result->Item(1);
  1644   float initialVal = 1.0f;
  1645   switch (filterFunction) {
  1646     case eCSSKeyword_blur: {
  1647       nsCSSUnit unit;
  1648       if (funcArg1.GetUnit() == funcArg2.GetUnit()) {
  1649         unit = funcArg1.GetUnit();
  1650       } else {
  1651         // If units differ, we'll just combine them with calc().
  1652         unit = eCSSUnit_Calc;
  1654       if (!AddCSSValuePixelPercentCalc(restrictions,
  1655                                        unit,
  1656                                        aCoeff1, funcArg1,
  1657                                        aCoeff2, funcArg2,
  1658                                        resultArg)) {
  1659         return false;
  1661       break;
  1663     case eCSSKeyword_grayscale:
  1664     case eCSSKeyword_invert:
  1665     case eCSSKeyword_sepia:
  1666       initialVal = 0.0f;
  1667     case eCSSKeyword_brightness:
  1668     case eCSSKeyword_contrast:
  1669     case eCSSKeyword_opacity:
  1670     case eCSSKeyword_saturate:
  1671       AddCSSValuePercentNumber(restrictions,
  1672                                aCoeff1, funcArg1,
  1673                                aCoeff2, funcArg2,
  1674                                resultArg,
  1675                                initialVal);
  1676       break;
  1677     case eCSSKeyword_hue_rotate:
  1678       AddCSSValueAngle(aCoeff1, funcArg1,
  1679                        aCoeff2, funcArg2,
  1680                        resultArg);
  1681       break;
  1682     case eCSSKeyword_drop_shadow: {
  1683       nsCSSValueList* resultShadow = resultArg.SetListValue();
  1684       nsAutoPtr<nsCSSValueList> shadowValue;
  1685       nsCSSValueList **shadowTail = getter_Transfers(shadowValue);
  1686       NS_ABORT_IF_FALSE(!funcArg1.GetListValue()->mNext &&
  1687                         !funcArg2.GetListValue()->mNext,
  1688                         "drop-shadow filter func doesn't support lists");
  1689       if (!AddShadowItems(aCoeff1, funcArg1.GetListValue()->mValue,
  1690                           aCoeff2, funcArg2.GetListValue()->mValue,
  1691                           shadowTail)) {
  1692         return false;
  1694       *resultShadow = *shadowValue;
  1695       break;
  1697     default:
  1698       NS_ABORT_IF_FALSE(false, "unknown filter function");
  1699       return false;
  1702   *aResultTail = resultListEntry.forget();
  1703   aResultTail = &(*aResultTail)->mNext;
  1705   return true;
  1708 static bool
  1709 AddFilterFunction(double aCoeff1, const nsCSSValueList* aList1,
  1710                   double aCoeff2, const nsCSSValueList* aList2,
  1711                   nsCSSValueList**& aResultTail)
  1713   NS_ABORT_IF_FALSE(aList1 || aList2,
  1714                     "one function list item must not be null");
  1715   // Note that one of our arguments could be null, indicating that
  1716   // it's the initial value. Rather than adding special null-handling
  1717   // logic, we just check for null values and replace them with
  1718   // 0 * the other value. That way, AddFilterFunctionImpl can assume
  1719   // its args are non-null.
  1720   if (!aList1) {
  1721     return AddFilterFunctionImpl(aCoeff2, aList2, 0, aList2, aResultTail);
  1723   if (!aList2) {
  1724     return AddFilterFunctionImpl(aCoeff1, aList1, 0, aList1, aResultTail);
  1727   return AddFilterFunctionImpl(aCoeff1, aList1, aCoeff2, aList2, aResultTail);
  1730 static nsCSSValueList*
  1731 AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
  1732                   double aCoeff2, const nsCSSValueList* aList2)
  1734   nsAutoPtr<nsCSSValueList> result;
  1735   nsCSSValueList **resultTail = getter_Transfers(result);
  1737   do {
  1738     nsRefPtr<nsCSSValue::Array> a1 = ToPrimitive(aList1->mValue.GetArrayValue()),
  1739                                 a2 = ToPrimitive(aList2->mValue.GetArrayValue());
  1740     NS_ABORT_IF_FALSE(TransformFunctionsMatch(nsStyleTransformMatrix::TransformFunctionOf(a1),
  1741                                               nsStyleTransformMatrix::TransformFunctionOf(a2)),
  1742                       "transform function mismatch");
  1743     NS_ABORT_IF_FALSE(!*resultTail,
  1744                       "resultTail isn't pointing to the tail (may leak)");
  1746     nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
  1747     nsRefPtr<nsCSSValue::Array> arr;
  1748     if (tfunc != eCSSKeyword_matrix &&
  1749         tfunc != eCSSKeyword_matrix3d &&
  1750         tfunc != eCSSKeyword_interpolatematrix &&
  1751         tfunc != eCSSKeyword_rotate3d &&
  1752         tfunc != eCSSKeyword_perspective) {
  1753       arr = nsStyleAnimation::AppendTransformFunction(tfunc, resultTail);
  1756     switch (tfunc) {
  1757       case eCSSKeyword_translate3d: {
  1758           NS_ABORT_IF_FALSE(a1->Count() == 4, "unexpected count");
  1759           NS_ABORT_IF_FALSE(a2->Count() == 4, "unexpected count");
  1760           AddTransformTranslate(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
  1761                                 arr->Item(1));
  1762           AddTransformTranslate(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2),
  1763                                 arr->Item(2));
  1764           AddTransformTranslate(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3),
  1765                                 arr->Item(3));
  1766           break;
  1768       case eCSSKeyword_scale3d: {
  1769           NS_ABORT_IF_FALSE(a1->Count() == 4, "unexpected count");
  1770           NS_ABORT_IF_FALSE(a2->Count() == 4, "unexpected count");
  1772           AddTransformScale(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
  1773                             arr->Item(1));
  1774           AddTransformScale(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2),
  1775                             arr->Item(2));
  1776           AddTransformScale(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3),
  1777                             arr->Item(3));
  1779           break;
  1781       // It would probably be nicer to animate skew in tangent space
  1782       // rather than angle space.  However, it's easy to specify
  1783       // skews with infinite tangents, and behavior changes pretty
  1784       // drastically when crossing such skews (since the direction of
  1785       // animation flips), so interop is probably more important here.
  1786       case eCSSKeyword_skew: {
  1787         NS_ABORT_IF_FALSE(a1->Count() == 2 || a1->Count() == 3,
  1788                           "unexpected count");
  1789         NS_ABORT_IF_FALSE(a2->Count() == 2 || a2->Count() == 3,
  1790                           "unexpected count");
  1792         nsCSSValue zero(0.0f, eCSSUnit_Radian);
  1793         // Add Y component of skew.
  1794         AddCSSValueAngle(aCoeff1,
  1795                          a1->Count() == 3 ? a1->Item(2) : zero,
  1796                          aCoeff2,
  1797                          a2->Count() == 3 ? a2->Item(2) : zero,
  1798                          arr->Item(2));
  1800         // Add X component of skew (which can be merged with case below
  1801         // in non-DEBUG).
  1802         AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
  1803                          arr->Item(1));
  1805         break;
  1807       case eCSSKeyword_skewx:
  1808       case eCSSKeyword_skewy:
  1809       case eCSSKeyword_rotate:
  1810       case eCSSKeyword_rotatex:
  1811       case eCSSKeyword_rotatey:
  1812       case eCSSKeyword_rotatez: {
  1813         NS_ABORT_IF_FALSE(a1->Count() == 2, "unexpected count");
  1814         NS_ABORT_IF_FALSE(a2->Count() == 2, "unexpected count");
  1816         AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
  1817                          arr->Item(1));
  1819         break;
  1821       case eCSSKeyword_matrix:
  1822       case eCSSKeyword_matrix3d:
  1823       case eCSSKeyword_interpolatematrix:
  1824       case eCSSKeyword_rotate3d:
  1825       case eCSSKeyword_perspective: {
  1826         // FIXME: If the matrix contains only numbers then we could decompose
  1827         // here.
  1829         // Construct temporary lists with only this item in them.
  1830         nsCSSValueList tempList1, tempList2;
  1831         tempList1.mValue = aList1->mValue;
  1832         tempList2.mValue = aList2->mValue;
  1834         if (aList1 == aList2) {
  1835           *resultTail =
  1836             AddDifferentTransformLists(aCoeff1, &tempList1, aCoeff2, &tempList1);
  1837         } else {
  1838           *resultTail =
  1839             AddDifferentTransformLists(aCoeff1, &tempList1, aCoeff2, &tempList2);
  1842         // Now advance resultTail to point to the new tail slot.
  1843         while (*resultTail) {
  1844           resultTail = &(*resultTail)->mNext;
  1847         break;
  1849       default:
  1850         NS_ABORT_IF_FALSE(false, "unknown transform function");
  1853     aList1 = aList1->mNext;
  1854     aList2 = aList2->mNext;
  1855   } while (aList1);
  1856   NS_ABORT_IF_FALSE(!aList2, "list length mismatch");
  1857   NS_ABORT_IF_FALSE(!*resultTail,
  1858                     "resultTail isn't pointing to the tail");
  1860   return result.forget();
  1863 bool
  1864 nsStyleAnimation::AddWeighted(nsCSSProperty aProperty,
  1865                               double aCoeff1, const Value& aValue1,
  1866                               double aCoeff2, const Value& aValue2,
  1867                               Value& aResultValue)
  1869   Unit commonUnit =
  1870     GetCommonUnit(aProperty, aValue1.GetUnit(), aValue2.GetUnit());
  1871   // Maybe need a followup method to convert the inputs into the common
  1872   // unit-type, if they don't already match it. (Or would it make sense to do
  1873   // that in GetCommonUnit? in which case maybe ConvertToCommonUnit would be
  1874   // better.)
  1876   switch (commonUnit) {
  1877     case eUnit_Null:
  1878     case eUnit_Auto:
  1879     case eUnit_None:
  1880     case eUnit_Normal:
  1881     case eUnit_UnparsedString:
  1882       return false;
  1884     case eUnit_Enumerated:
  1885       switch (aProperty) {
  1886         case eCSSProperty_font_stretch: {
  1887           // Animate just like eUnit_Integer.
  1888           int32_t result = floor(aCoeff1 * double(aValue1.GetIntValue()) +
  1889                                  aCoeff2 * double(aValue2.GetIntValue()));
  1890           if (result < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED) {
  1891             result = NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED;
  1892           } else if (result > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
  1893             result = NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED;
  1895           aResultValue.SetIntValue(result, eUnit_Enumerated);
  1896           return true;
  1898         default:
  1899           return false;
  1901     case eUnit_Visibility: {
  1902       int32_t enum1 = aValue1.GetIntValue();
  1903       int32_t enum2 = aValue2.GetIntValue();
  1904       if (enum1 == enum2) {
  1905         aResultValue.SetIntValue(enum1, eUnit_Visibility);
  1906         return true;
  1908       if ((enum1 == NS_STYLE_VISIBILITY_VISIBLE) ==
  1909           (enum2 == NS_STYLE_VISIBILITY_VISIBLE)) {
  1910         return false;
  1912       int32_t val1 = enum1 == NS_STYLE_VISIBILITY_VISIBLE;
  1913       int32_t val2 = enum2 == NS_STYLE_VISIBILITY_VISIBLE;
  1914       double interp = aCoeff1 * val1 + aCoeff2 * val2;
  1915       int32_t result = interp > 0.0 ? NS_STYLE_VISIBILITY_VISIBLE
  1916                                     : (val1 ? enum2 : enum1);
  1917       aResultValue.SetIntValue(result, eUnit_Visibility);
  1918       return true;
  1920     case eUnit_Integer: {
  1921       // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
  1922       // says we should use floor
  1923       int32_t result = floor(aCoeff1 * double(aValue1.GetIntValue()) +
  1924                              aCoeff2 * double(aValue2.GetIntValue()));
  1925       if (aProperty == eCSSProperty_font_weight) {
  1926         if (result < 100) {
  1927           result = 100;
  1928         } else if (result > 900) {
  1929           result = 900;
  1931         result -= result % 100;
  1932       } else {
  1933         result = RestrictValue(aProperty, result);
  1935       aResultValue.SetIntValue(result, eUnit_Integer);
  1936       return true;
  1938     case eUnit_Coord: {
  1939       aResultValue.SetCoordValue(RestrictValue(aProperty, NSToCoordRound(
  1940         aCoeff1 * aValue1.GetCoordValue() +
  1941         aCoeff2 * aValue2.GetCoordValue())));
  1942       return true;
  1944     case eUnit_Percent: {
  1945       aResultValue.SetPercentValue(RestrictValue(aProperty,
  1946         aCoeff1 * aValue1.GetPercentValue() +
  1947         aCoeff2 * aValue2.GetPercentValue()));
  1948       return true;
  1950     case eUnit_Float: {
  1951       // Special case for flex-grow and flex-shrink: animations are
  1952       // disallowed between 0 and other values.
  1953       if ((aProperty == eCSSProperty_flex_grow ||
  1954            aProperty == eCSSProperty_flex_shrink) &&
  1955           (aValue1.GetFloatValue() == 0.0f ||
  1956            aValue2.GetFloatValue() == 0.0f) &&
  1957           aValue1.GetFloatValue() != aValue2.GetFloatValue()) {
  1958         return false;
  1961       aResultValue.SetFloatValue(RestrictValue(aProperty,
  1962         aCoeff1 * aValue1.GetFloatValue() +
  1963         aCoeff2 * aValue2.GetFloatValue()));
  1964       return true;
  1966     case eUnit_Color: {
  1967       nscolor color1 = aValue1.GetColorValue();
  1968       nscolor color2 = aValue2.GetColorValue();
  1969       // FIXME (spec): The CSS transitions spec doesn't say whether
  1970       // colors are premultiplied, but things work better when they are,
  1971       // so use premultiplication.  Spec issue is still open per
  1972       // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
  1974       // To save some math, scale the alpha down to a 0-1 scale, but
  1975       // leave the color components on a 0-255 scale.
  1976       double A1 = NS_GET_A(color1) * (1.0 / 255.0);
  1977       double R1 = NS_GET_R(color1) * A1;
  1978       double G1 = NS_GET_G(color1) * A1;
  1979       double B1 = NS_GET_B(color1) * A1;
  1980       double A2 = NS_GET_A(color2) * (1.0 / 255.0);
  1981       double R2 = NS_GET_R(color2) * A2;
  1982       double G2 = NS_GET_G(color2) * A2;
  1983       double B2 = NS_GET_B(color2) * A2;
  1984       double Aresf = (A1 * aCoeff1 + A2 * aCoeff2);
  1985       nscolor resultColor;
  1986       if (Aresf <= 0.0) {
  1987         resultColor = NS_RGBA(0, 0, 0, 0);
  1988       } else {
  1989         if (Aresf > 1.0) {
  1990           Aresf = 1.0;
  1992         double factor = 1.0 / Aresf;
  1993         uint8_t Ares = NSToIntRound(Aresf * 255.0);
  1994         uint8_t Rres = ClampColor((R1 * aCoeff1 + R2 * aCoeff2) * factor);
  1995         uint8_t Gres = ClampColor((G1 * aCoeff1 + G2 * aCoeff2) * factor);
  1996         uint8_t Bres = ClampColor((B1 * aCoeff1 + B2 * aCoeff2) * factor);
  1997         resultColor = NS_RGBA(Rres, Gres, Bres, Ares);
  1999       aResultValue.SetColorValue(resultColor);
  2000       return true;
  2002     case eUnit_Calc: {
  2003       CalcValue v1 = ExtractCalcValue(aValue1);
  2004       CalcValue v2 = ExtractCalcValue(aValue2);
  2005       double len = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
  2006       double pct = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
  2007       bool hasPct = (aCoeff1 != 0.0 && v1.mHasPercent) ||
  2008                       (aCoeff2 != 0.0 && v2.mHasPercent);
  2009       nsCSSValue *val = new nsCSSValue();
  2010       nsCSSValue::Array *arr = nsCSSValue::Array::Create(1);
  2011       val->SetArrayValue(arr, eCSSUnit_Calc);
  2012       if (hasPct) {
  2013         nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
  2014         arr2->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
  2015         arr2->Item(1).SetPercentValue(pct);
  2016         arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
  2017       } else {
  2018         arr->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
  2020       aResultValue.SetAndAdoptCSSValueValue(val, eUnit_Calc);
  2021       return true;
  2023     case eUnit_CSSValuePair: {
  2024       const nsCSSValuePair *pair1 = aValue1.GetCSSValuePairValue();
  2025       const nsCSSValuePair *pair2 = aValue2.GetCSSValuePairValue();
  2026       nsCSSUnit unit[2];
  2027       unit[0] = GetCommonUnit(aProperty, pair1->mXValue.GetUnit(),
  2028                               pair2->mXValue.GetUnit());
  2029       unit[1] = GetCommonUnit(aProperty, pair1->mYValue.GetUnit(),
  2030                               pair2->mYValue.GetUnit());
  2031       if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
  2032           unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) {
  2033         return false;
  2036       nsAutoPtr<nsCSSValuePair> result(new nsCSSValuePair);
  2037       static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
  2038         &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
  2039       };
  2040       uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
  2041       for (uint32_t i = 0; i < 2; ++i) {
  2042         nsCSSValue nsCSSValuePair::*member = pairValues[i];
  2043         if (!AddCSSValuePixelPercentCalc(restrictions, unit[i],
  2044                                          aCoeff1, pair1->*member,
  2045                                          aCoeff2, pair2->*member,
  2046                                          result->*member) ) {
  2047           NS_ABORT_IF_FALSE(false, "unexpected unit");
  2048           return false;
  2052       aResultValue.SetAndAdoptCSSValuePairValue(result.forget(),
  2053                                                 eUnit_CSSValuePair);
  2054       return true;
  2056     case eUnit_CSSValueTriplet: {
  2057       nsCSSValueTriplet triplet1(*aValue1.GetCSSValueTripletValue());
  2058       nsCSSValueTriplet triplet2(*aValue2.GetCSSValueTripletValue());
  2060       nsCSSUnit unit[3];
  2061       unit[0] = GetCommonUnit(aProperty, triplet1.mXValue.GetUnit(),
  2062                               triplet2.mXValue.GetUnit());
  2063       unit[1] = GetCommonUnit(aProperty, triplet1.mYValue.GetUnit(),
  2064                                triplet2.mYValue.GetUnit());
  2065       unit[2] = GetCommonUnit(aProperty, triplet1.mZValue.GetUnit(),
  2066                               triplet2.mZValue.GetUnit());
  2067       if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
  2068           unit[2] == eCSSUnit_Null) {
  2069         return false;
  2072       nsAutoPtr<nsCSSValueTriplet> result(new nsCSSValueTriplet);
  2073       static nsCSSValue nsCSSValueTriplet::* const tripletValues[3] = {
  2074         &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
  2075       };
  2076       uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
  2077       for (uint32_t i = 0; i < 3; ++i) {
  2078         nsCSSValue nsCSSValueTriplet::*member = tripletValues[i];
  2079         if (!AddCSSValuePixelPercentCalc(restrictions, unit[i],
  2080                                          aCoeff1, &triplet1->*member,
  2081                                          aCoeff2, &triplet2->*member,
  2082                                          result->*member) ) {
  2083           NS_ABORT_IF_FALSE(false, "unexpected unit");
  2084           return false;
  2088       aResultValue.SetAndAdoptCSSValueTripletValue(result.forget(),
  2089                                                    eUnit_CSSValueTriplet);
  2090       return true;
  2092     case eUnit_CSSRect: {
  2093       NS_ABORT_IF_FALSE(nsCSSProps::ValueRestrictions(aProperty) == 0,
  2094                         "must add code for handling value restrictions");
  2095       const nsCSSRect *rect1 = aValue1.GetCSSRectValue();
  2096       const nsCSSRect *rect2 = aValue2.GetCSSRectValue();
  2097       if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
  2098           rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
  2099           rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
  2100           rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
  2101         // At least until we have calc()
  2102         return false;
  2105       nsAutoPtr<nsCSSRect> result(new nsCSSRect);
  2106       for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) {
  2107         nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
  2108         NS_ABORT_IF_FALSE((rect1->*member).GetUnit() ==
  2109                             (rect2->*member).GetUnit(),
  2110                           "should have returned above");
  2111         switch ((rect1->*member).GetUnit()) {
  2112           case eCSSUnit_Pixel:
  2113             AddCSSValuePixel(aCoeff1, rect1->*member, aCoeff2, rect2->*member,
  2114                              result->*member);
  2115             break;
  2116           case eCSSUnit_Auto:
  2117             if (float(aCoeff1 + aCoeff2) != 1.0f) {
  2118               // Interpolating between two auto values makes sense;
  2119               // adding in other ratios does not.
  2120               return false;
  2122             (result->*member).SetAutoValue();
  2123             break;
  2124           default:
  2125             NS_ABORT_IF_FALSE(false, "unexpected unit");
  2126             return false;
  2130       aResultValue.SetAndAdoptCSSRectValue(result.forget(), eUnit_CSSRect);
  2131       return true;
  2133     case eUnit_Dasharray: {
  2134       const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
  2135       const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
  2137       uint32_t len1 = 0, len2 = 0;
  2138       for (const nsCSSValueList *v = list1; v; v = v->mNext) {
  2139         ++len1;
  2141       for (const nsCSSValueList *v = list2; v; v = v->mNext) {
  2142         ++len2;
  2144       NS_ABORT_IF_FALSE(len1 > 0 && len2 > 0, "unexpected length");
  2145       if (list1->mValue.GetUnit() == eCSSUnit_None ||
  2146           list2->mValue.GetUnit() == eCSSUnit_None) {
  2147         // One of our values is "none".  Can't do addition with that.
  2148         NS_ABORT_IF_FALSE(
  2149           (list1->mValue.GetUnit() != eCSSUnit_None || len1 == 1) &&
  2150           (list2->mValue.GetUnit() != eCSSUnit_None || len2 == 1),
  2151           "multi-value valuelist with 'none' as first element");
  2152         return false;
  2155       nsAutoPtr<nsCSSValueList> result;
  2156       nsCSSValueList **resultTail = getter_Transfers(result);
  2157       for (uint32_t i = 0, i_end = EuclidLCM<uint32_t>(len1, len2); i != i_end; ++i) {
  2158         const nsCSSValue &v1 = list1->mValue;
  2159         const nsCSSValue &v2 = list2->mValue;
  2160         NS_ABORT_IF_FALSE(v1.GetUnit() == eCSSUnit_Number ||
  2161                           v1.GetUnit() == eCSSUnit_Percent, "unexpected");
  2162         NS_ABORT_IF_FALSE(v2.GetUnit() == eCSSUnit_Number ||
  2163                           v2.GetUnit() == eCSSUnit_Percent, "unexpected");
  2164         if (v1.GetUnit() != v2.GetUnit()) {
  2165           // Can't animate between lengths and percentages (until calc()).
  2166           return false;
  2169         nsCSSValueList *item = new nsCSSValueList;
  2170         if (!item) {
  2171           return false;
  2173         *resultTail = item;
  2174         resultTail = &item->mNext;
  2176         if (v1.GetUnit() == eCSSUnit_Number) {
  2177           AddCSSValueNumber(aCoeff1, v1, aCoeff2, v2, item->mValue,
  2178                             CSS_PROPERTY_VALUE_NONNEGATIVE);
  2179         } else {
  2180           AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, item->mValue,
  2181                              CSS_PROPERTY_VALUE_NONNEGATIVE);
  2184         list1 = list1->mNext;
  2185         if (!list1) {
  2186           list1 = aValue1.GetCSSValueListValue();
  2188         list2 = list2->mNext;
  2189         if (!list2) {
  2190           list2 = aValue2.GetCSSValueListValue();
  2194       aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
  2195                                                 eUnit_Dasharray);
  2196       return true;
  2198     case eUnit_Shadow: {
  2199       // This is implemented according to:
  2200       // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
  2201       // and the third item in the summary of:
  2202       // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
  2203       const nsCSSValueList *shadow1 = aValue1.GetCSSValueListValue();
  2204       const nsCSSValueList *shadow2 = aValue2.GetCSSValueListValue();
  2205       nsAutoPtr<nsCSSValueList> result;
  2206       nsCSSValueList **resultTail = getter_Transfers(result);
  2207       while (shadow1 && shadow2) {
  2208         if (!AddShadowItems(aCoeff1, shadow1->mValue,
  2209                             aCoeff2, shadow2->mValue,
  2210                             resultTail)) {
  2211           return false;
  2213         shadow1 = shadow1->mNext;
  2214         shadow2 = shadow2->mNext;
  2216       if (shadow1 || shadow2) {
  2217         const nsCSSValueList *longShadow;
  2218         double longCoeff;
  2219         if (shadow1) {
  2220           longShadow = shadow1;
  2221           longCoeff = aCoeff1;
  2222         } else {
  2223           longShadow = shadow2;
  2224           longCoeff = aCoeff2;
  2227         while (longShadow) {
  2228           // Passing coefficients that add to less than 1 produces the
  2229           // desired result of interpolating "0 0 0 transparent" with
  2230           // the current shadow.
  2231           if (!AddShadowItems(longCoeff, longShadow->mValue,
  2232                               0.0, longShadow->mValue,
  2233                               resultTail)) {
  2234             return false;
  2237           longShadow = longShadow->mNext;
  2240       aResultValue.SetAndAdoptCSSValueListValue(result.forget(), eUnit_Shadow);
  2241       return true;
  2244     case eUnit_Filter: {
  2245       const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
  2246       const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
  2248       nsAutoPtr<nsCSSValueList> result;
  2249       nsCSSValueList **resultTail = getter_Transfers(result);
  2250       while (list1 || list2) {
  2251         NS_ABORT_IF_FALSE(!*resultTail,
  2252           "resultTail isn't pointing to the tail (may leak)");
  2253         if ((list1 && list1->mValue.GetUnit() != eCSSUnit_Function) ||
  2254             (list2 && list2->mValue.GetUnit() != eCSSUnit_Function)) {
  2255           // If we don't have filter-functions, we must have filter-URLs, which
  2256           // we can't add or interpolate.
  2257           return false;
  2260         if (!AddFilterFunction(aCoeff1, list1, aCoeff2, list2, resultTail)) {
  2261           // filter function mismatch
  2262           return false;
  2265         // move to next list items
  2266         if (list1) {
  2267           list1 = list1->mNext;
  2269         if (list2) {
  2270           list2 = list2->mNext;
  2273       NS_ABORT_IF_FALSE(!*resultTail,
  2274                         "resultTail isn't pointing to the tail (may leak)");
  2276       aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
  2277                                                 eUnit_Filter);
  2278       return true;
  2281     case eUnit_Transform: {
  2282       const nsCSSValueList* list1 = aValue1.GetCSSValueSharedListValue()->mHead;
  2283       const nsCSSValueList* list2 = aValue2.GetCSSValueSharedListValue()->mHead;
  2285       MOZ_ASSERT(list1);
  2286       MOZ_ASSERT(list2);
  2288       // We want to avoid the matrix decomposition when we can, since
  2289       // avoiding it can produce better results both for compound
  2290       // transforms and for skew and skewY (see below).  We can do this
  2291       // in two cases:
  2292       //   (1) if one of the transforms is 'none'
  2293       //   (2) if the lists have the same length and the transform
  2294       //       functions match
  2295       nsAutoPtr<nsCSSValueList> result;
  2296       if (list1->mValue.GetUnit() == eCSSUnit_None) {
  2297         if (list2->mValue.GetUnit() == eCSSUnit_None) {
  2298           result = new nsCSSValueList;
  2299           if (result) {
  2300             result->mValue.SetNoneValue();
  2302         } else {
  2303           result = AddTransformLists(0, list2, aCoeff2, list2);
  2305       } else {
  2306         if (list2->mValue.GetUnit() == eCSSUnit_None) {
  2307           result = AddTransformLists(0, list1, aCoeff1, list1);
  2308         } else {
  2309           bool match = true;
  2312             const nsCSSValueList *item1 = list1, *item2 = list2;
  2313             do {
  2314               nsCSSKeyword func1 = nsStyleTransformMatrix::TransformFunctionOf(
  2315                                      item1->mValue.GetArrayValue());
  2316               nsCSSKeyword func2 = nsStyleTransformMatrix::TransformFunctionOf(
  2317                                      item2->mValue.GetArrayValue());
  2319               if (!TransformFunctionsMatch(func1, func2)) {
  2320                 break;
  2323               item1 = item1->mNext;
  2324               item2 = item2->mNext;
  2325             } while (item1 && item2);
  2326             if (item1 || item2) {
  2327               // Either |break| above or length mismatch.
  2328               match = false;
  2332           if (match) {
  2333             result = AddTransformLists(aCoeff1, list1, aCoeff2, list2);
  2334           } else {
  2335             result = AddDifferentTransformLists(aCoeff1, list1, aCoeff2, list2);
  2340       aResultValue.SetTransformValue(new nsCSSValueSharedList(result.forget()));
  2341       return true;
  2343     case eUnit_BackgroundPosition: {
  2344       const nsCSSValueList *position1 = aValue1.GetCSSValueListValue();
  2345       const nsCSSValueList *position2 = aValue2.GetCSSValueListValue();
  2346       nsAutoPtr<nsCSSValueList> result;
  2347       nsCSSValueList **resultTail = getter_Transfers(result);
  2348       while (position1 && position2) {
  2349         nsCSSValueList *item = new nsCSSValueList;
  2350         if (!item) {
  2351           return false;
  2353         *resultTail = item;
  2354         resultTail = &item->mNext;
  2356         nsCSSValue::Array* bgPos1 = position1->mValue.GetArrayValue();
  2357         nsCSSValue::Array* bgPos2 = position2->mValue.GetArrayValue();
  2358         nsCSSValue::Array* bgPosRes = nsCSSValue::Array::Create(4);
  2359         item->mValue.SetArrayValue(bgPosRes, eCSSUnit_Array);
  2361         uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
  2363         /* Only iterate over elements 1 and 3. The background position is
  2364          * 'uncomputed' to only those elements.
  2365          */
  2366         for (int i = 1; i < 4; i+=2) {
  2367           const nsCSSValue& v1 = bgPos1->Item(i);
  2368           const nsCSSValue& v2 = bgPos2->Item(i);
  2369           nsCSSValue& vr = bgPosRes->Item(i);
  2371           nsCSSUnit unit = GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
  2373           if (!AddCSSValuePixelPercentCalc(restrictions, unit, aCoeff1, v1,
  2374                                            aCoeff2, v2, vr) ) {
  2375             if (v1 != v2) {
  2376               return false;
  2378             vr = v1;
  2382         position1 = position1->mNext;
  2383         position2 = position2->mNext;
  2386       // Check for different lengths
  2387       if (position1 || position2) {
  2388         return false;
  2391       aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
  2392                                                 eUnit_BackgroundPosition);
  2393       return true;
  2395     case eUnit_CSSValuePairList: {
  2396       const nsCSSValuePairList *list1 = aValue1.GetCSSValuePairListValue();
  2397       const nsCSSValuePairList *list2 = aValue2.GetCSSValuePairListValue();
  2398       nsAutoPtr<nsCSSValuePairList> result;
  2399       nsCSSValuePairList **resultTail = getter_Transfers(result);
  2400       do {
  2401         nsCSSValuePairList *item = new nsCSSValuePairList;
  2402         if (!item) {
  2403           return false;
  2405         *resultTail = item;
  2406         resultTail = &item->mNext;
  2408         static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
  2409           &nsCSSValuePairList::mXValue,
  2410           &nsCSSValuePairList::mYValue,
  2411         };
  2412         uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
  2413         for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) {
  2414           const nsCSSValue &v1 = list1->*(pairListValues[i]);
  2415           const nsCSSValue &v2 = list2->*(pairListValues[i]);
  2416           nsCSSValue &vr = item->*(pairListValues[i]);
  2417           nsCSSUnit unit =
  2418             GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
  2419           if (unit == eCSSUnit_Null) {
  2420             return false;
  2422           if (!AddCSSValuePixelPercentCalc(restrictions, unit, aCoeff1, v1,
  2423                                            aCoeff2, v2, vr) ) {
  2424             if (v1 != v2) {
  2425               return false;
  2427             vr = v1;
  2430         list1 = list1->mNext;
  2431         list2 = list2->mNext;
  2432       } while (list1 && list2);
  2433       if (list1 || list2) {
  2434         // We can't interpolate lists of different lengths.
  2435         return false;
  2438       aResultValue.SetAndAdoptCSSValuePairListValue(result.forget());
  2439       return true;
  2443   NS_ABORT_IF_FALSE(false, "Can't interpolate using the given common unit");
  2444   return false;
  2447 already_AddRefed<css::StyleRule>
  2448 BuildStyleRule(nsCSSProperty aProperty,
  2449                dom::Element* aTargetElement,
  2450                const nsAString& aSpecifiedValue,
  2451                bool aUseSVGMode)
  2453   // Set up an empty CSS Declaration
  2454   nsAutoPtr<css::Declaration> declaration(new css::Declaration());
  2455   declaration->InitializeEmpty();
  2457   bool changed; // ignored, but needed as outparam for ParseProperty
  2458   nsIDocument* doc = aTargetElement->OwnerDoc();
  2459   nsCOMPtr<nsIURI> baseURI = aTargetElement->GetBaseURI();
  2460   nsCSSParser parser(doc->CSSLoader());
  2462   nsCSSProperty propertyToCheck = nsCSSProps::IsShorthand(aProperty) ?
  2463     nsCSSProps::SubpropertyEntryFor(aProperty)[0] : aProperty;
  2465   // Get a parser, parse the property, and check for CSS parsing errors.
  2466   // If any of these steps fails, we bail out and delete the declaration.
  2467   if (NS_FAILED(parser.ParseProperty(aProperty, aSpecifiedValue,
  2468                                      doc->GetDocumentURI(), baseURI,
  2469                                      aTargetElement->NodePrincipal(),
  2470                                      declaration, &changed, false,
  2471                                      aUseSVGMode)) ||
  2472       // check whether property parsed without CSS parsing errors
  2473       !declaration->HasNonImportantValueFor(propertyToCheck)) {
  2474     NS_WARNING("failure in BuildStyleRule");
  2475     return nullptr;
  2478   nsRefPtr<css::StyleRule> rule = new css::StyleRule(nullptr, declaration.forget());
  2479   return rule.forget();
  2482 inline
  2483 already_AddRefed<nsStyleContext>
  2484 LookupStyleContext(dom::Element* aElement)
  2486   nsIDocument* doc = aElement->GetCurrentDoc();
  2487   nsIPresShell* shell = doc->GetShell();
  2488   if (!shell) {
  2489     return nullptr;
  2491   return nsComputedDOMStyle::GetStyleContextForElement(aElement, nullptr, shell);
  2494 bool
  2495 nsStyleAnimation::ComputeValue(nsCSSProperty aProperty,
  2496                                dom::Element* aTargetElement,
  2497                                const nsAString& aSpecifiedValue,
  2498                                bool aUseSVGMode,
  2499                                Value& aComputedValue,
  2500                                bool* aIsContextSensitive)
  2502   NS_ABORT_IF_FALSE(aTargetElement, "null target element");
  2503   NS_ABORT_IF_FALSE(aTargetElement->GetCurrentDoc(),
  2504                     "we should only be able to actively animate nodes that "
  2505                     "are in a document");
  2507   nsCSSProperty propToParse =
  2508     nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_REPORT_OTHER_NAME)
  2509       ? nsCSSProps::OtherNameFor(aProperty) : aProperty;
  2511   // Parse specified value into a temporary css::StyleRule
  2512   nsRefPtr<css::StyleRule> styleRule =
  2513     BuildStyleRule(propToParse, aTargetElement, aSpecifiedValue, aUseSVGMode);
  2514   if (!styleRule) {
  2515     return false;
  2518   if (nsCSSProps::IsShorthand(aProperty) ||
  2519       nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) {
  2520     // Just capture the specified value
  2521     aComputedValue.SetUnparsedStringValue(nsString(aSpecifiedValue));
  2522     if (aIsContextSensitive) {
  2523       // Since we're just returning the string as-is, aComputedValue isn't going
  2524       // to change depending on the context
  2525       *aIsContextSensitive = false;
  2527     return true;
  2530   // Look up style context for our target element
  2531   nsRefPtr<nsStyleContext> styleContext = LookupStyleContext(aTargetElement);
  2532   if (!styleContext) {
  2533     return false;
  2535   nsStyleSet* styleSet = styleContext->PresContext()->StyleSet();
  2537   nsRefPtr<nsStyleContext> tmpStyleContext;
  2538   if (aIsContextSensitive) {
  2539     nsCOMArray<nsIStyleRule> ruleArray;
  2540     ruleArray.AppendObject(styleSet->InitialStyleRule());
  2541     ruleArray.AppendObject(styleRule);
  2542     styleRule->RuleMatched();
  2543     tmpStyleContext =
  2544       styleSet->ResolveStyleByAddingRules(styleContext, ruleArray);
  2545     if (!tmpStyleContext) {
  2546       return false;
  2549     // Force walk of rule tree
  2550     nsStyleStructID sid = nsCSSProps::kSIDTable[aProperty];
  2551     tmpStyleContext->StyleData(sid);
  2553     // If the rule node will have cached style data if the value is not
  2554     // context-sensitive. So if there's nothing cached, it's not context
  2555     // sensitive.
  2556     *aIsContextSensitive =
  2557       !tmpStyleContext->RuleNode()->NodeHasCachedData(sid);
  2560   // If we're not concerned whether the property is context sensitive then just
  2561   // add the rule to a new temporary style context alongside the target
  2562   // element's style context.
  2563   // Also, if we previously discovered that this property IS context-sensitive
  2564   // then we need to throw the temporary style context out since the property's
  2565   // value may have been biased by the 'initial' values supplied.
  2566   if (!aIsContextSensitive || *aIsContextSensitive) {
  2567     nsCOMArray<nsIStyleRule> ruleArray;
  2568     ruleArray.AppendObject(styleRule);
  2569     styleRule->RuleMatched();
  2570     tmpStyleContext =
  2571       styleSet->ResolveStyleByAddingRules(styleContext, ruleArray);
  2572     if (!tmpStyleContext) {
  2573       return false;
  2577   // Extract computed value of our property from the temporary style rule
  2578   return ExtractComputedValue(aProperty, tmpStyleContext, aComputedValue);
  2581 bool
  2582 nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty,
  2583                                  const Value& aComputedValue,
  2584                                  nsCSSValue& aSpecifiedValue)
  2586   switch (aComputedValue.GetUnit()) {
  2587     case eUnit_Normal:
  2588       aSpecifiedValue.SetNormalValue();
  2589       break;
  2590     case eUnit_Auto:
  2591       aSpecifiedValue.SetAutoValue();
  2592       break;
  2593     case eUnit_None:
  2594       aSpecifiedValue.SetNoneValue();
  2595       break;
  2596     case eUnit_Enumerated:
  2597     case eUnit_Visibility:
  2598       aSpecifiedValue.
  2599         SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Enumerated);
  2600       break;
  2601     case eUnit_Integer:
  2602       aSpecifiedValue.
  2603         SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Integer);
  2604       break;
  2605     case eUnit_Coord:
  2606       nscoordToCSSValue(aComputedValue.GetCoordValue(), aSpecifiedValue);
  2607       break;
  2608     case eUnit_Percent:
  2609       aSpecifiedValue.SetPercentValue(aComputedValue.GetPercentValue());
  2610       break;
  2611     case eUnit_Float:
  2612       aSpecifiedValue.
  2613         SetFloatValue(aComputedValue.GetFloatValue(), eCSSUnit_Number);
  2614       break;
  2615     case eUnit_Color:
  2616       // colors can be alone, or part of a paint server
  2617       aSpecifiedValue.SetColorValue(aComputedValue.GetColorValue());
  2618       break;
  2619     case eUnit_Calc: {
  2620       nsCSSValue *val = aComputedValue.GetCSSValueValue();
  2621       NS_ABORT_IF_FALSE(val->GetUnit() == eCSSUnit_Calc, "unexpected unit");
  2622       aSpecifiedValue = *val;
  2623       break;
  2625     case eUnit_CSSValuePair: {
  2626       // Rule node processing expects pair values to be collapsed to a
  2627       // single value if both halves would be equal, for most but not
  2628       // all properties.  At present, all animatable properties that
  2629       // use pairs do expect collapsing.
  2630       const nsCSSValuePair* pair = aComputedValue.GetCSSValuePairValue();
  2631       if (pair->mXValue == pair->mYValue) {
  2632         aSpecifiedValue = pair->mXValue;
  2633       } else {
  2634         aSpecifiedValue.SetPairValue(pair);
  2636     } break;
  2637     case eUnit_CSSValueTriplet: {
  2638       // Rule node processing expects triplet values to be collapsed to a
  2639       // single value if both halves would be equal, for most but not
  2640       // all properties.  At present, all animatable properties that
  2641       // use pairs do expect collapsing.
  2642       const nsCSSValueTriplet* triplet = aComputedValue.GetCSSValueTripletValue();
  2643       if (triplet->mXValue == triplet->mYValue && triplet->mYValue == triplet->mZValue) {
  2644         aSpecifiedValue = triplet->mXValue;
  2645       } else {
  2646         aSpecifiedValue.SetTripletValue(triplet);
  2648     } break;
  2649     case eUnit_CSSRect: {
  2650       nsCSSRect& rect = aSpecifiedValue.SetRectValue();
  2651       rect = *aComputedValue.GetCSSRectValue();
  2652     } break;
  2653     case eUnit_Dasharray:
  2654     case eUnit_Shadow:
  2655     case eUnit_Filter:
  2656     case eUnit_BackgroundPosition:
  2657       aSpecifiedValue.
  2658         SetDependentListValue(aComputedValue.GetCSSValueListValue());
  2659       break;
  2660     case eUnit_Transform:
  2661       aSpecifiedValue.
  2662         SetSharedListValue(aComputedValue.GetCSSValueSharedListValue());
  2663       break;
  2664     case eUnit_CSSValuePairList:
  2665       aSpecifiedValue.
  2666         SetDependentPairListValue(aComputedValue.GetCSSValuePairListValue());
  2667       break;
  2668     default:
  2669       return false;
  2671   return true;
  2674 bool
  2675 nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty,
  2676                                  const Value& aComputedValue,
  2677                                  nsAString& aSpecifiedValue)
  2679   aSpecifiedValue.Truncate(); // Clear outparam, if it's not already empty
  2681   if (aComputedValue.GetUnit() == eUnit_UnparsedString) {
  2682     aComputedValue.GetStringValue(aSpecifiedValue);
  2683     return true;
  2685   nsCSSValue val;
  2686   if (!nsStyleAnimation::UncomputeValue(aProperty, aComputedValue, val)) {
  2687     return false;
  2690   val.AppendToString(aProperty, aSpecifiedValue, nsCSSValue::eNormalized);
  2691   return true;
  2694 inline const void*
  2695 StyleDataAtOffset(const void* aStyleStruct, ptrdiff_t aOffset)
  2697   return reinterpret_cast<const char*>(aStyleStruct) + aOffset;
  2700 inline void*
  2701 StyleDataAtOffset(void* aStyleStruct, ptrdiff_t aOffset)
  2703   return reinterpret_cast<char*>(aStyleStruct) + aOffset;
  2706 static void
  2707 ExtractBorderColor(nsStyleContext* aStyleContext, const void* aStyleBorder,
  2708                    mozilla::css::Side aSide, nsStyleAnimation::Value& aComputedValue)
  2710   nscolor color;
  2711   bool foreground;
  2712   static_cast<const nsStyleBorder*>(aStyleBorder)->
  2713     GetBorderColor(aSide, color, foreground);
  2714   if (foreground) {
  2715     // FIXME: should add test for this
  2716     color = aStyleContext->StyleColor()->mColor;
  2718   aComputedValue.SetColorValue(color);
  2721 static bool
  2722 StyleCoordToValue(const nsStyleCoord& aCoord, nsStyleAnimation::Value& aValue)
  2724   switch (aCoord.GetUnit()) {
  2725     case eStyleUnit_Normal:
  2726       aValue.SetNormalValue();
  2727       break;
  2728     case eStyleUnit_Auto:
  2729       aValue.SetAutoValue();
  2730       break;
  2731     case eStyleUnit_None:
  2732       aValue.SetNoneValue();
  2733       break;
  2734     case eStyleUnit_Percent:
  2735       aValue.SetPercentValue(aCoord.GetPercentValue());
  2736       break;
  2737     case eStyleUnit_Factor:
  2738       aValue.SetFloatValue(aCoord.GetFactorValue());
  2739       break;
  2740     case eStyleUnit_Coord:
  2741       aValue.SetCoordValue(aCoord.GetCoordValue());
  2742       break;
  2743     case eStyleUnit_Enumerated:
  2744       aValue.SetIntValue(aCoord.GetIntValue(),
  2745                          nsStyleAnimation::eUnit_Enumerated);
  2746       break;
  2747     case eStyleUnit_Integer:
  2748       aValue.SetIntValue(aCoord.GetIntValue(),
  2749                          nsStyleAnimation::eUnit_Integer);
  2750       break;
  2751     case eStyleUnit_Calc: {
  2752       nsAutoPtr<nsCSSValue> val(new nsCSSValue);
  2753       SetCalcValue(aCoord.GetCalcValue(), *val);
  2754       aValue.SetAndAdoptCSSValueValue(val.forget(),
  2755                                       nsStyleAnimation::eUnit_Calc);
  2756       break;
  2758     default:
  2759       return false;
  2761   return true;
  2764 static bool
  2765 StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue)
  2767   switch (aCoord.GetUnit()) {
  2768     case eStyleUnit_Coord:
  2769       nscoordToCSSValue(aCoord.GetCoordValue(), aCSSValue);
  2770       break;
  2771     case eStyleUnit_Factor:
  2772       aCSSValue.SetFloatValue(aCoord.GetFactorValue(), eCSSUnit_Number);
  2773       break;
  2774     case eStyleUnit_Percent:
  2775       aCSSValue.SetPercentValue(aCoord.GetPercentValue());
  2776       break;
  2777     case eStyleUnit_Calc:
  2778       SetCalcValue(aCoord.GetCalcValue(), aCSSValue);
  2779       break;
  2780     case eStyleUnit_Degree:
  2781       aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Degree);
  2782       break;
  2783     case eStyleUnit_Grad:
  2784       aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Grad);
  2785       break;
  2786     case eStyleUnit_Radian:
  2787       aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Radian);
  2788       break;
  2789     case eStyleUnit_Turn:
  2790       aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Turn);
  2791       break;
  2792     default:
  2793       NS_ABORT_IF_FALSE(false, "unexpected unit");
  2794       return false;
  2796   return true;
  2799 /*
  2800  * Assign |aOutput = aInput|, except with any non-pixel lengths
  2801  * replaced with the equivalent in pixels, and any non-canonical calc()
  2802  * expressions replaced with canonical ones.
  2803  */
  2804 static void
  2805 SubstitutePixelValues(nsStyleContext* aStyleContext,
  2806                       const nsCSSValue& aInput, nsCSSValue& aOutput)
  2808   if (aInput.IsCalcUnit()) {
  2809     bool canStoreInRuleTree = true;
  2810     nsRuleNode::ComputedCalc c =
  2811       nsRuleNode::SpecifiedCalcToComputedCalc(aInput, aStyleContext,
  2812                                               aStyleContext->PresContext(),
  2813                                               canStoreInRuleTree);
  2814     nsStyleCoord::Calc c2;
  2815     c2.mLength = c.mLength;
  2816     c2.mPercent = c.mPercent;
  2817     c2.mHasPercent = true; // doesn't matter for transform translate
  2818     SetCalcValue(&c2, aOutput);
  2819   } else if (aInput.UnitHasArrayValue()) {
  2820     const nsCSSValue::Array *inputArray = aInput.GetArrayValue();
  2821     nsRefPtr<nsCSSValue::Array> outputArray =
  2822       nsCSSValue::Array::Create(inputArray->Count());
  2823     for (size_t i = 0, i_end = inputArray->Count(); i < i_end; ++i) {
  2824       SubstitutePixelValues(aStyleContext,
  2825                             inputArray->Item(i), outputArray->Item(i));
  2827     aOutput.SetArrayValue(outputArray, aInput.GetUnit());
  2828   } else if (aInput.IsLengthUnit() &&
  2829              aInput.GetUnit() != eCSSUnit_Pixel) {
  2830     bool canStoreInRuleTree = true;
  2831     nscoord len = nsRuleNode::CalcLength(aInput, aStyleContext,
  2832                                          aStyleContext->PresContext(),
  2833                                          canStoreInRuleTree);
  2834     aOutput.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(len),
  2835                           eCSSUnit_Pixel);
  2836   } else {
  2837     aOutput = aInput;
  2841 bool
  2842 nsStyleAnimation::ExtractComputedValue(nsCSSProperty aProperty,
  2843                                        nsStyleContext* aStyleContext,
  2844                                        Value& aComputedValue)
  2846   NS_ABORT_IF_FALSE(0 <= aProperty &&
  2847                     aProperty < eCSSProperty_COUNT_no_shorthands,
  2848                     "bad property");
  2849   const void* styleStruct =
  2850     aStyleContext->StyleData(nsCSSProps::kSIDTable[aProperty]);
  2851   ptrdiff_t ssOffset = nsCSSProps::kStyleStructOffsetTable[aProperty];
  2852   nsStyleAnimType animType = nsCSSProps::kAnimTypeTable[aProperty];
  2853   NS_ABORT_IF_FALSE(0 <= ssOffset || animType == eStyleAnimType_Custom,
  2854                     "must be dealing with animatable property");
  2855   switch (animType) {
  2856     case eStyleAnimType_Custom:
  2857       switch (aProperty) {
  2858         // For border-width, ignore the border-image business (which
  2859         // only exists until we update our implementation to the current
  2860         // spec) and use GetComputedBorder
  2862         #define BORDER_WIDTH_CASE(prop_, side_)                               \
  2863         case prop_:                                                           \
  2864           aComputedValue.SetCoordValue(                                       \
  2865             static_cast<const nsStyleBorder*>(styleStruct)->                  \
  2866               GetComputedBorder().side_);                                     \
  2867           break;
  2868         BORDER_WIDTH_CASE(eCSSProperty_border_bottom_width, bottom)
  2869         BORDER_WIDTH_CASE(eCSSProperty_border_left_width_value, left)
  2870         BORDER_WIDTH_CASE(eCSSProperty_border_right_width_value, right)
  2871         BORDER_WIDTH_CASE(eCSSProperty_border_top_width, top)
  2872         #undef BORDER_WIDTH_CASE
  2874         case eCSSProperty__moz_column_rule_width:
  2875           aComputedValue.SetCoordValue(
  2876             static_cast<const nsStyleColumn*>(styleStruct)->
  2877               GetComputedColumnRuleWidth());
  2878           break;
  2880         case eCSSProperty_border_bottom_color:
  2881           ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_BOTTOM,
  2882                              aComputedValue);
  2883           break;
  2884         case eCSSProperty_border_left_color_value:
  2885           ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_LEFT,
  2886                              aComputedValue);
  2887           break;
  2888         case eCSSProperty_border_right_color_value:
  2889           ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_RIGHT,
  2890                              aComputedValue);
  2891           break;
  2892         case eCSSProperty_border_top_color:
  2893           ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_TOP,
  2894                              aComputedValue);
  2895           break;
  2897         case eCSSProperty_outline_color: {
  2898           const nsStyleOutline *styleOutline =
  2899             static_cast<const nsStyleOutline*>(styleStruct);
  2900           nscolor color;
  2901           if (!styleOutline->GetOutlineColor(color))
  2902             color = aStyleContext->StyleColor()->mColor;
  2903           aComputedValue.SetColorValue(color);
  2904           break;
  2907         case eCSSProperty__moz_column_rule_color: {
  2908           const nsStyleColumn *styleColumn =
  2909             static_cast<const nsStyleColumn*>(styleStruct);
  2910           nscolor color;
  2911           if (styleColumn->mColumnRuleColorIsForeground) {
  2912             color = aStyleContext->StyleColor()->mColor;
  2913           } else {
  2914             color = styleColumn->mColumnRuleColor;
  2916           aComputedValue.SetColorValue(color);
  2917           break;
  2920         case eCSSProperty__moz_column_count: {
  2921           const nsStyleColumn *styleColumn =
  2922             static_cast<const nsStyleColumn*>(styleStruct);
  2923           if (styleColumn->mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO) {
  2924             aComputedValue.SetAutoValue();
  2925           } else {
  2926             aComputedValue.SetIntValue(styleColumn->mColumnCount,
  2927                                        eUnit_Integer);
  2929           break;
  2932         case eCSSProperty_order: {
  2933           const nsStylePosition *stylePosition =
  2934             static_cast<const nsStylePosition*>(styleStruct);
  2935           aComputedValue.SetIntValue(stylePosition->mOrder,
  2936                                      eUnit_Integer);
  2937           break;
  2940         case eCSSProperty_text_decoration_color: {
  2941           const nsStyleTextReset *styleTextReset =
  2942             static_cast<const nsStyleTextReset*>(styleStruct);
  2943           nscolor color;
  2944           bool isForeground;
  2945           styleTextReset->GetDecorationColor(color, isForeground);
  2946           if (isForeground) {
  2947             color = aStyleContext->StyleColor()->mColor;
  2949           aComputedValue.SetColorValue(color);
  2950           break;
  2953         case eCSSProperty_text_decoration_style: {
  2954           uint8_t decorationStyle =
  2955             static_cast<const nsStyleTextReset*>(styleStruct)->
  2956               GetDecorationStyle();
  2957           aComputedValue.SetIntValue(decorationStyle, eUnit_Enumerated);
  2958           break;
  2961         case eCSSProperty_border_spacing: {
  2962           const nsStyleTableBorder *styleTableBorder =
  2963             static_cast<const nsStyleTableBorder*>(styleStruct);
  2964           nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
  2965           if (!pair) {
  2966             return false;
  2968           nscoordToCSSValue(styleTableBorder->mBorderSpacingX, pair->mXValue);
  2969           nscoordToCSSValue(styleTableBorder->mBorderSpacingY, pair->mYValue);
  2970           aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
  2971                                                       eUnit_CSSValuePair);
  2972           break;
  2975         case eCSSProperty_transform_origin: {
  2976           const nsStyleDisplay *styleDisplay =
  2977             static_cast<const nsStyleDisplay*>(styleStruct);
  2978           nsAutoPtr<nsCSSValueTriplet> triplet(new nsCSSValueTriplet);
  2979           if (!triplet ||
  2980               !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[0],
  2981                                     triplet->mXValue) ||
  2982               !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[1],
  2983                                     triplet->mYValue) ||
  2984               !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[2],
  2985                                     triplet->mZValue)) {
  2986             return false;
  2988           aComputedValue.SetAndAdoptCSSValueTripletValue(triplet.forget(),
  2989                                                          eUnit_CSSValueTriplet);
  2990           break;
  2993         case eCSSProperty_perspective_origin: {
  2994           const nsStyleDisplay *styleDisplay =
  2995             static_cast<const nsStyleDisplay*>(styleStruct);
  2996           nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
  2997           if (!pair ||
  2998               !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[0],
  2999                                     pair->mXValue) ||
  3000               !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[1],
  3001                                     pair->mYValue)) {
  3002             return false;
  3004           aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
  3005                                                       eUnit_CSSValuePair);
  3006           break;
  3009         case eCSSProperty_stroke_dasharray: {
  3010           const nsStyleSVG *svg = static_cast<const nsStyleSVG*>(styleStruct);
  3011           NS_ABORT_IF_FALSE((svg->mStrokeDasharray != nullptr) ==
  3012                             (svg->mStrokeDasharrayLength != 0),
  3013                             "pointer/length mismatch");
  3014           nsAutoPtr<nsCSSValueList> result;
  3015           if (svg->mStrokeDasharray) {
  3016             NS_ABORT_IF_FALSE(svg->mStrokeDasharrayLength > 0,
  3017                               "non-null list should have positive length");
  3018             nsCSSValueList **resultTail = getter_Transfers(result);
  3019             for (uint32_t i = 0, i_end = svg->mStrokeDasharrayLength;
  3020                  i != i_end; ++i) {
  3021               nsCSSValueList *item = new nsCSSValueList;
  3022               if (!item) {
  3023                 return false;
  3025               *resultTail = item;
  3026               resultTail = &item->mNext;
  3028               const nsStyleCoord &coord = svg->mStrokeDasharray[i];
  3029               nsCSSValue &value = item->mValue;
  3030               switch (coord.GetUnit()) {
  3031                 case eStyleUnit_Coord:
  3032                   // Number means the same thing as length; we want to
  3033                   // animate them the same way.  Normalize both to number
  3034                   // since it has more accuracy (float vs nscoord).
  3035                   value.SetFloatValue(nsPresContext::
  3036                     AppUnitsToFloatCSSPixels(coord.GetCoordValue()),
  3037                     eCSSUnit_Number);
  3038                   break;
  3039                 case eStyleUnit_Factor:
  3040                   value.SetFloatValue(coord.GetFactorValue(),
  3041                                       eCSSUnit_Number);
  3042                   break;
  3043                 case eStyleUnit_Percent:
  3044                   value.SetPercentValue(coord.GetPercentValue());
  3045                   break;
  3046                 default:
  3047                   NS_ABORT_IF_FALSE(false, "unexpected unit");
  3048                   return false;
  3051           } else {
  3052             result = new nsCSSValueList;
  3053             if (!result) {
  3054               return false;
  3056             result->mValue.SetNoneValue();
  3058           aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
  3059                                                       eUnit_Dasharray);
  3060           break;
  3063         case eCSSProperty_font_stretch: {
  3064           int16_t stretch =
  3065             static_cast<const nsStyleFont*>(styleStruct)->mFont.stretch;
  3066           static_assert(NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED == -4 &&
  3067                         NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED == 4,
  3068                         "font stretch constants not as expected");
  3069           if (stretch < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED ||
  3070               stretch > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
  3071             return false;
  3073           aComputedValue.SetIntValue(stretch, eUnit_Enumerated);
  3074           return true;
  3077         case eCSSProperty_font_weight: {
  3078           uint16_t weight =
  3079             static_cast<const nsStyleFont*>(styleStruct)->mFont.weight;
  3080           if (weight % 100 != 0) {
  3081             return false;
  3083           aComputedValue.SetIntValue(weight, eUnit_Integer);
  3084           return true;
  3087         case eCSSProperty_image_region: {
  3088           const nsStyleList *list =
  3089             static_cast<const nsStyleList*>(styleStruct);
  3090           const nsRect &srect = list->mImageRegion;
  3091           if (srect.IsEmpty()) {
  3092             aComputedValue.SetAutoValue();
  3093             break;
  3096           nsCSSRect *vrect = new nsCSSRect;
  3097           nscoordToCSSValue(srect.x, vrect->mLeft);
  3098           nscoordToCSSValue(srect.y, vrect->mTop);
  3099           nscoordToCSSValue(srect.XMost(), vrect->mRight);
  3100           nscoordToCSSValue(srect.YMost(), vrect->mBottom);
  3101           aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
  3102           break;
  3105         case eCSSProperty_clip: {
  3106           const nsStyleDisplay *display =
  3107             static_cast<const nsStyleDisplay*>(styleStruct);
  3108           if (!(display->mClipFlags & NS_STYLE_CLIP_RECT)) {
  3109             aComputedValue.SetAutoValue();
  3110           } else {
  3111             nsCSSRect *vrect = new nsCSSRect;
  3112             const nsRect &srect = display->mClip;
  3113             if (display->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) {
  3114               vrect->mTop.SetAutoValue();
  3115             } else {
  3116               nscoordToCSSValue(srect.y, vrect->mTop);
  3118             if (display->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) {
  3119               vrect->mRight.SetAutoValue();
  3120             } else {
  3121               nscoordToCSSValue(srect.XMost(), vrect->mRight);
  3123             if (display->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) {
  3124               vrect->mBottom.SetAutoValue();
  3125             } else {
  3126               nscoordToCSSValue(srect.YMost(), vrect->mBottom);
  3128             if (display->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) {
  3129               vrect->mLeft.SetAutoValue();
  3130             } else {
  3131               nscoordToCSSValue(srect.x, vrect->mLeft);
  3133             aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
  3135           break;
  3138         case eCSSProperty_background_position: {
  3139           const nsStyleBackground *bg =
  3140             static_cast<const nsStyleBackground*>(styleStruct);
  3141           nsAutoPtr<nsCSSValueList> result;
  3142           nsCSSValueList **resultTail = getter_Transfers(result);
  3143           NS_ABORT_IF_FALSE(bg->mPositionCount > 0, "unexpected count");
  3144           for (uint32_t i = 0, i_end = bg->mPositionCount; i != i_end; ++i) {
  3145             nsCSSValueList *item = new nsCSSValueList;
  3146             *resultTail = item;
  3147             resultTail = &item->mNext;
  3148             nsRefPtr<nsCSSValue::Array> bgArray = nsCSSValue::Array::Create(4);
  3149             item->mValue.SetArrayValue(bgArray.get(), eCSSUnit_Array);
  3151             const nsStyleBackground::Position &pos = bg->mLayers[i].mPosition;
  3152             // XXXbz is there a good reason we can't just
  3153             // SetCalcValue(&pos.mXPosition, item->mXValue) here?
  3154             nsCSSValue &xValue = bgArray->Item(1),
  3155                        &yValue = bgArray->Item(3);
  3156             if (!pos.mXPosition.mHasPercent) {
  3157               NS_ABORT_IF_FALSE(pos.mXPosition.mPercent == 0.0f,
  3158                                 "Shouldn't have mPercent!");
  3159               nscoordToCSSValue(pos.mXPosition.mLength, xValue);
  3160             } else if (pos.mXPosition.mLength == 0) {
  3161               xValue.SetPercentValue(pos.mXPosition.mPercent);
  3162             } else {
  3163               SetCalcValue(&pos.mXPosition, xValue);
  3166             if (!pos.mYPosition.mHasPercent) {
  3167               NS_ABORT_IF_FALSE(pos.mYPosition.mPercent == 0.0f,
  3168                                 "Shouldn't have mPercent!");
  3169               nscoordToCSSValue(pos.mYPosition.mLength, yValue);
  3170             } else if (pos.mYPosition.mLength == 0) {
  3171               yValue.SetPercentValue(pos.mYPosition.mPercent);
  3172             } else {
  3173               SetCalcValue(&pos.mYPosition, yValue);
  3177           aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
  3178                                                       eUnit_BackgroundPosition);
  3179           break;
  3182         case eCSSProperty_background_size: {
  3183           const nsStyleBackground *bg =
  3184             static_cast<const nsStyleBackground*>(styleStruct);
  3185           nsAutoPtr<nsCSSValuePairList> result;
  3186           nsCSSValuePairList **resultTail = getter_Transfers(result);
  3187           NS_ABORT_IF_FALSE(bg->mSizeCount > 0, "unexpected count");
  3188           for (uint32_t i = 0, i_end = bg->mSizeCount; i != i_end; ++i) {
  3189             nsCSSValuePairList *item = new nsCSSValuePairList;
  3190             *resultTail = item;
  3191             resultTail = &item->mNext;
  3193             const nsStyleBackground::Size &size = bg->mLayers[i].mSize;
  3194             switch (size.mWidthType) {
  3195               case nsStyleBackground::Size::eContain:
  3196               case nsStyleBackground::Size::eCover:
  3197                 item->mXValue.SetIntValue(size.mWidthType,
  3198                                           eCSSUnit_Enumerated);
  3199                 break;
  3200               case nsStyleBackground::Size::eAuto:
  3201                 item->mXValue.SetAutoValue();
  3202                 break;
  3203               case nsStyleBackground::Size::eLengthPercentage:
  3204                 // XXXbz is there a good reason we can't just
  3205                 // SetCalcValue(&size.mWidth, item->mXValue) here?
  3206                 if (!size.mWidth.mHasPercent &&
  3207                     // negative values must have come from calc()
  3208                     size.mWidth.mLength >= 0) {
  3209                   NS_ABORT_IF_FALSE(size.mWidth.mPercent == 0.0f,
  3210                                     "Shouldn't have mPercent");
  3211                   nscoordToCSSValue(size.mWidth.mLength, item->mXValue);
  3212                 } else if (size.mWidth.mLength == 0 &&
  3213                            // negative values must have come from calc()
  3214                            size.mWidth.mPercent >= 0.0f) {
  3215                   item->mXValue.SetPercentValue(size.mWidth.mPercent);
  3216                 } else {
  3217                   SetCalcValue(&size.mWidth, item->mXValue);
  3219                 break;
  3222             switch (size.mHeightType) {
  3223               case nsStyleBackground::Size::eContain:
  3224               case nsStyleBackground::Size::eCover:
  3225                 // leave it null
  3226                 break;
  3227               case nsStyleBackground::Size::eAuto:
  3228                 item->mYValue.SetAutoValue();
  3229                 break;
  3230               case nsStyleBackground::Size::eLengthPercentage:
  3231                 // XXXbz is there a good reason we can't just
  3232                 // SetCalcValue(&size.mHeight, item->mYValue) here?
  3233                 if (!size.mHeight.mHasPercent &&
  3234                     // negative values must have come from calc()
  3235                     size.mHeight.mLength >= 0) {
  3236                   NS_ABORT_IF_FALSE(size.mHeight.mPercent == 0.0f,
  3237                                     "Shouldn't have mPercent");
  3238                   nscoordToCSSValue(size.mHeight.mLength, item->mYValue);
  3239                 } else if (size.mHeight.mLength == 0 &&
  3240                            // negative values must have come from calc()
  3241                            size.mHeight.mPercent >= 0.0f) {
  3242                   item->mYValue.SetPercentValue(size.mHeight.mPercent);
  3243                 } else {
  3244                   SetCalcValue(&size.mHeight, item->mYValue);
  3246                 break;
  3250           aComputedValue.SetAndAdoptCSSValuePairListValue(result.forget());
  3251           break;
  3254         case eCSSProperty_filter: {
  3255           const nsStyleSVGReset *svgReset =
  3256             static_cast<const nsStyleSVGReset*>(styleStruct);
  3257           const nsTArray<nsStyleFilter>& filters = svgReset->mFilters;
  3258           nsAutoPtr<nsCSSValueList> result;
  3259           nsCSSValueList **resultTail = getter_Transfers(result);
  3260           for (uint32_t i = 0; i < filters.Length(); ++i) {
  3261             nsCSSValueList *item = new nsCSSValueList;
  3262             *resultTail = item;
  3263             resultTail = &item->mNext;
  3264             const nsStyleFilter& filter = filters[i];
  3265             int32_t type = filter.GetType();
  3266             if (type == NS_STYLE_FILTER_URL) {
  3267               nsIDocument* doc = aStyleContext->PresContext()->Document();
  3268               nsRefPtr<nsStringBuffer> uriAsStringBuffer =
  3269                 GetURIAsUtf16StringBuffer(filter.GetURL());
  3270               nsRefPtr<mozilla::css::URLValue> url =
  3271                 new mozilla::css::URLValue(filter.GetURL(),
  3272                                            uriAsStringBuffer,
  3273                                            doc->GetDocumentURI(),
  3274                                            doc->NodePrincipal());
  3275               item->mValue.SetURLValue(url);
  3276             } else {
  3277               nsCSSKeyword functionName =
  3278                 nsCSSProps::ValueToKeywordEnum(type,
  3279                   nsCSSProps::kFilterFunctionKTable);
  3280               nsCSSValue::Array* filterArray =
  3281                 item->mValue.InitFunction(functionName, 1);
  3282               if (type >= NS_STYLE_FILTER_BLUR && type <= NS_STYLE_FILTER_HUE_ROTATE) {
  3283                 if (!StyleCoordToCSSValue(
  3284                       filter.GetFilterParameter(),
  3285                       filterArray->Item(1))) {
  3286                   return false;
  3288               } else if (type == NS_STYLE_FILTER_DROP_SHADOW) {
  3289                 nsCSSValueList* shadowResult = filterArray->Item(1).SetListValue();
  3290                 nsAutoPtr<nsCSSValueList> tmpShadowValue;
  3291                 nsCSSValueList **tmpShadowResultTail = getter_Transfers(tmpShadowValue);
  3292                 nsCSSShadowArray* shadowArray = filter.GetDropShadow();
  3293                 NS_ABORT_IF_FALSE(shadowArray->Length() == 1,
  3294                                   "expected exactly one shadow");
  3295                 AppendCSSShadowValue(shadowArray->ShadowAt(0), tmpShadowResultTail);
  3296                 *shadowResult = *tmpShadowValue;
  3297               } else {
  3298                 // We checked all possible nsStyleFilter types but
  3299                 // NS_STYLE_FILTER_NULL before. We should never enter this path.
  3300                 NS_NOTREACHED("no other filter functions defined");
  3301                 return false;
  3306           aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
  3307                                                       eUnit_Filter);
  3308           break;
  3311         case eCSSProperty_transform: {
  3312           const nsStyleDisplay *display =
  3313             static_cast<const nsStyleDisplay*>(styleStruct);
  3314           nsAutoPtr<nsCSSValueList> result;
  3315           if (display->mSpecifiedTransform) {
  3316             // Clone, and convert all lengths (not percents) to pixels.
  3317             nsCSSValueList **resultTail = getter_Transfers(result);
  3318             for (const nsCSSValueList *l = display->mSpecifiedTransform->mHead;
  3319                  l; l = l->mNext) {
  3320               nsCSSValueList *clone = new nsCSSValueList;
  3321               *resultTail = clone;
  3322               resultTail = &clone->mNext;
  3324               SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue);
  3326           } else {
  3327             result = new nsCSSValueList();
  3328             result->mValue.SetNoneValue();
  3331           aComputedValue.SetTransformValue(
  3332               new nsCSSValueSharedList(result.forget()));
  3333           break;
  3336         default:
  3337           NS_ABORT_IF_FALSE(false, "missing property implementation");
  3338           return false;
  3339       };
  3340       return true;
  3341     case eStyleAnimType_Coord:
  3342       return StyleCoordToValue(*static_cast<const nsStyleCoord*>(
  3343         StyleDataAtOffset(styleStruct, ssOffset)), aComputedValue);
  3344     case eStyleAnimType_Sides_Top:
  3345     case eStyleAnimType_Sides_Right:
  3346     case eStyleAnimType_Sides_Bottom:
  3347     case eStyleAnimType_Sides_Left: {
  3348       static_assert(
  3349        NS_SIDE_TOP    == eStyleAnimType_Sides_Top   -eStyleAnimType_Sides_Top &&
  3350        NS_SIDE_RIGHT  == eStyleAnimType_Sides_Right -eStyleAnimType_Sides_Top &&
  3351        NS_SIDE_BOTTOM == eStyleAnimType_Sides_Bottom-eStyleAnimType_Sides_Top &&
  3352        NS_SIDE_LEFT   == eStyleAnimType_Sides_Left  -eStyleAnimType_Sides_Top,
  3353        "box side constants out of sync with animation side constants");
  3355       const nsStyleCoord &coord = static_cast<const nsStyleSides*>(
  3356         StyleDataAtOffset(styleStruct, ssOffset))->
  3357           Get(mozilla::css::Side(animType - eStyleAnimType_Sides_Top));
  3358       return StyleCoordToValue(coord, aComputedValue);
  3360     case eStyleAnimType_Corner_TopLeft:
  3361     case eStyleAnimType_Corner_TopRight:
  3362     case eStyleAnimType_Corner_BottomRight:
  3363     case eStyleAnimType_Corner_BottomLeft: {
  3364       static_assert(
  3365        NS_CORNER_TOP_LEFT     == eStyleAnimType_Corner_TopLeft -
  3366                                  eStyleAnimType_Corner_TopLeft        &&
  3367        NS_CORNER_TOP_RIGHT    == eStyleAnimType_Corner_TopRight -
  3368                                  eStyleAnimType_Corner_TopLeft        &&
  3369        NS_CORNER_BOTTOM_RIGHT == eStyleAnimType_Corner_BottomRight -
  3370                                  eStyleAnimType_Corner_TopLeft        &&
  3371        NS_CORNER_BOTTOM_LEFT  == eStyleAnimType_Corner_BottomLeft -
  3372                                  eStyleAnimType_Corner_TopLeft,
  3373        "box corner constants out of sync with animation corner constants");
  3375       const nsStyleCorners *corners = static_cast<const nsStyleCorners*>(
  3376         StyleDataAtOffset(styleStruct, ssOffset));
  3377       uint8_t fullCorner = animType - eStyleAnimType_Corner_TopLeft;
  3378       const nsStyleCoord &horiz =
  3379         corners->Get(NS_FULL_TO_HALF_CORNER(fullCorner, false));
  3380       const nsStyleCoord &vert =
  3381         corners->Get(NS_FULL_TO_HALF_CORNER(fullCorner, true));
  3382       nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
  3383       if (!pair ||
  3384           !StyleCoordToCSSValue(horiz, pair->mXValue) ||
  3385           !StyleCoordToCSSValue(vert, pair->mYValue)) {
  3386         return false;
  3388       aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
  3389                                                   eUnit_CSSValuePair);
  3390       return true;
  3392     case eStyleAnimType_nscoord:
  3393       aComputedValue.SetCoordValue(*static_cast<const nscoord*>(
  3394         StyleDataAtOffset(styleStruct, ssOffset)));
  3395       return true;
  3396     case eStyleAnimType_EnumU8:
  3397       aComputedValue.SetIntValue(*static_cast<const uint8_t*>(
  3398         StyleDataAtOffset(styleStruct, ssOffset)), eUnit_Enumerated);
  3399       return true;
  3400     case eStyleAnimType_float:
  3401       aComputedValue.SetFloatValue(*static_cast<const float*>(
  3402         StyleDataAtOffset(styleStruct, ssOffset)));
  3403       if (aProperty == eCSSProperty_font_size_adjust &&
  3404           aComputedValue.GetFloatValue() == 0.0f) {
  3405         // In nsStyleFont, we set mFont.sizeAdjust to 0 to represent
  3406         // font-size-adjust: none.  Here, we have to treat this as a keyword
  3407         // instead of a float value, to make sure we don't end up doing
  3408         // interpolation with it.
  3409         aComputedValue.SetNoneValue();
  3411       return true;
  3412     case eStyleAnimType_Color:
  3413       aComputedValue.SetColorValue(*static_cast<const nscolor*>(
  3414         StyleDataAtOffset(styleStruct, ssOffset)));
  3415       return true;
  3416     case eStyleAnimType_PaintServer: {
  3417       const nsStyleSVGPaint &paint = *static_cast<const nsStyleSVGPaint*>(
  3418         StyleDataAtOffset(styleStruct, ssOffset));
  3419       if (paint.mType == eStyleSVGPaintType_Color) {
  3420         aComputedValue.SetColorValue(paint.mPaint.mColor);
  3421         return true;
  3423       if (paint.mType == eStyleSVGPaintType_Server) {
  3424         if (!paint.mPaint.mPaintServer) {
  3425           NS_WARNING("Null paint server");
  3426           return false;
  3428         nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
  3429         nsRefPtr<nsStringBuffer> uriAsStringBuffer =
  3430           GetURIAsUtf16StringBuffer(paint.mPaint.mPaintServer);
  3431         NS_ENSURE_TRUE(!!uriAsStringBuffer, false);
  3432         nsIDocument* doc = aStyleContext->PresContext()->Document();
  3433         nsRefPtr<mozilla::css::URLValue> url =
  3434           new mozilla::css::URLValue(paint.mPaint.mPaintServer,
  3435                                      uriAsStringBuffer,
  3436                                      doc->GetDocumentURI(),
  3437                                      doc->NodePrincipal());
  3438         pair->mXValue.SetURLValue(url);
  3439         pair->mYValue.SetColorValue(paint.mFallbackColor);
  3440         aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
  3441                                                     eUnit_CSSValuePair);
  3442         return true;
  3444       if (paint.mType == eStyleSVGPaintType_ContextFill ||
  3445           paint.mType == eStyleSVGPaintType_ContextStroke) {
  3446         nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
  3447         pair->mXValue.SetIntValue(paint.mType == eStyleSVGPaintType_ContextFill ?
  3448                                   NS_COLOR_CONTEXT_FILL : NS_COLOR_CONTEXT_STROKE,
  3449                                   eCSSUnit_Enumerated);
  3450         pair->mYValue.SetColorValue(paint.mFallbackColor);
  3451         aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
  3452                                                     eUnit_CSSValuePair);
  3453         return true;
  3455       NS_ABORT_IF_FALSE(paint.mType == eStyleSVGPaintType_None,
  3456           "Unexpected SVG paint type");
  3457       aComputedValue.SetNoneValue();
  3458       return true;
  3460     case eStyleAnimType_Shadow: {
  3461       const nsCSSShadowArray *shadowArray =
  3462         *static_cast<const nsRefPtr<nsCSSShadowArray>*>(
  3463           StyleDataAtOffset(styleStruct, ssOffset));
  3464       if (!shadowArray) {
  3465         aComputedValue.SetAndAdoptCSSValueListValue(nullptr, eUnit_Shadow);
  3466         return true;
  3468       nsAutoPtr<nsCSSValueList> result;
  3469       nsCSSValueList **resultTail = getter_Transfers(result);
  3470       for (uint32_t i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
  3471         AppendCSSShadowValue(shadowArray->ShadowAt(i), resultTail);
  3473       aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
  3474                                                   eUnit_Shadow);
  3475       return true;
  3477     case eStyleAnimType_None:
  3478       NS_NOTREACHED("shouldn't use on non-animatable properties");
  3480   return false;
  3483 nsStyleAnimation::Value::Value(int32_t aInt, Unit aUnit,
  3484                                IntegerConstructorType)
  3486   NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
  3487   mUnit = aUnit;
  3488   mValue.mInt = aInt;
  3491 nsStyleAnimation::Value::Value(nscoord aLength, CoordConstructorType)
  3493   mUnit = eUnit_Coord;
  3494   mValue.mCoord = aLength;
  3497 nsStyleAnimation::Value::Value(float aPercent, PercentConstructorType)
  3499   mUnit = eUnit_Percent;
  3500   mValue.mFloat = aPercent;
  3501   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
  3504 nsStyleAnimation::Value::Value(float aFloat, FloatConstructorType)
  3506   mUnit = eUnit_Float;
  3507   mValue.mFloat = aFloat;
  3508   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
  3511 nsStyleAnimation::Value::Value(nscolor aColor, ColorConstructorType)
  3513   mUnit = eUnit_Color;
  3514   mValue.mColor = aColor;
  3517 nsStyleAnimation::Value&
  3518 nsStyleAnimation::Value::operator=(const Value& aOther)
  3520   FreeValue();
  3522   mUnit = aOther.mUnit;
  3523   switch (mUnit) {
  3524     case eUnit_Null:
  3525     case eUnit_Normal:
  3526     case eUnit_Auto:
  3527     case eUnit_None:
  3528       break;
  3529     case eUnit_Enumerated:
  3530     case eUnit_Visibility:
  3531     case eUnit_Integer:
  3532       mValue.mInt = aOther.mValue.mInt;
  3533       break;
  3534     case eUnit_Coord:
  3535       mValue.mCoord = aOther.mValue.mCoord;
  3536       break;
  3537     case eUnit_Percent:
  3538     case eUnit_Float:
  3539       mValue.mFloat = aOther.mValue.mFloat;
  3540       MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
  3541       break;
  3542     case eUnit_Color:
  3543       mValue.mColor = aOther.mValue.mColor;
  3544       break;
  3545     case eUnit_Calc:
  3546       NS_ABORT_IF_FALSE(aOther.mValue.mCSSValue, "values may not be null");
  3547       mValue.mCSSValue = new nsCSSValue(*aOther.mValue.mCSSValue);
  3548       if (!mValue.mCSSValue) {
  3549         mUnit = eUnit_Null;
  3551       break;
  3552     case eUnit_CSSValuePair:
  3553       NS_ABORT_IF_FALSE(aOther.mValue.mCSSValuePair,
  3554                         "value pairs may not be null");
  3555       mValue.mCSSValuePair = new nsCSSValuePair(*aOther.mValue.mCSSValuePair);
  3556       if (!mValue.mCSSValuePair) {
  3557         mUnit = eUnit_Null;
  3559       break;
  3560     case eUnit_CSSValueTriplet:
  3561       NS_ABORT_IF_FALSE(aOther.mValue.mCSSValueTriplet,
  3562                         "value triplets may not be null");
  3563       mValue.mCSSValueTriplet = new nsCSSValueTriplet(*aOther.mValue.mCSSValueTriplet);
  3564       if (!mValue.mCSSValueTriplet) {
  3565         mUnit = eUnit_Null;
  3567       break;
  3568     case eUnit_CSSRect:
  3569       NS_ABORT_IF_FALSE(aOther.mValue.mCSSRect, "rects may not be null");
  3570       mValue.mCSSRect = new nsCSSRect(*aOther.mValue.mCSSRect);
  3571       if (!mValue.mCSSRect) {
  3572         mUnit = eUnit_Null;
  3574       break;
  3575     case eUnit_Filter:
  3576     case eUnit_Dasharray:
  3577     case eUnit_Shadow:
  3578     case eUnit_BackgroundPosition:
  3579       NS_ABORT_IF_FALSE(mUnit == eUnit_Shadow || mUnit == eUnit_Filter ||
  3580                         aOther.mValue.mCSSValueList,
  3581                         "value lists other than shadows and filters may not be null");
  3582       if (aOther.mValue.mCSSValueList) {
  3583         mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
  3584         if (!mValue.mCSSValueList) {
  3585           mUnit = eUnit_Null;
  3587       } else {
  3588         mValue.mCSSValueList = nullptr;
  3590       break;
  3591     case eUnit_Transform:
  3592       mValue.mCSSValueSharedList = aOther.mValue.mCSSValueSharedList;
  3593       mValue.mCSSValueSharedList->AddRef();
  3594       break;
  3595     case eUnit_CSSValuePairList:
  3596       NS_ABORT_IF_FALSE(aOther.mValue.mCSSValuePairList,
  3597                         "value pair lists may not be null");
  3598       mValue.mCSSValuePairList = aOther.mValue.mCSSValuePairList->Clone();
  3599       if (!mValue.mCSSValuePairList) {
  3600         mUnit = eUnit_Null;
  3602       break;
  3603     case eUnit_UnparsedString:
  3604       NS_ABORT_IF_FALSE(aOther.mValue.mString, "expecting non-null string");
  3605       mValue.mString = aOther.mValue.mString;
  3606       mValue.mString->AddRef();
  3607       break;
  3610   return *this;
  3613 void
  3614 nsStyleAnimation::Value::SetNormalValue()
  3616   FreeValue();
  3617   mUnit = eUnit_Normal;
  3620 void
  3621 nsStyleAnimation::Value::SetAutoValue()
  3623   FreeValue();
  3624   mUnit = eUnit_Auto;
  3627 void
  3628 nsStyleAnimation::Value::SetNoneValue()
  3630   FreeValue();
  3631   mUnit = eUnit_None;
  3634 void
  3635 nsStyleAnimation::Value::SetIntValue(int32_t aInt, Unit aUnit)
  3637   NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
  3638   FreeValue();
  3639   mUnit = aUnit;
  3640   mValue.mInt = aInt;
  3643 void
  3644 nsStyleAnimation::Value::SetCoordValue(nscoord aLength)
  3646   FreeValue();
  3647   mUnit = eUnit_Coord;
  3648   mValue.mCoord = aLength;
  3651 void
  3652 nsStyleAnimation::Value::SetPercentValue(float aPercent)
  3654   FreeValue();
  3655   mUnit = eUnit_Percent;
  3656   mValue.mFloat = aPercent;
  3657   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
  3660 void
  3661 nsStyleAnimation::Value::SetFloatValue(float aFloat)
  3663   FreeValue();
  3664   mUnit = eUnit_Float;
  3665   mValue.mFloat = aFloat;
  3666   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
  3669 void
  3670 nsStyleAnimation::Value::SetColorValue(nscolor aColor)
  3672   FreeValue();
  3673   mUnit = eUnit_Color;
  3674   mValue.mColor = aColor;
  3677 void
  3678 nsStyleAnimation::Value::SetUnparsedStringValue(const nsString& aString)
  3680   FreeValue();
  3681   mUnit = eUnit_UnparsedString;
  3682   mValue.mString = nsCSSValue::BufferFromString(aString).take();
  3685 void
  3686 nsStyleAnimation::Value::SetAndAdoptCSSValueValue(nsCSSValue *aValue,
  3687                                                   Unit aUnit)
  3689   FreeValue();
  3690   NS_ABORT_IF_FALSE(IsCSSValueUnit(aUnit), "bad unit");
  3691   NS_ABORT_IF_FALSE(aValue != nullptr, "values may not be null");
  3692   mUnit = aUnit;
  3693   mValue.mCSSValue = aValue; // take ownership
  3696 void
  3697 nsStyleAnimation::Value::SetAndAdoptCSSValuePairValue(
  3698                            nsCSSValuePair *aValuePair, Unit aUnit)
  3700   FreeValue();
  3701   NS_ABORT_IF_FALSE(IsCSSValuePairUnit(aUnit), "bad unit");
  3702   NS_ABORT_IF_FALSE(aValuePair != nullptr, "value pairs may not be null");
  3703   mUnit = aUnit;
  3704   mValue.mCSSValuePair = aValuePair; // take ownership
  3707 void
  3708 nsStyleAnimation::Value::SetAndAdoptCSSValueTripletValue(
  3709                            nsCSSValueTriplet *aValueTriplet, Unit aUnit)
  3711     FreeValue();
  3712     NS_ABORT_IF_FALSE(IsCSSValueTripletUnit(aUnit), "bad unit");
  3713     NS_ABORT_IF_FALSE(aValueTriplet != nullptr, "value pairs may not be null");
  3714     mUnit = aUnit;
  3715     mValue.mCSSValueTriplet = aValueTriplet; // take ownership
  3718 void
  3719 nsStyleAnimation::Value::SetAndAdoptCSSRectValue(nsCSSRect *aRect, Unit aUnit)
  3721   FreeValue();
  3722   NS_ABORT_IF_FALSE(IsCSSRectUnit(aUnit), "bad unit");
  3723   NS_ABORT_IF_FALSE(aRect != nullptr, "value pairs may not be null");
  3724   mUnit = aUnit;
  3725   mValue.mCSSRect = aRect; // take ownership
  3728 void
  3729 nsStyleAnimation::Value::SetAndAdoptCSSValueListValue(
  3730                            nsCSSValueList *aValueList, Unit aUnit)
  3732   FreeValue();
  3733   NS_ABORT_IF_FALSE(IsCSSValueListUnit(aUnit), "bad unit");
  3734   NS_ABORT_IF_FALSE(aUnit == eUnit_Shadow || aUnit == eUnit_Filter ||
  3735                     aValueList != nullptr,
  3736                     "value lists other than shadows and filters may not be null");
  3737   mUnit = aUnit;
  3738   mValue.mCSSValueList = aValueList; // take ownership
  3741 void
  3742 nsStyleAnimation::Value::SetTransformValue(nsCSSValueSharedList* aList)
  3744   FreeValue();
  3745   mUnit = eUnit_Transform;
  3746   mValue.mCSSValueSharedList = aList;
  3747   mValue.mCSSValueSharedList->AddRef();
  3750 void
  3751 nsStyleAnimation::Value::SetAndAdoptCSSValuePairListValue(
  3752                            nsCSSValuePairList *aValuePairList)
  3754   FreeValue();
  3755   NS_ABORT_IF_FALSE(aValuePairList, "may not be null");
  3756   mUnit = eUnit_CSSValuePairList;
  3757   mValue.mCSSValuePairList = aValuePairList; // take ownership
  3760 void
  3761 nsStyleAnimation::Value::FreeValue()
  3763   if (IsCSSValueUnit(mUnit)) {
  3764     delete mValue.mCSSValue;
  3765   } else if (IsCSSValueListUnit(mUnit)) {
  3766     delete mValue.mCSSValueList;
  3767   } else if (IsCSSValueSharedListValue(mUnit)) {
  3768     mValue.mCSSValueSharedList->Release();
  3769   } else if (IsCSSValuePairUnit(mUnit)) {
  3770     delete mValue.mCSSValuePair;
  3771   } else if (IsCSSValueTripletUnit(mUnit)) {
  3772     delete mValue.mCSSValueTriplet;
  3773   } else if (IsCSSRectUnit(mUnit)) {
  3774     delete mValue.mCSSRect;
  3775   } else if (IsCSSValuePairListUnit(mUnit)) {
  3776     delete mValue.mCSSValuePairList;
  3777   } else if (IsStringUnit(mUnit)) {
  3778     NS_ABORT_IF_FALSE(mValue.mString, "expecting non-null string");
  3779     mValue.mString->Release();
  3783 bool
  3784 nsStyleAnimation::Value::operator==(const Value& aOther) const
  3786   if (mUnit != aOther.mUnit) {
  3787     return false;
  3790   switch (mUnit) {
  3791     case eUnit_Null:
  3792     case eUnit_Normal:
  3793     case eUnit_Auto:
  3794     case eUnit_None:
  3795       return true;
  3796     case eUnit_Enumerated:
  3797     case eUnit_Visibility:
  3798     case eUnit_Integer:
  3799       return mValue.mInt == aOther.mValue.mInt;
  3800     case eUnit_Coord:
  3801       return mValue.mCoord == aOther.mValue.mCoord;
  3802     case eUnit_Percent:
  3803     case eUnit_Float:
  3804       return mValue.mFloat == aOther.mValue.mFloat;
  3805     case eUnit_Color:
  3806       return mValue.mColor == aOther.mValue.mColor;
  3807     case eUnit_Calc:
  3808       return *mValue.mCSSValue == *aOther.mValue.mCSSValue;
  3809     case eUnit_CSSValuePair:
  3810       return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
  3811     case eUnit_CSSValueTriplet:
  3812       return *mValue.mCSSValueTriplet == *aOther.mValue.mCSSValueTriplet;
  3813     case eUnit_CSSRect:
  3814       return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
  3815     case eUnit_Dasharray:
  3816     case eUnit_Filter:
  3817     case eUnit_Shadow:
  3818     case eUnit_BackgroundPosition:
  3819       return *mValue.mCSSValueList == *aOther.mValue.mCSSValueList;
  3820     case eUnit_Transform:
  3821       return *mValue.mCSSValueSharedList == *aOther.mValue.mCSSValueSharedList;
  3822     case eUnit_CSSValuePairList:
  3823       return *mValue.mCSSValuePairList == *aOther.mValue.mCSSValuePairList;
  3824     case eUnit_UnparsedString:
  3825       return (NS_strcmp(GetStringBufferValue(),
  3826                         aOther.GetStringBufferValue()) == 0);
  3829   NS_NOTREACHED("incomplete case");
  3830   return false;

mercurial