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