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