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