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