Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / layout / style / nsStyleAnimation.cpp
blob30ce09e1f78a2434893f286774f567e4880e8549
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is nsStyleAnimation.
17 * The Initial Developer of the Original Code is
18 * The Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2009
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Daniel Holbert <dholbert@mozilla.com>, Mozilla Corporation
24 * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 /* Utilities for animation of computed style values */
42 #include "nsStyleAnimation.h"
43 #include "nsCOMArray.h"
44 #include "nsIStyleRule.h"
45 #include "nsICSSStyleRule.h"
46 #include "nsString.h"
47 #include "nsStyleContext.h"
48 #include "nsStyleSet.h"
49 #include "nsComputedDOMStyle.h"
50 #include "nsCSSParser.h"
51 #include "mozilla/css/Declaration.h"
52 #include "nsCSSStruct.h"
53 #include "prlog.h"
54 #include <math.h>
56 namespace css = mozilla::css;
58 // HELPER METHODS
59 // --------------
61 * Given two units, this method returns a common unit that they can both be
62 * converted into, if possible. This is intended to facilitate
63 * interpolation, distance-computation, and addition between "similar" units.
65 * The ordering of the arguments should not affect the output of this method.
67 * If there's no sensible common unit, this method returns eUnit_Null.
69 * @param aFirstUnit One unit to resolve.
70 * @param aFirstUnit The other unit to resolve.
71 * @return A "common" unit that both source units can be converted into, or
72 * eUnit_Null if that's not possible.
74 static
75 nsStyleAnimation::Unit
76 GetCommonUnit(nsCSSProperty aProperty,
77 nsStyleAnimation::Unit aFirstUnit,
78 nsStyleAnimation::Unit aSecondUnit)
80 if (aFirstUnit != aSecondUnit) {
81 if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
82 (aFirstUnit == nsStyleAnimation::eUnit_Coord ||
83 aFirstUnit == nsStyleAnimation::eUnit_Percent ||
84 aFirstUnit == nsStyleAnimation::eUnit_Calc) &&
85 (aSecondUnit == nsStyleAnimation::eUnit_Coord ||
86 aSecondUnit == nsStyleAnimation::eUnit_Percent ||
87 aSecondUnit == nsStyleAnimation::eUnit_Calc)) {
88 // We can use calc() as the common unit.
89 return nsStyleAnimation::eUnit_Calc;
91 return nsStyleAnimation::eUnit_Null;
93 return aFirstUnit;
96 static
97 nsCSSUnit
98 GetCommonUnit(nsCSSProperty aProperty,
99 nsCSSUnit aFirstUnit,
100 nsCSSUnit aSecondUnit)
102 if (aFirstUnit != aSecondUnit) {
103 if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
104 (aFirstUnit == eCSSUnit_Pixel ||
105 aFirstUnit == eCSSUnit_Percent ||
106 aFirstUnit == eCSSUnit_Calc) &&
107 (aSecondUnit == eCSSUnit_Pixel ||
108 aSecondUnit == eCSSUnit_Percent ||
109 aSecondUnit == eCSSUnit_Calc)) {
110 // We can use calc() as the common unit.
111 return eCSSUnit_Calc;
113 return eCSSUnit_Null;
115 return aFirstUnit;
119 // Greatest Common Divisor
120 static PRUint32
121 gcd(PRUint32 a, PRUint32 b)
123 // Euclid's algorithm; O(N) in the worst case. (There are better
124 // ways, but we don't need them for stroke-dasharray animation.)
125 NS_ABORT_IF_FALSE(a > 0 && b > 0, "positive numbers expected");
127 while (a != b) {
128 if (a > b) {
129 a = a - b;
130 } else {
131 b = b - a;
135 return a;
138 // Least Common Multiple
139 static PRUint32
140 lcm(PRUint32 a, PRUint32 b)
142 // Divide first to reduce overflow risk.
143 return (a / gcd(a, b)) * b;
146 inline void
147 nscoordToCSSValue(nscoord aCoord, nsCSSValue& aCSSValue)
149 aCSSValue.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(aCoord),
150 eCSSUnit_Pixel);
153 // Like nsStyleCoord::Calc, but with length in float pixels instead of nscoord.
154 struct CalcValue {
155 float mLength, mPercent;
156 PRBool mHasPercent;
159 // Requires a canonical calc() value that we generated.
160 static CalcValue
161 ExtractCalcValueInternal(const nsCSSValue& aValue)
163 NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Calc, "unexpected unit");
164 nsCSSValue::Array *arr = aValue.GetArrayValue();
165 NS_ABORT_IF_FALSE(arr->Count() == 1, "unexpected length");
167 const nsCSSValue &topval = arr->Item(0);
168 CalcValue result;
169 if (topval.GetUnit() == eCSSUnit_Pixel) {
170 result.mLength = topval.GetFloatValue();
171 result.mPercent = 0.0f;
172 result.mHasPercent = PR_FALSE;
173 } else {
174 NS_ABORT_IF_FALSE(topval.GetUnit() == eCSSUnit_Calc_Plus,
175 "unexpected unit");
176 nsCSSValue::Array *arr2 = topval.GetArrayValue();
177 const nsCSSValue &len = arr2->Item(0);
178 const nsCSSValue &pct = arr2->Item(1);
179 NS_ABORT_IF_FALSE(len.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
180 NS_ABORT_IF_FALSE(pct.GetUnit() == eCSSUnit_Percent, "unexpected unit");
181 result.mLength = len.GetFloatValue();
182 result.mPercent = pct.GetPercentValue();
183 result.mHasPercent = PR_TRUE;
186 return result;
189 // Requires a canonical calc() value that we generated.
190 static CalcValue
191 ExtractCalcValue(const nsStyleAnimation::Value& aValue)
193 CalcValue result;
194 if (aValue.GetUnit() == nsStyleAnimation::eUnit_Coord) {
195 result.mLength =
196 nsPresContext::AppUnitsToFloatCSSPixels(aValue.GetCoordValue());
197 result.mPercent = 0.0f;
198 result.mHasPercent = PR_FALSE;
199 return result;
201 if (aValue.GetUnit() == nsStyleAnimation::eUnit_Percent) {
202 result.mLength = 0.0f;
203 result.mPercent = aValue.GetPercentValue();
204 result.mHasPercent = PR_TRUE;
205 return result;
207 NS_ABORT_IF_FALSE(aValue.GetUnit() == nsStyleAnimation::eUnit_Calc,
208 "unexpected unit");
209 nsCSSValue *val = aValue.GetCSSValueValue();
210 return ExtractCalcValueInternal(*val);
213 static CalcValue
214 ExtractCalcValue(const nsCSSValue& aValue)
216 CalcValue result;
217 if (aValue.GetUnit() == eCSSUnit_Pixel) {
218 result.mLength = aValue.GetFloatValue();
219 result.mPercent = 0.0f;
220 result.mHasPercent = PR_FALSE;
221 return result;
223 if (aValue.GetUnit() == eCSSUnit_Percent) {
224 result.mLength = 0.0f;
225 result.mPercent = aValue.GetPercentValue();
226 result.mHasPercent = PR_TRUE;
227 return result;
229 return ExtractCalcValueInternal(aValue);
232 static void
233 SetCalcValue(const nsStyleCoord::Calc* aCalc, nsCSSValue& aValue)
235 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
236 if (!aCalc->mHasPercent) {
237 nscoordToCSSValue(aCalc->mLength, arr->Item(0));
238 } else {
239 nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
240 arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
241 nscoordToCSSValue(aCalc->mLength, arr2->Item(0));
242 arr2->Item(1).SetPercentValue(aCalc->mPercent);
245 aValue.SetArrayValue(arr, eCSSUnit_Calc);
248 static already_AddRefed<nsStringBuffer>
249 GetURIAsUtf16StringBuffer(nsIURI* aUri)
251 nsCAutoString utf8String;
252 nsresult rv = aUri->GetSpec(utf8String);
253 NS_ENSURE_SUCCESS(rv, nsnull);
255 return nsCSSValue::BufferFromString(NS_ConvertUTF8toUTF16(utf8String));
258 // CLASS METHODS
259 // -------------
261 PRBool
262 nsStyleAnimation::ComputeDistance(nsCSSProperty aProperty,
263 const Value& aStartValue,
264 const Value& aEndValue,
265 double& aDistance)
267 Unit commonUnit =
268 GetCommonUnit(aProperty, aStartValue.GetUnit(), aEndValue.GetUnit());
270 switch (commonUnit) {
271 case eUnit_Null:
272 case eUnit_Auto:
273 case eUnit_None:
274 case eUnit_Normal:
275 case eUnit_UnparsedString:
276 return PR_FALSE;
278 case eUnit_Enumerated:
279 switch (aProperty) {
280 case eCSSProperty_font_stretch: {
281 // just like eUnit_Integer.
282 PRInt32 startInt = aStartValue.GetIntValue();
283 PRInt32 endInt = aEndValue.GetIntValue();
284 aDistance = PR_ABS(endInt - startInt);
285 return PR_TRUE;
287 default:
288 return PR_FALSE;
290 case eUnit_Visibility: {
291 PRInt32 startVal =
292 aStartValue.GetIntValue() == NS_STYLE_VISIBILITY_VISIBLE;
293 PRInt32 endVal =
294 aEndValue.GetIntValue() == NS_STYLE_VISIBILITY_VISIBLE;
295 aDistance = PR_ABS(startVal - endVal);
296 return PR_TRUE;
298 case eUnit_Integer: {
299 PRInt32 startInt = aStartValue.GetIntValue();
300 PRInt32 endInt = aEndValue.GetIntValue();
301 aDistance = PR_ABS(endInt - startInt);
302 return PR_TRUE;
304 case eUnit_Coord: {
305 nscoord startCoord = aStartValue.GetCoordValue();
306 nscoord endCoord = aEndValue.GetCoordValue();
307 aDistance = fabs(double(endCoord - startCoord));
308 return PR_TRUE;
310 case eUnit_Percent: {
311 float startPct = aStartValue.GetPercentValue();
312 float endPct = aEndValue.GetPercentValue();
313 aDistance = fabs(double(endPct - startPct));
314 return PR_TRUE;
316 case eUnit_Float: {
317 float startFloat = aStartValue.GetFloatValue();
318 float endFloat = aEndValue.GetFloatValue();
319 aDistance = fabs(double(endFloat - startFloat));
320 return PR_TRUE;
322 case eUnit_Color: {
323 // http://www.w3.org/TR/smil-animation/#animateColorElement says
324 // that we should use Euclidean RGB cube distance. However, we
325 // have to extend that to RGBA. For now, we'll just use the
326 // Euclidean distance in the (part of the) 4-cube of premultiplied
327 // colors.
328 // FIXME (spec): The CSS transitions spec doesn't say whether
329 // colors are premultiplied, but things work better when they are,
330 // so use premultiplication. Spec issue is still open per
331 // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
332 nscolor startColor = aStartValue.GetColorValue();
333 nscolor endColor = aEndValue.GetColorValue();
335 // Get a color component on a 0-1 scale, which is much easier to
336 // deal with when working with alpha.
337 #define GET_COMPONENT(component_, color_) \
338 (NS_GET_##component_(color_) * (1.0 / 255.0))
340 double startA = GET_COMPONENT(A, startColor);
341 double startR = GET_COMPONENT(R, startColor) * startA;
342 double startG = GET_COMPONENT(G, startColor) * startA;
343 double startB = GET_COMPONENT(B, startColor) * startA;
344 double endA = GET_COMPONENT(A, endColor);
345 double endR = GET_COMPONENT(R, endColor) * endA;
346 double endG = GET_COMPONENT(G, endColor) * endA;
347 double endB = GET_COMPONENT(B, endColor) * endA;
349 #undef GET_COMPONENT
351 double diffA = startA - endA;
352 double diffR = startR - endR;
353 double diffG = startG - endG;
354 double diffB = startB - endB;
355 aDistance = sqrt(diffA * diffA + diffR * diffR +
356 diffG * diffG + diffB * diffB);
357 return PR_TRUE;
359 case eUnit_Calc: {
360 CalcValue v1 = ExtractCalcValue(aStartValue);
361 CalcValue v2 = ExtractCalcValue(aEndValue);
362 float difflen = v2.mLength - v1.mLength;
363 float diffpct = v2.mPercent - v1.mPercent;
364 aDistance = sqrt(difflen * difflen + diffpct * diffpct);
365 return PR_TRUE;
367 case eUnit_CSSValuePair: {
368 const nsCSSValuePair *pair1 = aStartValue.GetCSSValuePairValue();
369 const nsCSSValuePair *pair2 = aEndValue.GetCSSValuePairValue();
370 nsCSSUnit unit[2];
371 unit[0] = GetCommonUnit(aProperty, pair1->mXValue.GetUnit(),
372 pair2->mXValue.GetUnit());
373 unit[1] = GetCommonUnit(aProperty, pair1->mYValue.GetUnit(),
374 pair2->mYValue.GetUnit());
375 if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
376 unit[0] == eCSSUnit_URL) {
377 return PR_FALSE;
380 double squareDistance = 0.0;
381 static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
382 &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
384 for (PRUint32 i = 0; i < 2; ++i) {
385 nsCSSValue nsCSSValuePair::*member = pairValues[i];
386 double diffsquared;
387 switch (unit[i]) {
388 case eCSSUnit_Pixel: {
389 float diff = (pair1->*member).GetFloatValue() -
390 (pair2->*member).GetFloatValue();
391 diffsquared = diff * diff;
392 break;
394 case eCSSUnit_Percent: {
395 float diff = (pair1->*member).GetPercentValue() -
396 (pair2->*member).GetPercentValue();
397 diffsquared = diff * diff;
398 break;
400 case eCSSUnit_Calc: {
401 CalcValue v1 = ExtractCalcValue(pair1->*member);
402 CalcValue v2 = ExtractCalcValue(pair2->*member);
403 float difflen = v2.mLength - v1.mLength;
404 float diffpct = v2.mPercent - v1.mPercent;
405 diffsquared = difflen * difflen + diffpct * diffpct;
406 break;
408 default:
409 NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
410 return PR_FALSE;
412 squareDistance += diffsquared;
415 aDistance = sqrt(squareDistance);
416 return PR_TRUE;
418 case eUnit_CSSRect: {
419 const nsCSSRect *rect1 = aStartValue.GetCSSRectValue();
420 const nsCSSRect *rect2 = aEndValue.GetCSSRectValue();
421 if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
422 rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
423 rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
424 rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
425 // At least until we have calc()
426 return PR_FALSE;
429 double squareDistance = 0.0;
430 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(nsCSSRect::sides); ++i) {
431 nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
432 NS_ABORT_IF_FALSE((rect1->*member).GetUnit() ==
433 (rect2->*member).GetUnit(),
434 "should have returned above");
435 double diff;
436 switch ((rect1->*member).GetUnit()) {
437 case eCSSUnit_Pixel:
438 diff = (rect1->*member).GetFloatValue() -
439 (rect2->*member).GetFloatValue();
440 break;
441 case eCSSUnit_Auto:
442 diff = 0;
443 break;
444 default:
445 NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
446 return PR_FALSE;
448 squareDistance += diff * diff;
451 aDistance = sqrt(squareDistance);
452 return PR_TRUE;
454 case eUnit_Dasharray: {
455 // NOTE: This produces results on substantially different scales
456 // for length values and percentage values, which might even be
457 // mixed in the same property value. This means the result isn't
458 // particularly useful for paced animation.
460 // Call AddWeighted to make us lists of the same length.
461 Value normValue1, normValue2;
462 if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
463 normValue1) ||
464 !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
465 normValue2)) {
466 return PR_FALSE;
469 double squareDistance = 0.0;
470 const nsCSSValueList *list1 = normValue1.GetCSSValueListValue();
471 const nsCSSValueList *list2 = normValue2.GetCSSValueListValue();
473 NS_ABORT_IF_FALSE(!list1 == !list2, "lists should be same length");
474 while (list1) {
475 const nsCSSValue &val1 = list1->mValue;
476 const nsCSSValue &val2 = list2->mValue;
478 NS_ABORT_IF_FALSE(val1.GetUnit() == val2.GetUnit(),
479 "unit match should be assured by AddWeighted");
480 double diff;
481 switch (val1.GetUnit()) {
482 case eCSSUnit_Percent:
483 diff = val1.GetPercentValue() - val2.GetPercentValue();
484 break;
485 case eCSSUnit_Number:
486 diff = val1.GetFloatValue() - val2.GetFloatValue();
487 break;
488 default:
489 NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
490 return PR_FALSE;
492 squareDistance += diff * diff;
494 list1 = list1->mNext;
495 list2 = list2->mNext;
496 NS_ABORT_IF_FALSE(!list1 == !list2, "lists should be same length");
499 aDistance = sqrt(squareDistance);
500 return PR_TRUE;
502 case eUnit_Shadow: {
503 // Call AddWeighted to make us lists of the same length.
504 Value normValue1, normValue2;
505 if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
506 normValue1) ||
507 !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
508 normValue2)) {
509 return PR_FALSE;
512 const nsCSSValueList *shadow1 = normValue1.GetCSSValueListValue();
513 const nsCSSValueList *shadow2 = normValue2.GetCSSValueListValue();
515 double squareDistance = 0.0;
516 NS_ABORT_IF_FALSE(!shadow1 == !shadow2, "lists should be same length");
517 while (shadow1) {
518 nsCSSValue::Array *array1 = shadow1->mValue.GetArrayValue();
519 nsCSSValue::Array *array2 = shadow2->mValue.GetArrayValue();
520 for (size_t i = 0; i < 4; ++i) {
521 NS_ABORT_IF_FALSE(array1->Item(i).GetUnit() == eCSSUnit_Pixel,
522 "unexpected unit");
523 NS_ABORT_IF_FALSE(array2->Item(i).GetUnit() == eCSSUnit_Pixel,
524 "unexpected unit");
525 double diff = array1->Item(i).GetFloatValue() -
526 array2->Item(i).GetFloatValue();
527 squareDistance += diff * diff;
530 const nsCSSValue &color1 = array1->Item(4);
531 const nsCSSValue &color2 = array2->Item(4);
532 #ifdef DEBUG
534 const nsCSSValue &inset1 = array1->Item(5);
535 const nsCSSValue &inset2 = array2->Item(5);
536 // There are only two possible states of the inset value:
537 // (1) GetUnit() == eCSSUnit_Null
538 // (2) GetUnit() == eCSSUnit_Enumerated &&
539 // GetIntValue() == NS_STYLE_BOX_SHADOW_INSET
540 NS_ABORT_IF_FALSE(color1.GetUnit() == color2.GetUnit() &&
541 inset1 == inset2,
542 "AddWeighted should have failed");
544 #endif
546 if (color1.GetUnit() != eCSSUnit_Null) {
547 nsStyleAnimation::Value color1Value
548 (color1.GetColorValue(), nsStyleAnimation::Value::ColorConstructor);
549 nsStyleAnimation::Value color2Value
550 (color2.GetColorValue(), nsStyleAnimation::Value::ColorConstructor);
551 double colorDistance;
553 #ifdef DEBUG
554 PRBool ok =
555 #endif
556 nsStyleAnimation::ComputeDistance(eCSSProperty_color,
557 color1Value, color2Value,
558 colorDistance);
559 NS_ABORT_IF_FALSE(ok, "should not fail");
560 squareDistance += colorDistance * colorDistance;
563 shadow1 = shadow1->mNext;
564 shadow2 = shadow2->mNext;
565 NS_ABORT_IF_FALSE(!shadow1 == !shadow2, "lists should be same length");
567 aDistance = sqrt(squareDistance);
568 return PR_TRUE;
570 case eUnit_Transform: {
571 // Call AddWeighted to normalize to the format we use for
572 // interpolation. (This is far from ideal, but it provides good
573 // behavior for distance along a running transition.)
574 Value normValue1, normValue2;
575 if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
576 normValue1) ||
577 !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
578 normValue2)) {
579 return PR_FALSE;
581 const nsCSSValueList *list1 = normValue1.GetCSSValueListValue();
582 const nsCSSValueList *list2 = normValue2.GetCSSValueListValue();
584 NS_ABORT_IF_FALSE((list1->mValue.GetUnit() == eCSSUnit_None) ==
585 (list2->mValue.GetUnit() == eCSSUnit_None),
586 "none-ness should match after AddWeighted");
587 if (list1->mValue.GetUnit() == eCSSUnit_None) {
588 aDistance = 0;
589 return PR_TRUE;
592 double squareDistance = 0.0;
593 for (; list1 && list2; list1 = list1->mNext, list2 = list2->mNext) {
594 NS_ABORT_IF_FALSE(list1->mValue.GetUnit() == eCSSUnit_Function &&
595 list2->mValue.GetUnit() == eCSSUnit_Function,
596 "unexpected unit");
597 const nsCSSValue::Array *a1 = list1->mValue.GetArrayValue(),
598 *a2 = list2->mValue.GetArrayValue();
599 NS_ABORT_IF_FALSE(a1->Item(0).GetUnit() == eCSSUnit_Ident &&
600 a2->Item(0).GetUnit() == eCSSUnit_Ident,
601 "unexpected unit");
602 NS_ABORT_IF_FALSE(a1->Item(0) == a2->Item(0),
603 "unexpected function mismatch");
604 NS_ABORT_IF_FALSE(a1->Count() == a2->Count(),
605 "unexpected count mismatch");
606 for (size_t i = 1, iEnd = NS_MIN(a1->Count(), a2->Count());
607 i < iEnd; ++i) {
608 const nsCSSValue &v1 = a1->Item(i), &v2 = a2->Item(i);
609 NS_ABORT_IF_FALSE(v1.GetUnit() == eCSSUnit_Pixel ||
610 v1.GetUnit() == eCSSUnit_Percent ||
611 v1.GetUnit() == eCSSUnit_Calc ||
612 v1.GetUnit() == eCSSUnit_Radian ||
613 v1.GetUnit() == eCSSUnit_Number,
614 "unexpected unit");
615 NS_ABORT_IF_FALSE(v2.GetUnit() == eCSSUnit_Pixel ||
616 v2.GetUnit() == eCSSUnit_Percent ||
617 v2.GetUnit() == eCSSUnit_Calc ||
618 v2.GetUnit() == eCSSUnit_Radian ||
619 v2.GetUnit() == eCSSUnit_Number,
620 "unexpected unit");
621 if (v1.GetUnit() == eCSSUnit_Pixel ||
622 v1.GetUnit() == eCSSUnit_Percent ||
623 v1.GetUnit() == eCSSUnit_Calc) {
624 NS_ABORT_IF_FALSE(v2.GetUnit() == eCSSUnit_Pixel ||
625 v2.GetUnit() == eCSSUnit_Percent ||
626 v2.GetUnit() == eCSSUnit_Calc,
627 "unit mismatch");
628 CalcValue c1 = ExtractCalcValue(v1),
629 c2 = ExtractCalcValue(v2);
630 double diff = c1.mLength - c2.mLength;
631 squareDistance += diff * diff;
632 diff = c1.mPercent - c2.mPercent;
633 squareDistance += diff * diff;
634 } else {
635 NS_ABORT_IF_FALSE(v1.GetUnit() == v2.GetUnit(), "unit mismatch");
636 double diff = v2.GetFloatValue() - v1.GetFloatValue();
637 squareDistance += diff * diff;
641 NS_ABORT_IF_FALSE(!list1 && !list2,
642 "list lengths should match after AddWeighted");
644 aDistance = sqrt(squareDistance);
645 return PR_TRUE;
647 case eUnit_CSSValuePairList: {
648 const nsCSSValuePairList *list1 = aStartValue.GetCSSValuePairListValue();
649 const nsCSSValuePairList *list2 = aEndValue.GetCSSValuePairListValue();
650 double squareDistance = 0.0;
651 do {
652 static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
653 &nsCSSValuePairList::mXValue,
654 &nsCSSValuePairList::mYValue,
656 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(pairListValues); ++i) {
657 const nsCSSValue &v1 = list1->*(pairListValues[i]);
658 const nsCSSValue &v2 = list2->*(pairListValues[i]);
659 nsCSSUnit unit =
660 GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
661 if (unit == eCSSUnit_Null) {
662 return PR_FALSE;
664 double diffsquared = 0.0;
665 switch (unit) {
666 case eCSSUnit_Pixel: {
667 float diff = v1.GetFloatValue() - v2.GetFloatValue();
668 diffsquared = diff * diff;
669 break;
671 case eCSSUnit_Percent: {
672 float diff = v1.GetPercentValue() - v2.GetPercentValue();
673 diffsquared = diff * diff;
674 break;
676 case eCSSUnit_Calc: {
677 CalcValue val1 = ExtractCalcValue(v1);
678 CalcValue val2 = ExtractCalcValue(v2);
679 float difflen = val2.mLength - val1.mLength;
680 float diffpct = val2.mPercent - val1.mPercent;
681 diffsquared = difflen * difflen + diffpct * diffpct;
682 break;
684 default:
685 if (v1 != v2) {
686 return PR_FALSE;
688 break;
690 squareDistance += diffsquared;
692 list1 = list1->mNext;
693 list2 = list2->mNext;
694 } while (list1 && list2);
695 if (list1 || list2) {
696 // We can't interpolate lists of different lengths.
697 return PR_FALSE;
699 aDistance = sqrt(squareDistance);
700 return PR_TRUE;
704 NS_ABORT_IF_FALSE(false, "Can't compute distance using the given common unit");
705 return PR_FALSE;
708 #define MAX_PACKED_COLOR_COMPONENT 255
710 inline PRUint8 ClampColor(double aColor)
712 if (aColor >= MAX_PACKED_COLOR_COMPONENT)
713 return MAX_PACKED_COLOR_COMPONENT;
714 if (aColor <= 0.0)
715 return 0;
716 return NSToIntRound(aColor);
719 static inline void
720 AddCSSValuePixel(double aCoeff1, const nsCSSValue &aValue1,
721 double aCoeff2, const nsCSSValue &aValue2,
722 nsCSSValue &aResult)
724 NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
725 NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
726 aResult.SetFloatValue(aCoeff1 * aValue1.GetFloatValue() +
727 aCoeff2 * aValue2.GetFloatValue(),
728 eCSSUnit_Pixel);
731 static inline void
732 AddCSSValueNumber(double aCoeff1, const nsCSSValue &aValue1,
733 double aCoeff2, const nsCSSValue &aValue2,
734 nsCSSValue &aResult)
736 NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
737 NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
738 aResult.SetFloatValue(aCoeff1 * aValue1.GetFloatValue() +
739 aCoeff2 * aValue2.GetFloatValue(),
740 eCSSUnit_Number);
743 static inline void
744 AddCSSValuePercent(double aCoeff1, const nsCSSValue &aValue1,
745 double aCoeff2, const nsCSSValue &aValue2,
746 nsCSSValue &aResult)
748 NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Percent, "unexpected unit");
749 NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Percent, "unexpected unit");
750 aResult.SetPercentValue(aCoeff1 * aValue1.GetPercentValue() +
751 aCoeff2 * aValue2.GetPercentValue());
754 // Add two canonical-form calc values (eUnit_Calc) to make another
755 // canonical-form calc value.
756 static void
757 AddCSSValueCanonicalCalc(double aCoeff1, const nsCSSValue &aValue1,
758 double aCoeff2, const nsCSSValue &aValue2,
759 nsCSSValue &aResult)
761 CalcValue v1 = ExtractCalcValue(aValue1);
762 CalcValue v2 = ExtractCalcValue(aValue2);
763 NS_ABORT_IF_FALSE(v1.mHasPercent || v2.mHasPercent,
764 "only used on properties that always have percent in calc");
765 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2),
766 acalc = nsCSSValue::Array::Create(1);
767 a->Item(0).SetFloatValue(aCoeff1 * v1.mLength + aCoeff2 * v2.mLength,
768 eCSSUnit_Pixel);
769 a->Item(1).SetPercentValue(aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent);
770 acalc->Item(0).SetArrayValue(a, eCSSUnit_Calc_Plus);
771 aResult.SetArrayValue(acalc, eCSSUnit_Calc);
774 static void
775 AddCSSValueAngle(const nsCSSValue &aValue1, double aCoeff1,
776 const nsCSSValue &aValue2, double aCoeff2,
777 nsCSSValue &aResult)
779 aResult.SetFloatValue(aCoeff1 * aValue1.GetAngleValueInRadians() +
780 aCoeff2 * aValue2.GetAngleValueInRadians(),
781 eCSSUnit_Radian);
784 static PRBool
785 AddShadowItems(double aCoeff1, const nsCSSValue &aValue1,
786 double aCoeff2, const nsCSSValue &aValue2,
787 nsCSSValueList **&aResultTail)
789 // X, Y, Radius, Spread, Color, Inset
790 NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Array,
791 "wrong unit");
792 NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Array,
793 "wrong unit");
794 nsCSSValue::Array *array1 = aValue1.GetArrayValue();
795 nsCSSValue::Array *array2 = aValue2.GetArrayValue();
796 nsRefPtr<nsCSSValue::Array> resultArray = nsCSSValue::Array::Create(6);
798 for (size_t i = 0; i < 4; ++i) {
799 AddCSSValuePixel(aCoeff1, array1->Item(i), aCoeff2, array2->Item(i),
800 resultArray->Item(i));
803 const nsCSSValue& color1 = array1->Item(4);
804 const nsCSSValue& color2 = array2->Item(4);
805 const nsCSSValue& inset1 = array1->Item(5);
806 const nsCSSValue& inset2 = array2->Item(5);
807 if (color1.GetUnit() != color2.GetUnit() ||
808 inset1.GetUnit() != inset2.GetUnit()) {
809 // We don't know how to animate between color and no-color, or
810 // between inset and not-inset.
811 return PR_FALSE;
814 if (color1.GetUnit() != eCSSUnit_Null) {
815 nsStyleAnimation::Value color1Value
816 (color1.GetColorValue(), nsStyleAnimation::Value::ColorConstructor);
817 nsStyleAnimation::Value color2Value
818 (color2.GetColorValue(), nsStyleAnimation::Value::ColorConstructor);
819 nsStyleAnimation::Value resultColorValue;
820 #ifdef DEBUG
821 PRBool ok =
822 #endif
823 nsStyleAnimation::AddWeighted(eCSSProperty_color, aCoeff1, color1Value,
824 aCoeff2, color2Value, resultColorValue);
825 NS_ABORT_IF_FALSE(ok, "should not fail");
826 resultArray->Item(4).SetColorValue(resultColorValue.GetColorValue());
829 NS_ABORT_IF_FALSE(inset1 == inset2, "should match");
830 resultArray->Item(5) = inset1;
832 nsCSSValueList *resultItem = new nsCSSValueList;
833 if (!resultItem) {
834 return PR_FALSE;
836 resultItem->mValue.SetArrayValue(resultArray, eCSSUnit_Array);
837 *aResultTail = resultItem;
838 aResultTail = &resultItem->mNext;
839 return PR_TRUE;
842 static void
843 AddTransformTranslate(const nsCSSValue &aValue1, double aCoeff1,
844 const nsCSSValue &aValue2, double aCoeff2,
845 nsCSSValue &aResult)
847 NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Percent ||
848 aValue1.GetUnit() == eCSSUnit_Pixel ||
849 aValue1.IsCalcUnit(),
850 "unexpected unit");
851 NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Percent ||
852 aValue2.GetUnit() == eCSSUnit_Pixel ||
853 aValue2.IsCalcUnit(),
854 "unexpected unit");
856 if (aValue1.GetUnit() != aValue2.GetUnit() || aValue1.IsCalcUnit()) {
857 // different units; create a calc() expression
858 AddCSSValueCanonicalCalc(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
859 } else if (aValue1.GetUnit() == eCSSUnit_Percent) {
860 // both percent
861 AddCSSValuePercent(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
862 } else {
863 // both pixels
864 AddCSSValuePixel(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
868 static void
869 AddTransformScale(const nsCSSValue &aValue1, double aCoeff1,
870 const nsCSSValue &aValue2, double aCoeff2,
871 nsCSSValue &aResult)
873 // Handle scale, and the two matrix components where identity is 1, by
874 // subtracting 1, multiplying by the coefficients, and then adding 1
875 // back. This gets the right AddWeighted behavior and gets us the
876 // interpolation-against-identity behavior for free.
877 NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
878 NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
880 float v1 = aValue1.GetFloatValue() - 1.0f,
881 v2 = aValue2.GetFloatValue() - 1.0f;
882 float result = v1 * aCoeff1 + v2 * aCoeff2;
883 aResult.SetFloatValue(result + 1.0f, eCSSUnit_Number);
886 static already_AddRefed<nsCSSValue::Array>
887 AppendTransformFunction(nsCSSKeyword aTransformFunction,
888 nsCSSValueList**& aListTail)
890 PRUint32 nargs;
891 if (aTransformFunction == eCSSKeyword_matrix) {
892 nargs = 6;
893 } else if (aTransformFunction == eCSSKeyword_translate ||
894 aTransformFunction == eCSSKeyword_skew ||
895 aTransformFunction == eCSSKeyword_scale) {
896 nargs = 2;
897 } else {
898 NS_ABORT_IF_FALSE(aTransformFunction == eCSSKeyword_translatex ||
899 aTransformFunction == eCSSKeyword_translatey ||
900 aTransformFunction == eCSSKeyword_scalex ||
901 aTransformFunction == eCSSKeyword_scaley ||
902 aTransformFunction == eCSSKeyword_skewx ||
903 aTransformFunction == eCSSKeyword_skewy ||
904 aTransformFunction == eCSSKeyword_rotate,
905 "must be a transform function");
906 nargs = 1;
909 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(nargs + 1);
910 arr->Item(0).SetStringValue(
911 NS_ConvertUTF8toUTF16(nsCSSKeywords::GetStringValue(aTransformFunction)),
912 eCSSUnit_Ident);
914 nsCSSValueList *item = new nsCSSValueList;
915 item->mValue.SetArrayValue(arr, eCSSUnit_Function);
917 *aListTail = item;
918 aListTail = &item->mNext;
920 return arr.forget();
924 * The relevant section of the transitions specification:
925 * http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
926 * defers all of the details to the 2-D and 3-D transforms specifications.
927 * For the 2-D transforms specification (all that's relevant for us, right
928 * now), the relevant section is:
929 * http://dev.w3.org/csswg/css3-2d-transforms/#animation
930 * This, in turn, refers to the unmatrix program in Graphics Gems,
931 * available from http://tog.acm.org/resources/GraphicsGems/ , and in
932 * particular as the file GraphicsGems/gemsii/unmatrix.c
933 * in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
935 * The unmatrix reference is for general 3-D transform matrices (any of the
936 * 16 components can have any value).
938 * For CSS 2-D transforms, we have a 2-D matrix with the bottom row constant:
940 * [ A C E ]
941 * [ B D F ]
942 * [ 0 0 1 ]
944 * For that case, I believe the algorithm in unmatrix reduces to:
946 * (1) If A * D - B * C == 0, the matrix is singular. Fail.
948 * (2) Set translation components (Tx and Ty) to the translation parts of
949 * the matrix (E and F) and then ignore them for the rest of the time.
950 * (For us, E and F each actually consist of three constants: a
951 * length, a multiplier for the width, and a multiplier for the
952 * height. This actually requires its own decomposition, but I'll
953 * keep that separate.)
955 * (3) Let the X scale (Sx) be sqrt(A^2 + B^2). Then divide both A and B
956 * by it.
958 * (4) Let the XY shear (K) be A * C + B * D. From C, subtract A times
959 * the XY shear. From D, subtract B times the XY shear.
961 * (5) Let the Y scale (Sy) be sqrt(C^2 + D^2). Divide C, D, and the XY
962 * shear (K) by it.
964 * (6) At this point, A * D - B * C is either 1 or -1. If it is -1,
965 * negate the XY shear (K), the X scale (Sx), and A, B, C, and D.
966 * (Alternatively, we could negate the XY shear (K) and the Y scale
967 * (Sy).)
969 * (7) Let the rotation be R = atan2(B, A).
971 * Then the resulting decomposed transformation is:
973 * translate(Tx, Ty) rotate(R) skewX(atan(K)) scale(Sx, Sy)
975 * An interesting result of this is that all of the simple transform
976 * functions (i.e., all functions other than matrix()), in isolation,
977 * decompose back to themselves except for:
978 * 'skewY(φ)', which is 'matrix(1, tan(φ), 0, 1, 0, 0)', which decomposes
979 * to 'rotate(φ) skewX(φ) scale(sec(φ), cos(φ))' since (ignoring the
980 * alternate sign possibilities that would get fixed in step 6):
981 * In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
982 * Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
983 * In step 4, the XY shear is sin(φ).
984 * Thus, after step 4, C = -cos(φ)sin(φ) and D = 1 - sin²(φ) = cos²(φ).
985 * Thus, in step 5, the Y scale is sqrt(cos²(φ)(sin²(φ) + cos²(φ)) = cos(φ).
986 * Thus, after step 5, C = -sin(φ), D = cos(φ), and the XY shear is tan(φ).
987 * Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
988 * In step 7, the rotation is thus φ.
990 * skew(θ, φ), which is matrix(1, tan(φ), tan(θ), 1, 0, 0), which decomposes
991 * to 'rotate(φ) skewX(θ + φ) scale(sec(φ), cos(φ))' since (ignoring
992 * the alternate sign possibilities that would get fixed in step 6):
993 * In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
994 * Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
995 * In step 4, the XY shear is cos(φ)tan(θ) + sin(φ).
996 * Thus, after step 4,
997 * C = tan(θ) - cos(φ)(cos(φ)tan(θ) + sin(φ)) = tan(θ)sin²(φ) - cos(φ)sin(φ)
998 * D = 1 - sin(φ)(cos(φ)tan(θ) + sin(φ)) = cos²(φ) - sin(φ)cos(φ)tan(θ)
999 * Thus, in step 5, the Y scale is sqrt(C² + D²) =
1000 * sqrt(tan²(θ)(sin⁴(φ) + sin²(φ)cos²(φ)) -
1001 * 2 tan(θ)(sin³(φ)cos(φ) + sin(φ)cos³(φ)) +
1002 * (sin²(φ)cos²(φ) + cos⁴(φ))) =
1003 * sqrt(tan²(θ)sin²(φ) - 2 tan(θ)sin(φ)cos(φ) + cos²(φ)) =
1004 * cos(φ) - tan(θ)sin(φ) (taking the negative of the obvious solution so
1005 * we avoid flipping in step 6).
1006 * After step 5, C = -sin(φ) and D = cos(φ), and the XY shear is
1007 * (cos(φ)tan(θ) + sin(φ)) / (cos(φ) - tan(θ)sin(φ)) =
1008 * (dividing both numerator and denominator by cos(φ))
1009 * (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)) = tan(θ + φ).
1010 * (See http://en.wikipedia.org/wiki/List_of_trigonometric_identities .)
1011 * Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
1012 * In step 7, the rotation is thus φ.
1014 * To check this result, we can multiply things back together:
1016 * [ cos(φ) -sin(φ) ] [ 1 tan(θ + φ) ] [ sec(φ) 0 ]
1017 * [ sin(φ) cos(φ) ] [ 0 1 ] [ 0 cos(φ) ]
1019 * [ cos(φ) cos(φ)tan(θ + φ) - sin(φ) ] [ sec(φ) 0 ]
1020 * [ sin(φ) sin(φ)tan(θ + φ) + cos(φ) ] [ 0 cos(φ) ]
1022 * but since tan(θ + φ) = (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)),
1023 * cos(φ)tan(θ + φ) - sin(φ)
1024 * = cos(φ)(tan(θ) + tan(φ)) - sin(φ) + sin(φ)tan(θ)tan(φ)
1025 * = cos(φ)tan(θ) + sin(φ) - sin(φ) + sin(φ)tan(θ)tan(φ)
1026 * = cos(φ)tan(θ) + sin(φ)tan(θ)tan(φ)
1027 * = tan(θ) (cos(φ) + sin(φ)tan(φ))
1028 * = tan(θ) sec(φ) (cos²(φ) + sin²(φ))
1029 * = tan(θ) sec(φ)
1030 * and
1031 * sin(φ)tan(θ + φ) + cos(φ)
1032 * = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ)
1033 * = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ)
1034 * = sec(φ) (sin²(φ) + cos²(φ))
1035 * = sec(φ)
1036 * so the above is:
1037 * [ cos(φ) tan(θ) sec(φ) ] [ sec(φ) 0 ]
1038 * [ sin(φ) sec(φ) ] [ 0 cos(φ) ]
1040 * [ 1 tan(θ) ]
1041 * [ tan(φ) 1 ]
1045 * DecomposeMatrix implements the non-translation parts of the above
1046 * decomposition algorithm.
1048 static PRBool
1049 DecomposeMatrix(const nsStyleTransformMatrix &aMatrix,
1050 float &aRotate, float &aXYShear, float &aScaleX, float &aScaleY)
1052 float A = aMatrix.GetMainMatrixEntry(0),
1053 B = aMatrix.GetMainMatrixEntry(1),
1054 C = aMatrix.GetMainMatrixEntry(2),
1055 D = aMatrix.GetMainMatrixEntry(3);
1056 if (A * D == B * C) {
1057 // singular matrix
1058 return PR_FALSE;
1061 float scaleX = sqrt(A * A + B * B);
1062 A /= scaleX;
1063 B /= scaleX;
1065 float XYshear = A * C + B * D;
1066 C -= A * XYshear;
1067 D -= B * XYshear;
1069 float scaleY = sqrt(C * C + D * D);
1070 C /= scaleY;
1071 D /= scaleY;
1072 XYshear /= scaleY;
1074 // A*D - B*C should now be 1 or -1
1075 NS_ASSERTION(0.99 < PR_ABS(A*D - B*C) && PR_ABS(A*D - B*C) < 1.01,
1076 "determinant should now be 1 or -1");
1077 if (A * D < B * C) {
1078 A = -A;
1079 B = -B;
1080 C = -C;
1081 D = -D;
1082 XYshear = -XYshear;
1083 scaleX = -scaleX;
1086 float rotation = atan2f(B, A);
1088 aRotate = rotation;
1089 aXYShear = XYshear;
1090 aScaleX = scaleX;
1091 aScaleY = scaleY;
1093 return PR_TRUE;
1096 static nsCSSValueList*
1097 AddTransformMatrix(const nsStyleTransformMatrix &aMatrix1, double aCoeff1,
1098 const nsStyleTransformMatrix &aMatrix2, double aCoeff2)
1101 nsAutoPtr<nsCSSValueList> result;
1102 nsCSSValueList **resultTail = getter_Transfers(result);
1103 nsRefPtr<nsCSSValue::Array> arr;
1105 // The translation part of the matrix comes first in our result list,
1106 // but it's complicated by the mix of %s, possibly in between rotates.
1108 // append a rotate(90deg)
1109 arr = AppendTransformFunction(eCSSKeyword_rotate, resultTail);
1110 arr->Item(1).SetFloatValue(float(M_PI_2), eCSSUnit_Radian);
1112 // append the translation for parts of the % translation components
1113 // that were from inside a rotation
1114 float rtranslateXPercent =
1115 aMatrix1.GetWidthRelativeYTranslation() * aCoeff1 +
1116 aMatrix2.GetWidthRelativeYTranslation() * aCoeff2;
1117 float rtranslateYPercent =
1118 - (aMatrix1.GetHeightRelativeXTranslation() * aCoeff1 +
1119 aMatrix2.GetHeightRelativeXTranslation() * aCoeff2);
1120 arr = AppendTransformFunction(eCSSKeyword_translate, resultTail);
1121 arr->Item(1).SetPercentValue(rtranslateXPercent);
1122 arr->Item(2).SetPercentValue(rtranslateYPercent);
1124 // append a rotate(-90deg)
1125 arr = AppendTransformFunction(eCSSKeyword_rotate, resultTail);
1126 arr->Item(1).SetFloatValue(-float(M_PI_2), eCSSUnit_Radian);
1128 nscoord translateXCoord = NSToCoordRound(
1129 aMatrix1.GetCoordXTranslation() * aCoeff1 +
1130 aMatrix2.GetCoordXTranslation() * aCoeff2);
1131 nscoord translateYCoord = NSToCoordRound(
1132 aMatrix1.GetCoordYTranslation() * aCoeff1 +
1133 aMatrix2.GetCoordYTranslation() * aCoeff2);
1134 float translateXPercent = aMatrix1.GetWidthRelativeXTranslation() * aCoeff1 +
1135 aMatrix2.GetWidthRelativeXTranslation() * aCoeff2;
1136 float translateYPercent = aMatrix1.GetHeightRelativeYTranslation() * aCoeff1 +
1137 aMatrix2.GetHeightRelativeYTranslation() * aCoeff2;
1139 float rotate1, XYshear1, scaleX1, scaleY1;
1140 DecomposeMatrix(aMatrix1, rotate1, XYshear1, scaleX1, scaleY1);
1141 float rotate2, XYshear2, scaleX2, scaleY2;
1142 DecomposeMatrix(aMatrix2, rotate2, XYshear2, scaleX2, scaleY2);
1144 float rotate = rotate1 * aCoeff1 + rotate2 * aCoeff2;
1146 float skewX = atanf(XYshear1) * aCoeff1 + atanf(XYshear2) * aCoeff2;
1148 // Handle scale, and the two matrix components where identity is 1, by
1149 // subtracting 1, multiplying by the coefficients, and then adding 1
1150 // back. This gets the right AddWeighted behavior and gets us the
1151 // interpolation-against-identity behavior for free.
1152 float scaleX =
1153 ((scaleX1 - 1.0f) * aCoeff1 + (scaleX2 - 1.0f) * aCoeff2) + 1.0f;
1154 float scaleY =
1155 ((scaleY1 - 1.0f) * aCoeff1 + (scaleY2 - 1.0f) * aCoeff2) + 1.0f;
1157 // It's simpler to append an additional function for the percentage
1158 // translate parts than to build a calc() expression.
1159 arr = AppendTransformFunction(eCSSKeyword_translate, resultTail);
1160 arr->Item(1).SetPercentValue(translateXPercent);
1161 arr->Item(2).SetPercentValue(translateYPercent);
1163 arr = AppendTransformFunction(eCSSKeyword_translate, resultTail);
1164 arr->Item(1).SetFloatValue(
1165 nsPresContext::AppUnitsToFloatCSSPixels(translateXCoord), eCSSUnit_Pixel);
1166 arr->Item(2).SetFloatValue(
1167 nsPresContext::AppUnitsToFloatCSSPixels(translateYCoord), eCSSUnit_Pixel);
1169 arr = AppendTransformFunction(eCSSKeyword_rotate, resultTail);
1170 arr->Item(1).SetFloatValue(rotate, eCSSUnit_Radian);
1172 arr = AppendTransformFunction(eCSSKeyword_skewx, resultTail);
1173 arr->Item(1).SetFloatValue(skewX, eCSSUnit_Radian);
1175 arr = AppendTransformFunction(eCSSKeyword_scale, resultTail);
1176 arr->Item(1).SetFloatValue(scaleX, eCSSUnit_Number);
1177 arr->Item(2).SetFloatValue(scaleY, eCSSUnit_Number);
1179 return result.forget();
1182 static nsCSSValueList*
1183 AddTransformLists(const nsCSSValueList* aList1, double aCoeff1,
1184 const nsCSSValueList* aList2, double aCoeff2)
1186 nsAutoPtr<nsCSSValueList> result;
1187 nsCSSValueList **resultTail = getter_Transfers(result);
1189 do {
1190 const nsCSSValue::Array *a1 = aList1->mValue.GetArrayValue(),
1191 *a2 = aList2->mValue.GetArrayValue();
1192 NS_ABORT_IF_FALSE(nsStyleTransformMatrix::TransformFunctionOf(a1) ==
1193 nsStyleTransformMatrix::TransformFunctionOf(a2),
1194 "transform function mismatch");
1196 nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
1197 nsRefPtr<nsCSSValue::Array> arr;
1198 if (tfunc != eCSSKeyword_matrix) {
1199 arr = AppendTransformFunction(tfunc, resultTail);
1202 switch (tfunc) {
1203 case eCSSKeyword_translate: {
1204 NS_ABORT_IF_FALSE(a1->Count() == 2 || a1->Count() == 3,
1205 "unexpected count");
1206 NS_ABORT_IF_FALSE(a2->Count() == 2 || a2->Count() == 3,
1207 "unexpected count");
1209 // FIXME: This would produce fewer calc() expressions if the
1210 // zero were of compatible type (length vs. percent) when
1211 // needed.
1212 nsCSSValue zero(0.0f, eCSSUnit_Pixel);
1213 // Add Y component of translation.
1214 AddTransformTranslate(a1->Count() == 3 ? a1->Item(2) : zero,
1215 aCoeff1,
1216 a2->Count() == 3 ? a2->Item(2) : zero,
1217 aCoeff2,
1218 arr->Item(2));
1220 // Add X component of translation (which can be merged with case
1221 // below in non-DEBUG).
1222 AddTransformTranslate(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
1223 arr->Item(1));
1224 break;
1226 case eCSSKeyword_translatex:
1227 case eCSSKeyword_translatey: {
1228 NS_ABORT_IF_FALSE(a1->Count() == 2, "unexpected count");
1229 NS_ABORT_IF_FALSE(a2->Count() == 2, "unexpected count");
1230 AddTransformTranslate(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
1231 arr->Item(1));
1232 break;
1234 case eCSSKeyword_scale: {
1235 NS_ABORT_IF_FALSE(a1->Count() == 2 || a1->Count() == 3,
1236 "unexpected count");
1237 NS_ABORT_IF_FALSE(a2->Count() == 2 || a2->Count() == 3,
1238 "unexpected count");
1240 // This is different from skew() and translate(), since an
1241 // omitted second parameter repeats the first rather than being
1242 // zero.
1243 // Add Y component of scale.
1244 AddTransformScale(a1->Count() == 3 ? a1->Item(2) : a1->Item(1),
1245 aCoeff1,
1246 a2->Count() == 3 ? a2->Item(2) : a2->Item(1),
1247 aCoeff2,
1248 arr->Item(2));
1250 // Add X component of scale (which can be merged with case below
1251 // in non-DEBUG).
1252 AddTransformScale(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
1253 arr->Item(1));
1255 break;
1257 case eCSSKeyword_scalex:
1258 case eCSSKeyword_scaley: {
1259 NS_ABORT_IF_FALSE(a1->Count() == 2, "unexpected count");
1260 NS_ABORT_IF_FALSE(a2->Count() == 2, "unexpected count");
1262 AddTransformScale(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
1263 arr->Item(1));
1265 break;
1267 // It would probably be nicer to animate skew in tangent space
1268 // rather than angle space. However, it's easy to specify
1269 // skews with infinite tangents, and behavior changes pretty
1270 // drastically when crossing such skews (since the direction of
1271 // animation flips), so interop is probably more important here.
1272 case eCSSKeyword_skew: {
1273 NS_ABORT_IF_FALSE(a1->Count() == 2 || a1->Count() == 3,
1274 "unexpected count");
1275 NS_ABORT_IF_FALSE(a2->Count() == 2 || a2->Count() == 3,
1276 "unexpected count");
1278 nsCSSValue zero(0.0f, eCSSUnit_Radian);
1279 // Add Y component of skew.
1280 AddCSSValueAngle(a1->Count() == 3 ? a1->Item(2) : zero,
1281 aCoeff1,
1282 a2->Count() == 3 ? a2->Item(2) : zero,
1283 aCoeff2,
1284 arr->Item(2));
1286 // Add X component of skew (which can be merged with case below
1287 // in non-DEBUG).
1288 AddCSSValueAngle(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
1289 arr->Item(1));
1291 break;
1293 case eCSSKeyword_skewx:
1294 case eCSSKeyword_skewy:
1295 case eCSSKeyword_rotate: {
1296 NS_ABORT_IF_FALSE(a1->Count() == 2, "unexpected count");
1297 NS_ABORT_IF_FALSE(a2->Count() == 2, "unexpected count");
1299 AddCSSValueAngle(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
1300 arr->Item(1));
1302 break;
1304 case eCSSKeyword_matrix: {
1305 NS_ABORT_IF_FALSE(a1->Count() == 7, "unexpected count");
1306 NS_ABORT_IF_FALSE(a2->Count() == 7, "unexpected count");
1308 PRBool dummy;
1309 nsStyleTransformMatrix matrix1, matrix2;
1310 matrix1.SetToTransformFunction(a1, nsnull, nsnull, dummy);
1311 matrix2.SetToTransformFunction(a2, nsnull, nsnull, dummy);
1313 *resultTail =
1314 AddTransformMatrix(matrix1, aCoeff1, matrix2, aCoeff2);
1316 while ((*resultTail)->mNext) {
1317 resultTail = &(*resultTail)->mNext;
1320 break;
1322 default:
1323 NS_ABORT_IF_FALSE(PR_FALSE, "unknown transform function");
1326 aList1 = aList1->mNext;
1327 aList2 = aList2->mNext;
1328 } while (aList1);
1329 NS_ABORT_IF_FALSE(!aList2, "list length mismatch");
1331 return result.forget();
1334 PRBool
1335 nsStyleAnimation::AddWeighted(nsCSSProperty aProperty,
1336 double aCoeff1, const Value& aValue1,
1337 double aCoeff2, const Value& aValue2,
1338 Value& aResultValue)
1340 Unit commonUnit =
1341 GetCommonUnit(aProperty, aValue1.GetUnit(), aValue2.GetUnit());
1342 // Maybe need a followup method to convert the inputs into the common
1343 // unit-type, if they don't already match it. (Or would it make sense to do
1344 // that in GetCommonUnit? in which case maybe ConvertToCommonUnit would be
1345 // better.)
1347 switch (commonUnit) {
1348 case eUnit_Null:
1349 case eUnit_Auto:
1350 case eUnit_None:
1351 case eUnit_Normal:
1352 case eUnit_UnparsedString:
1353 return PR_FALSE;
1355 case eUnit_Enumerated:
1356 switch (aProperty) {
1357 case eCSSProperty_font_stretch: {
1358 // Animate just like eUnit_Integer.
1359 PRInt32 result = NS_floor(aCoeff1 * double(aValue1.GetIntValue()) +
1360 aCoeff2 * double(aValue2.GetIntValue()));
1361 aResultValue.SetIntValue(result, eUnit_Enumerated);
1362 return PR_TRUE;
1364 default:
1365 return PR_FALSE;
1367 case eUnit_Visibility: {
1368 PRInt32 val1 = aValue1.GetIntValue() == NS_STYLE_VISIBILITY_VISIBLE;
1369 PRInt32 val2 = aValue2.GetIntValue() == NS_STYLE_VISIBILITY_VISIBLE;
1370 double interp = aCoeff1 * val1 + aCoeff2 * val2;
1371 PRInt32 result = interp > 0.0 ? NS_STYLE_VISIBILITY_VISIBLE
1372 : NS_STYLE_VISIBILITY_HIDDEN;
1373 aResultValue.SetIntValue(result, eUnit_Visibility);
1374 return PR_TRUE;
1376 case eUnit_Integer: {
1377 // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
1378 // says we should use floor
1379 PRInt32 result = NS_floor(aCoeff1 * double(aValue1.GetIntValue()) +
1380 aCoeff2 * double(aValue2.GetIntValue()));
1381 if (aProperty == eCSSProperty_font_weight) {
1382 NS_ASSERTION(result > 0, "unexpected value");
1383 result -= result % 100;
1385 aResultValue.SetIntValue(result, eUnit_Integer);
1386 return PR_TRUE;
1388 case eUnit_Coord: {
1389 aResultValue.SetCoordValue(NSToCoordRound(
1390 aCoeff1 * aValue1.GetCoordValue() +
1391 aCoeff2 * aValue2.GetCoordValue()));
1392 return PR_TRUE;
1394 case eUnit_Percent: {
1395 aResultValue.SetPercentValue(
1396 aCoeff1 * aValue1.GetPercentValue() +
1397 aCoeff2 * aValue2.GetPercentValue());
1398 return PR_TRUE;
1400 case eUnit_Float: {
1401 aResultValue.SetFloatValue(
1402 aCoeff1 * aValue1.GetFloatValue() +
1403 aCoeff2 * aValue2.GetFloatValue());
1404 return PR_TRUE;
1406 case eUnit_Color: {
1407 nscolor color1 = aValue1.GetColorValue();
1408 nscolor color2 = aValue2.GetColorValue();
1409 // FIXME (spec): The CSS transitions spec doesn't say whether
1410 // colors are premultiplied, but things work better when they are,
1411 // so use premultiplication. Spec issue is still open per
1412 // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
1414 // To save some math, scale the alpha down to a 0-1 scale, but
1415 // leave the color components on a 0-255 scale.
1416 double A1 = NS_GET_A(color1) * (1.0 / 255.0);
1417 double R1 = NS_GET_R(color1) * A1;
1418 double G1 = NS_GET_G(color1) * A1;
1419 double B1 = NS_GET_B(color1) * A1;
1420 double A2 = NS_GET_A(color2) * (1.0 / 255.0);
1421 double R2 = NS_GET_R(color2) * A2;
1422 double G2 = NS_GET_G(color2) * A2;
1423 double B2 = NS_GET_B(color2) * A2;
1424 double Aresf = (A1 * aCoeff1 + A2 * aCoeff2);
1425 nscolor resultColor;
1426 if (Aresf <= 0.0) {
1427 resultColor = NS_RGBA(0, 0, 0, 0);
1428 } else {
1429 if (Aresf > 1.0) {
1430 Aresf = 1.0;
1432 double factor = 1.0 / Aresf;
1433 PRUint8 Ares = NSToIntRound(Aresf * 255.0);
1434 PRUint8 Rres = ClampColor((R1 * aCoeff1 + R2 * aCoeff2) * factor);
1435 PRUint8 Gres = ClampColor((G1 * aCoeff1 + G2 * aCoeff2) * factor);
1436 PRUint8 Bres = ClampColor((B1 * aCoeff1 + B2 * aCoeff2) * factor);
1437 resultColor = NS_RGBA(Rres, Gres, Bres, Ares);
1439 aResultValue.SetColorValue(resultColor);
1440 return PR_TRUE;
1442 case eUnit_Calc: {
1443 CalcValue v1 = ExtractCalcValue(aValue1);
1444 CalcValue v2 = ExtractCalcValue(aValue2);
1445 double len = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
1446 double pct = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
1447 PRBool hasPct = (aCoeff1 != 0.0 && v1.mHasPercent) ||
1448 (aCoeff2 != 0.0 && v2.mHasPercent);
1449 nsCSSValue *val = new nsCSSValue();
1450 nsCSSValue::Array *arr = nsCSSValue::Array::Create(1);
1451 val->SetArrayValue(arr, eCSSUnit_Calc);
1452 if (hasPct) {
1453 nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
1454 arr2->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
1455 arr2->Item(1).SetPercentValue(pct);
1456 arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
1457 } else {
1458 arr->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
1460 aResultValue.SetAndAdoptCSSValueValue(val, eUnit_Calc);
1461 return PR_TRUE;
1463 case eUnit_CSSValuePair: {
1464 const nsCSSValuePair *pair1 = aValue1.GetCSSValuePairValue();
1465 const nsCSSValuePair *pair2 = aValue2.GetCSSValuePairValue();
1466 nsCSSUnit unit[2];
1467 unit[0] = GetCommonUnit(aProperty, pair1->mXValue.GetUnit(),
1468 pair2->mXValue.GetUnit());
1469 unit[1] = GetCommonUnit(aProperty, pair1->mYValue.GetUnit(),
1470 pair2->mYValue.GetUnit());
1471 if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
1472 unit[0] == eCSSUnit_URL) {
1473 return PR_FALSE;
1476 nsAutoPtr<nsCSSValuePair> result(new nsCSSValuePair);
1477 static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
1478 &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
1480 for (PRUint32 i = 0; i < 2; ++i) {
1481 nsCSSValue nsCSSValuePair::*member = pairValues[i];
1482 switch (unit[i]) {
1483 case eCSSUnit_Pixel:
1484 AddCSSValuePixel(aCoeff1, pair1->*member, aCoeff2, pair2->*member,
1485 result->*member);
1486 break;
1487 case eCSSUnit_Percent:
1488 AddCSSValuePercent(aCoeff1, pair1->*member,
1489 aCoeff2, pair2->*member,
1490 result->*member);
1491 break;
1492 case eCSSUnit_Calc:
1493 AddCSSValueCanonicalCalc(aCoeff1, pair1->*member,
1494 aCoeff2, pair2->*member,
1495 result->*member);
1496 break;
1497 default:
1498 NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
1499 return PR_FALSE;
1503 aResultValue.SetAndAdoptCSSValuePairValue(result.forget(),
1504 eUnit_CSSValuePair);
1505 return PR_TRUE;
1507 case eUnit_CSSRect: {
1508 const nsCSSRect *rect1 = aValue1.GetCSSRectValue();
1509 const nsCSSRect *rect2 = aValue2.GetCSSRectValue();
1510 if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
1511 rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
1512 rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
1513 rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
1514 // At least until we have calc()
1515 return PR_FALSE;
1518 nsAutoPtr<nsCSSRect> result(new nsCSSRect);
1519 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(nsCSSRect::sides); ++i) {
1520 nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
1521 NS_ABORT_IF_FALSE((rect1->*member).GetUnit() ==
1522 (rect2->*member).GetUnit(),
1523 "should have returned above");
1524 switch ((rect1->*member).GetUnit()) {
1525 case eCSSUnit_Pixel:
1526 AddCSSValuePixel(aCoeff1, rect1->*member, aCoeff2, rect2->*member,
1527 result->*member);
1528 break;
1529 case eCSSUnit_Auto:
1530 if (float(aCoeff1 + aCoeff2) != 1.0f) {
1531 // Interpolating between two auto values makes sense;
1532 // adding in other ratios does not.
1533 return PR_FALSE;
1535 (result->*member).SetAutoValue();
1536 break;
1537 default:
1538 NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
1539 return PR_FALSE;
1543 aResultValue.SetAndAdoptCSSRectValue(result.forget(), eUnit_CSSRect);
1544 return PR_TRUE;
1546 case eUnit_Dasharray: {
1547 const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
1548 const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
1550 PRUint32 len1 = 0, len2 = 0;
1551 for (const nsCSSValueList *v = list1; v; v = v->mNext) {
1552 ++len1;
1554 for (const nsCSSValueList *v = list2; v; v = v->mNext) {
1555 ++len2;
1557 NS_ABORT_IF_FALSE(len1 > 0 && len2 > 0, "unexpected length");
1558 if (list1->mValue.GetUnit() == eCSSUnit_None ||
1559 list2->mValue.GetUnit() == eCSSUnit_None) {
1560 // One of our values is "none". Can't do addition with that.
1561 NS_ABORT_IF_FALSE(
1562 (list1->mValue.GetUnit() != eCSSUnit_None || len1 == 1) &&
1563 (list2->mValue.GetUnit() != eCSSUnit_None || len2 == 1),
1564 "multi-value valuelist with 'none' as first element");
1565 return PR_FALSE;
1568 nsAutoPtr<nsCSSValueList> result;
1569 nsCSSValueList **resultTail = getter_Transfers(result);
1570 for (PRUint32 i = 0, i_end = lcm(len1, len2); i != i_end; ++i) {
1571 const nsCSSValue &v1 = list1->mValue;
1572 const nsCSSValue &v2 = list2->mValue;
1573 NS_ABORT_IF_FALSE(v1.GetUnit() == eCSSUnit_Number ||
1574 v1.GetUnit() == eCSSUnit_Percent, "unexpected");
1575 NS_ABORT_IF_FALSE(v2.GetUnit() == eCSSUnit_Number ||
1576 v2.GetUnit() == eCSSUnit_Percent, "unexpected");
1577 if (v1.GetUnit() != v2.GetUnit()) {
1578 // Can't animate between lengths and percentages (until calc()).
1579 return PR_FALSE;
1582 nsCSSValueList *item = new nsCSSValueList;
1583 if (!item) {
1584 return PR_FALSE;
1586 *resultTail = item;
1587 resultTail = &item->mNext;
1589 if (v1.GetUnit() == eCSSUnit_Number) {
1590 AddCSSValueNumber(aCoeff1, v1, aCoeff2, v2, item->mValue);
1591 } else {
1592 AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, item->mValue);
1595 list1 = list1->mNext;
1596 if (!list1) {
1597 list1 = aValue1.GetCSSValueListValue();
1599 list2 = list2->mNext;
1600 if (!list2) {
1601 list2 = aValue2.GetCSSValueListValue();
1605 aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
1606 eUnit_Dasharray);
1607 return PR_TRUE;
1609 case eUnit_Shadow: {
1610 // This is implemented according to:
1611 // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
1612 // and the third item in the summary of:
1613 // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
1614 const nsCSSValueList *shadow1 = aValue1.GetCSSValueListValue();
1615 const nsCSSValueList *shadow2 = aValue2.GetCSSValueListValue();
1616 nsAutoPtr<nsCSSValueList> result;
1617 nsCSSValueList **resultTail = getter_Transfers(result);
1618 while (shadow1 && shadow2) {
1619 if (!AddShadowItems(aCoeff1, shadow1->mValue,
1620 aCoeff2, shadow2->mValue,
1621 resultTail)) {
1622 return PR_FALSE;
1624 shadow1 = shadow1->mNext;
1625 shadow2 = shadow2->mNext;
1627 if (shadow1 || shadow2) {
1628 const nsCSSValueList *longShadow;
1629 double longCoeff;
1630 if (shadow1) {
1631 longShadow = shadow1;
1632 longCoeff = aCoeff1;
1633 } else {
1634 longShadow = shadow2;
1635 longCoeff = aCoeff2;
1638 while (longShadow) {
1639 // Passing coefficients that add to less than 1 produces the
1640 // desired result of interpolating "0 0 0 transparent" with
1641 // the current shadow.
1642 if (!AddShadowItems(longCoeff, longShadow->mValue,
1643 0.0, longShadow->mValue,
1644 resultTail)) {
1645 return PR_FALSE;
1648 longShadow = longShadow->mNext;
1651 aResultValue.SetAndAdoptCSSValueListValue(result.forget(), eUnit_Shadow);
1652 return PR_TRUE;
1654 case eUnit_Transform: {
1655 const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
1656 const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
1658 // We want to avoid the matrix decomposition when we can, since
1659 // avoiding it can produce better results both for compound
1660 // transforms and for skew and skewY (see below). We can do this
1661 // in two cases:
1662 // (1) if one of the transforms is 'none'
1663 // (2) if the lists have the same length and the transform
1664 // functions match
1665 nsAutoPtr<nsCSSValueList> result;
1666 if (list1->mValue.GetUnit() == eCSSUnit_None) {
1667 if (list2->mValue.GetUnit() == eCSSUnit_None) {
1668 result = new nsCSSValueList;
1669 if (result) {
1670 result->mValue.SetNoneValue();
1672 } else {
1673 result = AddTransformLists(list2, aCoeff2, list2, 0);
1675 } else {
1676 if (list2->mValue.GetUnit() == eCSSUnit_None) {
1677 result = AddTransformLists(list1, aCoeff1, list1, 0);
1678 } else {
1679 PRBool match = PR_TRUE;
1682 const nsCSSValueList *item1 = list1, *item2 = list2;
1683 do {
1684 nsCSSKeyword func1 = nsStyleTransformMatrix::TransformFunctionOf(
1685 item1->mValue.GetArrayValue());
1686 nsCSSKeyword func2 = nsStyleTransformMatrix::TransformFunctionOf(
1687 item2->mValue.GetArrayValue());
1688 if (func1 != func2) {
1689 break;
1692 item1 = item1->mNext;
1693 item2 = item2->mNext;
1694 } while (item1 && item2);
1695 if (item1 || item2) {
1696 // Either |break| above or length mismatch.
1697 match = PR_FALSE;
1701 if (match) {
1702 result = AddTransformLists(list1, aCoeff1, list2, aCoeff2);
1703 } else {
1704 PRBool dummy;
1705 nsStyleTransformMatrix matrix1 =
1706 nsStyleTransformMatrix::ReadTransforms(list1, nsnull, nsnull,
1707 dummy),
1708 matrix2 =
1709 nsStyleTransformMatrix::ReadTransforms(list2, nsnull, nsnull,
1710 dummy);
1711 result = AddTransformMatrix(matrix1, aCoeff1, matrix2, aCoeff2);
1716 aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
1717 eUnit_Transform);
1718 return PR_TRUE;
1720 case eUnit_CSSValuePairList: {
1721 const nsCSSValuePairList *list1 = aValue1.GetCSSValuePairListValue();
1722 const nsCSSValuePairList *list2 = aValue2.GetCSSValuePairListValue();
1723 nsAutoPtr<nsCSSValuePairList> result;
1724 nsCSSValuePairList **resultTail = getter_Transfers(result);
1725 do {
1726 nsCSSValuePairList *item = new nsCSSValuePairList;
1727 if (!item) {
1728 return PR_FALSE;
1730 *resultTail = item;
1731 resultTail = &item->mNext;
1733 static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
1734 &nsCSSValuePairList::mXValue,
1735 &nsCSSValuePairList::mYValue,
1737 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(pairListValues); ++i) {
1738 const nsCSSValue &v1 = list1->*(pairListValues[i]);
1739 const nsCSSValue &v2 = list2->*(pairListValues[i]);
1740 nsCSSValue &vr = item->*(pairListValues[i]);
1741 nsCSSUnit unit =
1742 GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
1743 if (unit == eCSSUnit_Null) {
1744 return PR_FALSE;
1746 switch (unit) {
1747 case eCSSUnit_Pixel:
1748 AddCSSValuePixel(aCoeff1, v1, aCoeff2, v2, vr);
1749 break;
1750 case eCSSUnit_Percent:
1751 AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, vr);
1752 break;
1753 case eCSSUnit_Calc:
1754 AddCSSValueCanonicalCalc(aCoeff1, v1, aCoeff2, v2, vr);
1755 break;
1756 default:
1757 if (v1 != v2) {
1758 return PR_FALSE;
1760 vr = v1;
1761 break;
1764 list1 = list1->mNext;
1765 list2 = list2->mNext;
1766 } while (list1 && list2);
1767 if (list1 || list2) {
1768 // We can't interpolate lists of different lengths.
1769 return PR_FALSE;
1772 aResultValue.SetAndAdoptCSSValuePairListValue(result.forget());
1773 return PR_TRUE;
1777 NS_ABORT_IF_FALSE(false, "Can't interpolate using the given common unit");
1778 return PR_FALSE;
1781 already_AddRefed<nsICSSStyleRule>
1782 BuildStyleRule(nsCSSProperty aProperty,
1783 nsIContent* aTargetElement,
1784 const nsAString& aSpecifiedValue,
1785 PRBool aUseSVGMode)
1787 // Set up an empty CSS Declaration
1788 nsAutoPtr<css::Declaration> declaration(new css::Declaration());
1789 declaration->InitializeEmpty();
1791 PRBool changed; // ignored, but needed as outparam for ParseProperty
1792 nsIDocument* doc = aTargetElement->GetOwnerDoc();
1793 nsCOMPtr<nsIURI> baseURI = aTargetElement->GetBaseURI();
1794 nsCSSParser parser(doc->CSSLoader());
1796 if (aUseSVGMode) {
1797 #ifdef MOZ_SVG
1798 parser.SetSVGMode(PR_TRUE);
1799 #else
1800 NS_NOTREACHED("aUseSVGMode should not be set");
1801 #endif
1804 nsCSSProperty propertyToCheck = nsCSSProps::IsShorthand(aProperty) ?
1805 nsCSSProps::SubpropertyEntryFor(aProperty)[0] : aProperty;
1807 // Get a parser, parse the property, and check for CSS parsing errors.
1808 // If any of these steps fails, we bail out and delete the declaration.
1809 if (!parser ||
1810 NS_FAILED(parser.ParseProperty(aProperty, aSpecifiedValue,
1811 doc->GetDocumentURI(), baseURI,
1812 aTargetElement->NodePrincipal(),
1813 declaration, &changed, PR_FALSE)) ||
1814 // check whether property parsed without CSS parsing errors
1815 !declaration->HasNonImportantValueFor(propertyToCheck)) {
1816 NS_WARNING("failure in BuildStyleRule");
1817 return nsnull;
1820 return NS_NewCSSStyleRule(nsnull, declaration.forget());
1823 inline
1824 already_AddRefed<nsStyleContext>
1825 LookupStyleContext(nsIContent* aElement)
1827 nsIDocument* doc = aElement->GetCurrentDoc();
1828 nsIPresShell* shell = doc->GetShell();
1829 if (!shell) {
1830 return nsnull;
1832 return nsComputedDOMStyle::GetStyleContextForElement(aElement->AsElement(),
1833 nsnull, shell);
1838 * Helper function: StyleWithDeclarationAdded
1839 * Creates a nsStyleRule with the specified property set to the specified
1840 * value, and returns a nsStyleContext for this rule, as a sibling of the
1841 * given element's nsStyleContext.
1843 * If we fail to parse |aSpecifiedValue| for |aProperty|, this method will
1844 * return nsnull.
1846 * @param aProperty The property whose value we're customizing in the
1847 * custom style context.
1848 * @param aTargetElement The element whose style context we'll use as a
1849 * sibling for our custom style context.
1850 * @param aSpecifiedValue The value for |aProperty| in our custom style
1851 * context.
1852 * @param aUseSVGMode A flag to indicate whether we should parse
1853 * |aSpecifiedValue| in SVG mode.
1854 * @return The generated custom nsStyleContext, or nsnull on failure.
1856 already_AddRefed<nsStyleContext>
1857 StyleWithDeclarationAdded(nsCSSProperty aProperty,
1858 nsIContent* aTargetElement,
1859 const nsAString& aSpecifiedValue,
1860 PRBool aUseSVGMode)
1862 NS_ABORT_IF_FALSE(aTargetElement, "null target element");
1863 NS_ABORT_IF_FALSE(aTargetElement->GetCurrentDoc(),
1864 "element needs to be in a document "
1865 "if we're going to look up its style context");
1867 // Look up style context for our target element
1868 nsRefPtr<nsStyleContext> styleContext = LookupStyleContext(aTargetElement);
1869 if (!styleContext) {
1870 return nsnull;
1873 // Parse specified value into a temporary nsICSSStyleRule
1874 nsCOMPtr<nsICSSStyleRule> styleRule =
1875 BuildStyleRule(aProperty, aTargetElement, aSpecifiedValue, aUseSVGMode);
1876 if (!styleRule) {
1877 return nsnull;
1880 styleRule->RuleMatched();
1882 // Create a temporary nsStyleContext for the style rule
1883 nsCOMArray<nsIStyleRule> ruleArray;
1884 ruleArray.AppendObject(styleRule);
1885 nsStyleSet* styleSet = styleContext->PresContext()->StyleSet();
1886 return styleSet->ResolveStyleByAddingRules(styleContext, ruleArray);
1889 PRBool
1890 nsStyleAnimation::ComputeValue(nsCSSProperty aProperty,
1891 nsIContent* aTargetElement,
1892 const nsAString& aSpecifiedValue,
1893 PRBool aUseSVGMode,
1894 Value& aComputedValue)
1896 // XXXbz aTargetElement should be an Element
1897 NS_ABORT_IF_FALSE(aTargetElement, "null target element");
1898 NS_ABORT_IF_FALSE(aTargetElement->GetCurrentDoc(),
1899 "we should only be able to actively animate nodes that "
1900 "are in a document");
1902 nsCSSProperty propToParse =
1903 nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_REPORT_OTHER_NAME)
1904 ? nsCSSProps::OtherNameFor(aProperty) : aProperty;
1906 nsRefPtr<nsStyleContext> tmpStyleContext =
1907 StyleWithDeclarationAdded(propToParse, aTargetElement,
1908 aSpecifiedValue, aUseSVGMode);
1909 if (!tmpStyleContext) {
1910 return PR_FALSE;
1913 if (nsCSSProps::IsShorthand(aProperty) ||
1914 nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) {
1915 // Just capture the specified value
1916 aComputedValue.SetUnparsedStringValue(nsString(aSpecifiedValue));
1917 return PR_TRUE;
1919 // Extract computed value of our property from the temporary style rule
1920 return ExtractComputedValue(aProperty, tmpStyleContext, aComputedValue);
1923 PRBool
1924 nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty,
1925 nsPresContext* aPresContext,
1926 const Value& aComputedValue,
1927 nsCSSValue& aSpecifiedValue)
1929 NS_ABORT_IF_FALSE(aPresContext, "null pres context");
1931 switch (aComputedValue.GetUnit()) {
1932 case eUnit_Normal:
1933 aSpecifiedValue.SetNormalValue();
1934 break;
1935 case eUnit_Auto:
1936 aSpecifiedValue.SetAutoValue();
1937 break;
1938 case eUnit_None:
1939 aSpecifiedValue.SetNoneValue();
1940 break;
1941 case eUnit_Enumerated:
1942 case eUnit_Visibility:
1943 aSpecifiedValue.
1944 SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Enumerated);
1945 break;
1946 case eUnit_Integer:
1947 aSpecifiedValue.
1948 SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Integer);
1949 break;
1950 case eUnit_Coord:
1951 nscoordToCSSValue(aComputedValue.GetCoordValue(), aSpecifiedValue);
1952 break;
1953 case eUnit_Percent:
1954 aSpecifiedValue.SetPercentValue(aComputedValue.GetPercentValue());
1955 break;
1956 case eUnit_Float:
1957 aSpecifiedValue.
1958 SetFloatValue(aComputedValue.GetFloatValue(), eCSSUnit_Number);
1959 break;
1960 case eUnit_Color:
1961 // colors can be alone, or part of a paint server
1962 aSpecifiedValue.SetColorValue(aComputedValue.GetColorValue());
1963 break;
1964 case eUnit_Calc: {
1965 nsCSSValue *val = aComputedValue.GetCSSValueValue();
1966 NS_ABORT_IF_FALSE(val->GetUnit() == eCSSUnit_Calc, "unexpected unit");
1967 aSpecifiedValue = *val;
1968 break;
1970 case eUnit_CSSValuePair: {
1971 // Rule node processing expects pair values to be collapsed to a
1972 // single value if both halves would be equal, for most but not
1973 // all properties. At present, all animatable properties that
1974 // use pairs do expect collapsing.
1975 const nsCSSValuePair* pair = aComputedValue.GetCSSValuePairValue();
1976 if (pair->mXValue == pair->mYValue) {
1977 aSpecifiedValue = pair->mXValue;
1978 } else {
1979 aSpecifiedValue.SetPairValue(pair);
1981 } break;
1982 case eUnit_CSSRect: {
1983 nsCSSRect& rect = aSpecifiedValue.SetRectValue();
1984 rect = *aComputedValue.GetCSSRectValue();
1985 } break;
1986 case eUnit_Dasharray:
1987 case eUnit_Shadow:
1988 case eUnit_Transform:
1989 aSpecifiedValue.
1990 SetDependentListValue(aComputedValue.GetCSSValueListValue());
1991 break;
1992 case eUnit_CSSValuePairList:
1993 aSpecifiedValue.
1994 SetDependentPairListValue(aComputedValue.GetCSSValuePairListValue());
1995 break;
1996 default:
1997 return PR_FALSE;
1999 return PR_TRUE;
2002 PRBool
2003 nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty,
2004 nsPresContext* aPresContext,
2005 const Value& aComputedValue,
2006 nsAString& aSpecifiedValue)
2008 NS_ABORT_IF_FALSE(aPresContext, "null pres context");
2009 aSpecifiedValue.Truncate(); // Clear outparam, if it's not already empty
2011 if (aComputedValue.GetUnit() == eUnit_UnparsedString) {
2012 aComputedValue.GetStringValue(aSpecifiedValue);
2013 return PR_TRUE;
2015 nsCSSValue val;
2016 if (!nsStyleAnimation::UncomputeValue(aProperty, aPresContext,
2017 aComputedValue, val)) {
2018 return PR_FALSE;
2021 val.AppendToString(aProperty, aSpecifiedValue);
2022 return PR_TRUE;
2025 inline const void*
2026 StyleDataAtOffset(const void* aStyleStruct, ptrdiff_t aOffset)
2028 return reinterpret_cast<const char*>(aStyleStruct) + aOffset;
2031 inline void*
2032 StyleDataAtOffset(void* aStyleStruct, ptrdiff_t aOffset)
2034 return reinterpret_cast<char*>(aStyleStruct) + aOffset;
2037 static void
2038 ExtractBorderColor(nsStyleContext* aStyleContext, const void* aStyleBorder,
2039 mozilla::css::Side aSide, nsStyleAnimation::Value& aComputedValue)
2041 nscolor color;
2042 PRBool foreground;
2043 static_cast<const nsStyleBorder*>(aStyleBorder)->
2044 GetBorderColor(aSide, color, foreground);
2045 if (foreground) {
2046 // FIXME: should add test for this
2047 color = aStyleContext->GetStyleColor()->mColor;
2049 aComputedValue.SetColorValue(color);
2052 static PRBool
2053 StyleCoordToValue(const nsStyleCoord& aCoord, nsStyleAnimation::Value& aValue)
2055 switch (aCoord.GetUnit()) {
2056 case eStyleUnit_Normal:
2057 aValue.SetNormalValue();
2058 break;
2059 case eStyleUnit_Auto:
2060 aValue.SetAutoValue();
2061 break;
2062 case eStyleUnit_None:
2063 aValue.SetNoneValue();
2064 break;
2065 case eStyleUnit_Percent:
2066 aValue.SetPercentValue(aCoord.GetPercentValue());
2067 break;
2068 case eStyleUnit_Factor:
2069 aValue.SetFloatValue(aCoord.GetFactorValue());
2070 break;
2071 case eStyleUnit_Coord:
2072 aValue.SetCoordValue(aCoord.GetCoordValue());
2073 break;
2074 case eStyleUnit_Enumerated:
2075 aValue.SetIntValue(aCoord.GetIntValue(),
2076 nsStyleAnimation::eUnit_Enumerated);
2077 break;
2078 case eStyleUnit_Integer:
2079 aValue.SetIntValue(aCoord.GetIntValue(),
2080 nsStyleAnimation::eUnit_Integer);
2081 break;
2082 case eStyleUnit_Calc: {
2083 nsAutoPtr<nsCSSValue> val(new nsCSSValue);
2084 SetCalcValue(aCoord.GetCalcValue(), *val);
2085 aValue.SetAndAdoptCSSValueValue(val.forget(),
2086 nsStyleAnimation::eUnit_Calc);
2087 break;
2089 default:
2090 return PR_FALSE;
2092 return PR_TRUE;
2095 static PRBool
2096 StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue)
2098 switch (aCoord.GetUnit()) {
2099 case eStyleUnit_Coord:
2100 nscoordToCSSValue(aCoord.GetCoordValue(), aCSSValue);
2101 break;
2102 case eStyleUnit_Percent:
2103 aCSSValue.SetPercentValue(aCoord.GetPercentValue());
2104 break;
2105 case eStyleUnit_Calc:
2106 SetCalcValue(aCoord.GetCalcValue(), aCSSValue);
2107 break;
2108 default:
2109 NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
2110 return PR_FALSE;
2112 return PR_TRUE;
2116 * Assign |aOutput = aInput|, except with any non-pixel lengths
2117 * replaced with the equivalent in pixels, and any non-canonical calc()
2118 * expressions replaced with canonical ones.
2120 static void
2121 SubstitutePixelValues(nsStyleContext* aStyleContext,
2122 const nsCSSValue& aInput, nsCSSValue& aOutput)
2124 if (aInput.IsCalcUnit()) {
2125 PRBool canStoreInRuleTree = PR_TRUE;
2126 nsRuleNode::ComputedCalc c =
2127 nsRuleNode::SpecifiedCalcToComputedCalc(aInput, aStyleContext,
2128 aStyleContext->PresContext(),
2129 canStoreInRuleTree);
2130 nsStyleCoord::Calc c2;
2131 c2.mLength = c.mLength;
2132 c2.mPercent = c.mPercent;
2133 c2.mHasPercent = PR_TRUE; // doesn't matter for transform translate
2134 SetCalcValue(&c2, aOutput);
2135 } else if (aInput.UnitHasArrayValue()) {
2136 const nsCSSValue::Array *inputArray = aInput.GetArrayValue();
2137 nsRefPtr<nsCSSValue::Array> outputArray =
2138 nsCSSValue::Array::Create(inputArray->Count());
2139 for (size_t i = 0, i_end = inputArray->Count(); i < i_end; ++i) {
2140 SubstitutePixelValues(aStyleContext,
2141 inputArray->Item(i), outputArray->Item(i));
2143 aOutput.SetArrayValue(outputArray, aInput.GetUnit());
2144 } else if (aInput.IsLengthUnit() &&
2145 aInput.GetUnit() != eCSSUnit_Pixel) {
2146 PRBool canStoreInRuleTree = PR_TRUE;
2147 nscoord len = nsRuleNode::CalcLength(aInput, aStyleContext,
2148 aStyleContext->PresContext(),
2149 canStoreInRuleTree);
2150 aOutput.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(len),
2151 eCSSUnit_Pixel);
2152 } else {
2153 aOutput = aInput;
2157 PRBool
2158 nsStyleAnimation::ExtractComputedValue(nsCSSProperty aProperty,
2159 nsStyleContext* aStyleContext,
2160 Value& aComputedValue)
2162 NS_ABORT_IF_FALSE(0 <= aProperty &&
2163 aProperty < eCSSProperty_COUNT_no_shorthands,
2164 "bad property");
2165 const void* styleStruct =
2166 aStyleContext->GetStyleData(nsCSSProps::kSIDTable[aProperty]);
2167 ptrdiff_t ssOffset = nsCSSProps::kStyleStructOffsetTable[aProperty];
2168 nsStyleAnimType animType = nsCSSProps::kAnimTypeTable[aProperty];
2169 NS_ABORT_IF_FALSE(0 <= ssOffset || animType == eStyleAnimType_Custom,
2170 "must be dealing with animatable property");
2171 switch (animType) {
2172 case eStyleAnimType_Custom:
2173 switch (aProperty) {
2174 // For border-width, ignore the border-image business (which
2175 // only exists until we update our implementation to the current
2176 // spec) and use GetComputedBorder
2178 #define BORDER_WIDTH_CASE(prop_, side_) \
2179 case prop_: \
2180 aComputedValue.SetCoordValue( \
2181 static_cast<const nsStyleBorder*>(styleStruct)-> \
2182 GetComputedBorder().side_); \
2183 break;
2184 BORDER_WIDTH_CASE(eCSSProperty_border_bottom_width, bottom)
2185 BORDER_WIDTH_CASE(eCSSProperty_border_left_width_value, left)
2186 BORDER_WIDTH_CASE(eCSSProperty_border_right_width_value, right)
2187 BORDER_WIDTH_CASE(eCSSProperty_border_top_width, top)
2188 #undef BORDER_WIDTH_CASE
2190 case eCSSProperty__moz_column_rule_width:
2191 aComputedValue.SetCoordValue(
2192 static_cast<const nsStyleColumn*>(styleStruct)->
2193 GetComputedColumnRuleWidth());
2194 break;
2196 case eCSSProperty_border_bottom_color:
2197 ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_BOTTOM,
2198 aComputedValue);
2199 break;
2200 case eCSSProperty_border_left_color_value:
2201 ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_LEFT,
2202 aComputedValue);
2203 break;
2204 case eCSSProperty_border_right_color_value:
2205 ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_RIGHT,
2206 aComputedValue);
2207 break;
2208 case eCSSProperty_border_top_color:
2209 ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_TOP,
2210 aComputedValue);
2211 break;
2213 case eCSSProperty_outline_color: {
2214 const nsStyleOutline *styleOutline =
2215 static_cast<const nsStyleOutline*>(styleStruct);
2216 nscolor color;
2217 #ifdef GFX_HAS_INVERT
2218 // This isn't right. And note that outline drawing itself
2219 // goes through this codepath via GetVisitedDependentColor.
2220 styleOutline->GetOutlineColor(color);
2221 #else
2222 if (!styleOutline->GetOutlineColor(color))
2223 color = aStyleContext->GetStyleColor()->mColor;
2224 #endif
2225 aComputedValue.SetColorValue(color);
2226 break;
2229 case eCSSProperty__moz_column_rule_color: {
2230 const nsStyleColumn *styleColumn =
2231 static_cast<const nsStyleColumn*>(styleStruct);
2232 nscolor color;
2233 if (styleColumn->mColumnRuleColorIsForeground) {
2234 color = aStyleContext->GetStyleColor()->mColor;
2235 } else {
2236 color = styleColumn->mColumnRuleColor;
2238 aComputedValue.SetColorValue(color);
2239 break;
2242 case eCSSProperty__moz_column_count: {
2243 const nsStyleColumn *styleColumn =
2244 static_cast<const nsStyleColumn*>(styleStruct);
2245 if (styleColumn->mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO) {
2246 aComputedValue.SetAutoValue();
2247 } else {
2248 aComputedValue.SetIntValue(styleColumn->mColumnCount,
2249 eUnit_Integer);
2251 break;
2254 case eCSSProperty_border_spacing: {
2255 const nsStyleTableBorder *styleTableBorder =
2256 static_cast<const nsStyleTableBorder*>(styleStruct);
2257 nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
2258 if (!pair) {
2259 return PR_FALSE;
2261 nscoordToCSSValue(styleTableBorder->mBorderSpacingX, pair->mXValue);
2262 nscoordToCSSValue(styleTableBorder->mBorderSpacingY, pair->mYValue);
2263 aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
2264 eUnit_CSSValuePair);
2265 break;
2268 case eCSSProperty__moz_transform_origin: {
2269 const nsStyleDisplay *styleDisplay =
2270 static_cast<const nsStyleDisplay*>(styleStruct);
2271 nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
2272 if (!pair ||
2273 !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[0],
2274 pair->mXValue) ||
2275 !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[1],
2276 pair->mYValue)) {
2277 return PR_FALSE;
2279 aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
2280 eUnit_CSSValuePair);
2281 break;
2284 case eCSSProperty_stroke_dasharray: {
2285 const nsStyleSVG *svg = static_cast<const nsStyleSVG*>(styleStruct);
2286 NS_ABORT_IF_FALSE((svg->mStrokeDasharray != nsnull) ==
2287 (svg->mStrokeDasharrayLength != 0),
2288 "pointer/length mismatch");
2289 nsAutoPtr<nsCSSValueList> result;
2290 if (svg->mStrokeDasharray) {
2291 NS_ABORT_IF_FALSE(svg->mStrokeDasharrayLength > 0,
2292 "non-null list should have positive length");
2293 nsCSSValueList **resultTail = getter_Transfers(result);
2294 for (PRUint32 i = 0, i_end = svg->mStrokeDasharrayLength;
2295 i != i_end; ++i) {
2296 nsCSSValueList *item = new nsCSSValueList;
2297 if (!item) {
2298 return PR_FALSE;
2300 *resultTail = item;
2301 resultTail = &item->mNext;
2303 const nsStyleCoord &coord = svg->mStrokeDasharray[i];
2304 nsCSSValue &value = item->mValue;
2305 switch (coord.GetUnit()) {
2306 case eStyleUnit_Coord:
2307 // Number means the same thing as length; we want to
2308 // animate them the same way. Normalize both to number
2309 // since it has more accuracy (float vs nscoord).
2310 value.SetFloatValue(nsPresContext::
2311 AppUnitsToFloatCSSPixels(coord.GetCoordValue()),
2312 eCSSUnit_Number);
2313 break;
2314 case eStyleUnit_Factor:
2315 value.SetFloatValue(coord.GetFactorValue(),
2316 eCSSUnit_Number);
2317 break;
2318 case eStyleUnit_Percent:
2319 value.SetPercentValue(coord.GetPercentValue());
2320 break;
2321 default:
2322 NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
2323 return PR_FALSE;
2326 } else {
2327 result = new nsCSSValueList;
2328 if (!result) {
2329 return PR_FALSE;
2331 result->mValue.SetNoneValue();
2333 aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
2334 eUnit_Dasharray);
2335 break;
2338 case eCSSProperty_font_stretch: {
2339 PRInt16 stretch =
2340 static_cast<const nsStyleFont*>(styleStruct)->mFont.stretch;
2341 PR_STATIC_ASSERT(NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED == -4);
2342 PR_STATIC_ASSERT(NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED == 4);
2343 if (stretch < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED ||
2344 stretch > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
2345 return PR_FALSE;
2347 aComputedValue.SetIntValue(stretch, eUnit_Enumerated);
2348 return PR_TRUE;
2351 case eCSSProperty_font_weight: {
2352 PRUint16 weight =
2353 static_cast<const nsStyleFont*>(styleStruct)->mFont.weight;
2354 if (weight % 100 != 0) {
2355 return PR_FALSE;
2357 aComputedValue.SetIntValue(weight, eUnit_Integer);
2358 return PR_TRUE;
2361 case eCSSProperty_image_region: {
2362 const nsStyleList *list =
2363 static_cast<const nsStyleList*>(styleStruct);
2364 const nsRect &srect = list->mImageRegion;
2365 if (srect.IsEmpty()) {
2366 aComputedValue.SetAutoValue();
2367 break;
2370 nsCSSRect *vrect = new nsCSSRect;
2371 nscoordToCSSValue(srect.x, vrect->mLeft);
2372 nscoordToCSSValue(srect.y, vrect->mTop);
2373 nscoordToCSSValue(srect.XMost(), vrect->mRight);
2374 nscoordToCSSValue(srect.YMost(), vrect->mBottom);
2375 aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
2376 break;
2379 case eCSSProperty_clip: {
2380 const nsStyleDisplay *display =
2381 static_cast<const nsStyleDisplay*>(styleStruct);
2382 if (!(display->mClipFlags & NS_STYLE_CLIP_RECT)) {
2383 aComputedValue.SetAutoValue();
2384 } else {
2385 nsCSSRect *vrect = new nsCSSRect;
2386 const nsRect &srect = display->mClip;
2387 if (display->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) {
2388 vrect->mTop.SetAutoValue();
2389 } else {
2390 nscoordToCSSValue(srect.y, vrect->mTop);
2392 if (display->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) {
2393 vrect->mRight.SetAutoValue();
2394 } else {
2395 nscoordToCSSValue(srect.XMost(), vrect->mRight);
2397 if (display->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) {
2398 vrect->mBottom.SetAutoValue();
2399 } else {
2400 nscoordToCSSValue(srect.YMost(), vrect->mBottom);
2402 if (display->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) {
2403 vrect->mLeft.SetAutoValue();
2404 } else {
2405 nscoordToCSSValue(srect.x, vrect->mLeft);
2407 aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
2409 break;
2412 case eCSSProperty_background_position: {
2413 const nsStyleBackground *bg =
2414 static_cast<const nsStyleBackground*>(styleStruct);
2415 nsAutoPtr<nsCSSValuePairList> result;
2416 nsCSSValuePairList **resultTail = getter_Transfers(result);
2417 NS_ABORT_IF_FALSE(bg->mPositionCount > 0, "unexpected count");
2418 for (PRUint32 i = 0, i_end = bg->mPositionCount; i != i_end; ++i) {
2419 nsCSSValuePairList *item = new nsCSSValuePairList;
2420 *resultTail = item;
2421 resultTail = &item->mNext;
2423 const nsStyleBackground::Position &pos = bg->mLayers[i].mPosition;
2424 if (pos.mXPosition.mLength == 0) {
2425 item->mXValue.SetPercentValue(pos.mXPosition.mPercent);
2426 } else if (pos.mXPosition.mPercent == 0.0f) {
2427 nscoordToCSSValue(pos.mXPosition.mLength, item->mXValue);
2428 } else {
2429 nsStyleCoord::Calc calc;
2430 calc.mLength = pos.mXPosition.mLength;
2431 calc.mPercent = pos.mXPosition.mPercent;
2432 calc.mHasPercent = PR_TRUE;
2433 SetCalcValue(&calc, item->mXValue);
2435 if (pos.mYPosition.mLength == 0) {
2436 item->mYValue.SetPercentValue(pos.mYPosition.mPercent);
2437 } else if (pos.mYPosition.mPercent == 0.0f) {
2438 nscoordToCSSValue(pos.mYPosition.mLength, item->mYValue);
2439 } else {
2440 nsStyleCoord::Calc calc;
2441 calc.mLength = pos.mYPosition.mLength;
2442 calc.mPercent = pos.mYPosition.mPercent;
2443 calc.mHasPercent = PR_TRUE;
2444 SetCalcValue(&calc, item->mYValue);
2448 aComputedValue.SetAndAdoptCSSValuePairListValue(result.forget());
2449 break;
2452 case eCSSProperty_background_size: {
2453 const nsStyleBackground *bg =
2454 static_cast<const nsStyleBackground*>(styleStruct);
2455 nsAutoPtr<nsCSSValuePairList> result;
2456 nsCSSValuePairList **resultTail = getter_Transfers(result);
2457 NS_ABORT_IF_FALSE(bg->mSizeCount > 0, "unexpected count");
2458 for (PRUint32 i = 0, i_end = bg->mSizeCount; i != i_end; ++i) {
2459 nsCSSValuePairList *item = new nsCSSValuePairList;
2460 *resultTail = item;
2461 resultTail = &item->mNext;
2463 const nsStyleBackground::Size &size = bg->mLayers[i].mSize;
2464 switch (size.mWidthType) {
2465 case nsStyleBackground::Size::eContain:
2466 case nsStyleBackground::Size::eCover:
2467 item->mXValue.SetIntValue(size.mWidthType,
2468 eCSSUnit_Enumerated);
2469 break;
2470 case nsStyleBackground::Size::eAuto:
2471 item->mXValue.SetAutoValue();
2472 break;
2473 case nsStyleBackground::Size::eLengthPercentage:
2474 if (size.mWidth.mLength == 0) {
2475 item->mXValue.SetPercentValue(size.mWidth.mPercent);
2476 } else if (size.mWidth.mPercent == 0.0f) {
2477 nscoordToCSSValue(size.mWidth.mLength, item->mXValue);
2478 } else {
2479 nsStyleCoord::Calc calc;
2480 calc.mLength = size.mWidth.mLength;
2481 calc.mPercent = size.mWidth.mPercent;
2482 calc.mHasPercent = PR_TRUE;
2483 SetCalcValue(&calc, item->mXValue);
2485 break;
2488 switch (size.mHeightType) {
2489 case nsStyleBackground::Size::eContain:
2490 case nsStyleBackground::Size::eCover:
2491 // leave it null
2492 break;
2493 case nsStyleBackground::Size::eAuto:
2494 item->mYValue.SetAutoValue();
2495 break;
2496 case nsStyleBackground::Size::eLengthPercentage:
2497 if (size.mHeight.mLength == 0) {
2498 item->mYValue.SetPercentValue(size.mHeight.mPercent);
2499 } else if (size.mHeight.mPercent == 0.0f) {
2500 nscoordToCSSValue(size.mHeight.mLength, item->mYValue);
2501 } else {
2502 nsStyleCoord::Calc calc;
2503 calc.mLength = size.mHeight.mLength;
2504 calc.mPercent = size.mHeight.mPercent;
2505 calc.mHasPercent = PR_TRUE;
2506 SetCalcValue(&calc, item->mYValue);
2508 break;
2512 aComputedValue.SetAndAdoptCSSValuePairListValue(result.forget());
2513 break;
2516 case eCSSProperty__moz_transform: {
2517 const nsStyleDisplay *display =
2518 static_cast<const nsStyleDisplay*>(styleStruct);
2519 nsAutoPtr<nsCSSValueList> result;
2520 if (display->mSpecifiedTransform) {
2521 // Clone, and convert all lengths (not percents) to pixels.
2522 nsCSSValueList **resultTail = getter_Transfers(result);
2523 for (const nsCSSValueList *l = display->mSpecifiedTransform;
2524 l; l = l->mNext) {
2525 nsCSSValueList *clone = new nsCSSValueList;
2526 *resultTail = clone;
2527 resultTail = &clone->mNext;
2529 SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue);
2531 } else {
2532 result = new nsCSSValueList();
2533 result->mValue.SetNoneValue();
2536 aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
2537 eUnit_Transform);
2538 break;
2541 default:
2542 NS_ABORT_IF_FALSE(PR_FALSE, "missing property implementation");
2543 return PR_FALSE;
2545 return PR_TRUE;
2546 case eStyleAnimType_Coord:
2547 return StyleCoordToValue(*static_cast<const nsStyleCoord*>(
2548 StyleDataAtOffset(styleStruct, ssOffset)), aComputedValue);
2549 case eStyleAnimType_Sides_Top:
2550 case eStyleAnimType_Sides_Right:
2551 case eStyleAnimType_Sides_Bottom:
2552 case eStyleAnimType_Sides_Left: {
2553 PR_STATIC_ASSERT(0 == NS_SIDE_TOP);
2554 PR_STATIC_ASSERT(eStyleAnimType_Sides_Right - eStyleAnimType_Sides_Top
2555 == NS_SIDE_RIGHT);
2556 PR_STATIC_ASSERT(eStyleAnimType_Sides_Bottom - eStyleAnimType_Sides_Top
2557 == NS_SIDE_BOTTOM);
2558 PR_STATIC_ASSERT(eStyleAnimType_Sides_Left - eStyleAnimType_Sides_Top
2559 == NS_SIDE_LEFT);
2560 const nsStyleCoord &coord = static_cast<const nsStyleSides*>(
2561 StyleDataAtOffset(styleStruct, ssOffset))->
2562 Get(mozilla::css::Side(animType - eStyleAnimType_Sides_Top));
2563 return StyleCoordToValue(coord, aComputedValue);
2565 case eStyleAnimType_Corner_TopLeft:
2566 case eStyleAnimType_Corner_TopRight:
2567 case eStyleAnimType_Corner_BottomRight:
2568 case eStyleAnimType_Corner_BottomLeft: {
2569 PR_STATIC_ASSERT(0 == NS_CORNER_TOP_LEFT);
2570 PR_STATIC_ASSERT(eStyleAnimType_Corner_TopRight -
2571 eStyleAnimType_Corner_TopLeft
2572 == NS_CORNER_TOP_RIGHT);
2573 PR_STATIC_ASSERT(eStyleAnimType_Corner_BottomRight -
2574 eStyleAnimType_Corner_TopLeft
2575 == NS_CORNER_BOTTOM_RIGHT);
2576 PR_STATIC_ASSERT(eStyleAnimType_Corner_BottomLeft -
2577 eStyleAnimType_Corner_TopLeft
2578 == NS_CORNER_BOTTOM_LEFT);
2579 const nsStyleCorners *corners = static_cast<const nsStyleCorners*>(
2580 StyleDataAtOffset(styleStruct, ssOffset));
2581 PRUint8 fullCorner = animType - eStyleAnimType_Corner_TopLeft;
2582 const nsStyleCoord &horiz =
2583 corners->Get(NS_FULL_TO_HALF_CORNER(fullCorner, PR_FALSE));
2584 const nsStyleCoord &vert =
2585 corners->Get(NS_FULL_TO_HALF_CORNER(fullCorner, PR_TRUE));
2586 nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
2587 if (!pair ||
2588 !StyleCoordToCSSValue(horiz, pair->mXValue) ||
2589 !StyleCoordToCSSValue(vert, pair->mYValue)) {
2590 return PR_FALSE;
2592 aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
2593 eUnit_CSSValuePair);
2594 return PR_TRUE;
2596 case eStyleAnimType_nscoord:
2597 aComputedValue.SetCoordValue(*static_cast<const nscoord*>(
2598 StyleDataAtOffset(styleStruct, ssOffset)));
2599 return PR_TRUE;
2600 case eStyleAnimType_EnumU8:
2601 aComputedValue.SetIntValue(*static_cast<const PRUint8*>(
2602 StyleDataAtOffset(styleStruct, ssOffset)), eUnit_Enumerated);
2603 return PR_TRUE;
2604 case eStyleAnimType_float:
2605 aComputedValue.SetFloatValue(*static_cast<const float*>(
2606 StyleDataAtOffset(styleStruct, ssOffset)));
2607 if (aProperty == eCSSProperty_font_size_adjust &&
2608 aComputedValue.GetFloatValue() == 0.0f) {
2609 // In nsStyleFont, we set mFont.sizeAdjust to 0 to represent
2610 // font-size-adjust: none. Here, we have to treat this as a keyword
2611 // instead of a float value, to make sure we don't end up doing
2612 // interpolation with it.
2613 aComputedValue.SetNoneValue();
2615 return PR_TRUE;
2616 case eStyleAnimType_Color:
2617 aComputedValue.SetColorValue(*static_cast<const nscolor*>(
2618 StyleDataAtOffset(styleStruct, ssOffset)));
2619 return PR_TRUE;
2620 case eStyleAnimType_PaintServer: {
2621 const nsStyleSVGPaint &paint = *static_cast<const nsStyleSVGPaint*>(
2622 StyleDataAtOffset(styleStruct, ssOffset));
2623 if (paint.mType == eStyleSVGPaintType_Color) {
2624 aComputedValue.SetColorValue(paint.mPaint.mColor);
2625 return PR_TRUE;
2627 if (paint.mType == eStyleSVGPaintType_Server) {
2628 if (!paint.mPaint.mPaintServer) {
2629 NS_WARNING("Null paint server");
2630 return PR_FALSE;
2632 nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
2633 nsRefPtr<nsStringBuffer> uriAsStringBuffer =
2634 GetURIAsUtf16StringBuffer(paint.mPaint.mPaintServer);
2635 NS_ENSURE_TRUE(!!uriAsStringBuffer, PR_FALSE);
2636 nsIDocument* doc = aStyleContext->PresContext()->Document();
2637 nsRefPtr<nsCSSValue::URL> url =
2638 new nsCSSValue::URL(paint.mPaint.mPaintServer,
2639 uriAsStringBuffer,
2640 doc->GetDocumentURI(),
2641 doc->NodePrincipal());
2642 pair->mXValue.SetURLValue(url);
2643 pair->mYValue.SetColorValue(paint.mFallbackColor);
2644 aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
2645 eUnit_CSSValuePair);
2646 return PR_TRUE;
2648 NS_ABORT_IF_FALSE(paint.mType == eStyleSVGPaintType_None,
2649 "Unexpected SVG paint type");
2650 aComputedValue.SetNoneValue();
2651 return PR_TRUE;
2653 case eStyleAnimType_Shadow: {
2654 const nsCSSShadowArray *shadowArray =
2655 *static_cast<const nsRefPtr<nsCSSShadowArray>*>(
2656 StyleDataAtOffset(styleStruct, ssOffset));
2657 if (!shadowArray) {
2658 aComputedValue.SetAndAdoptCSSValueListValue(nsnull, eUnit_Shadow);
2659 return PR_TRUE;
2661 nsAutoPtr<nsCSSValueList> result;
2662 nsCSSValueList **resultTail = getter_Transfers(result);
2663 for (PRUint32 i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
2664 const nsCSSShadowItem *shadow = shadowArray->ShadowAt(i);
2665 // X, Y, Radius, Spread, Color, Inset
2666 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(6);
2667 nscoordToCSSValue(shadow->mXOffset, arr->Item(0));
2668 nscoordToCSSValue(shadow->mYOffset, arr->Item(1));
2669 nscoordToCSSValue(shadow->mRadius, arr->Item(2));
2670 // NOTE: This code sometimes stores mSpread: 0 even when
2671 // the parser would be required to leave it null.
2672 nscoordToCSSValue(shadow->mSpread, arr->Item(3));
2673 if (shadow->mHasColor) {
2674 arr->Item(4).SetColorValue(shadow->mColor);
2676 if (shadow->mInset) {
2677 arr->Item(5).SetIntValue(NS_STYLE_BOX_SHADOW_INSET,
2678 eCSSUnit_Enumerated);
2681 nsCSSValueList *resultItem = new nsCSSValueList;
2682 if (!resultItem) {
2683 return PR_FALSE;
2685 resultItem->mValue.SetArrayValue(arr, eCSSUnit_Array);
2686 *resultTail = resultItem;
2687 resultTail = &resultItem->mNext;
2689 aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
2690 eUnit_Shadow);
2691 return PR_TRUE;
2693 case eStyleAnimType_None:
2694 NS_NOTREACHED("shouldn't use on non-animatable properties");
2696 return PR_FALSE;
2699 nsStyleAnimation::Value::Value(PRInt32 aInt, Unit aUnit,
2700 IntegerConstructorType)
2702 NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
2703 mUnit = aUnit;
2704 mValue.mInt = aInt;
2707 nsStyleAnimation::Value::Value(nscoord aLength, CoordConstructorType)
2709 mUnit = eUnit_Coord;
2710 mValue.mCoord = aLength;
2713 nsStyleAnimation::Value::Value(float aPercent, PercentConstructorType)
2715 mUnit = eUnit_Percent;
2716 mValue.mFloat = aPercent;
2719 nsStyleAnimation::Value::Value(float aFloat, FloatConstructorType)
2721 mUnit = eUnit_Float;
2722 mValue.mFloat = aFloat;
2725 nsStyleAnimation::Value::Value(nscolor aColor, ColorConstructorType)
2727 mUnit = eUnit_Color;
2728 mValue.mColor = aColor;
2731 nsStyleAnimation::Value&
2732 nsStyleAnimation::Value::operator=(const Value& aOther)
2734 FreeValue();
2736 mUnit = aOther.mUnit;
2737 switch (mUnit) {
2738 case eUnit_Null:
2739 case eUnit_Normal:
2740 case eUnit_Auto:
2741 case eUnit_None:
2742 break;
2743 case eUnit_Enumerated:
2744 case eUnit_Visibility:
2745 case eUnit_Integer:
2746 mValue.mInt = aOther.mValue.mInt;
2747 break;
2748 case eUnit_Coord:
2749 mValue.mCoord = aOther.mValue.mCoord;
2750 break;
2751 case eUnit_Percent:
2752 case eUnit_Float:
2753 mValue.mFloat = aOther.mValue.mFloat;
2754 break;
2755 case eUnit_Color:
2756 mValue.mColor = aOther.mValue.mColor;
2757 break;
2758 case eUnit_Calc:
2759 NS_ABORT_IF_FALSE(aOther.mValue.mCSSValue, "values may not be null");
2760 mValue.mCSSValue = new nsCSSValue(*aOther.mValue.mCSSValue);
2761 if (!mValue.mCSSValue) {
2762 mUnit = eUnit_Null;
2764 break;
2765 case eUnit_CSSValuePair:
2766 NS_ABORT_IF_FALSE(aOther.mValue.mCSSValuePair,
2767 "value pairs may not be null");
2768 mValue.mCSSValuePair = new nsCSSValuePair(*aOther.mValue.mCSSValuePair);
2769 if (!mValue.mCSSValuePair) {
2770 mUnit = eUnit_Null;
2772 break;
2773 case eUnit_CSSRect:
2774 NS_ABORT_IF_FALSE(aOther.mValue.mCSSRect, "rects may not be null");
2775 mValue.mCSSRect = new nsCSSRect(*aOther.mValue.mCSSRect);
2776 if (!mValue.mCSSRect) {
2777 mUnit = eUnit_Null;
2779 break;
2780 case eUnit_Dasharray:
2781 case eUnit_Shadow:
2782 case eUnit_Transform:
2783 NS_ABORT_IF_FALSE(mUnit == eUnit_Shadow || aOther.mValue.mCSSValueList,
2784 "value lists other than shadows may not be null");
2785 if (aOther.mValue.mCSSValueList) {
2786 mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
2787 if (!mValue.mCSSValueList) {
2788 mUnit = eUnit_Null;
2790 } else {
2791 mValue.mCSSValueList = nsnull;
2793 break;
2794 case eUnit_CSSValuePairList:
2795 NS_ABORT_IF_FALSE(aOther.mValue.mCSSValuePairList,
2796 "value pair lists may not be null");
2797 mValue.mCSSValuePairList = aOther.mValue.mCSSValuePairList->Clone();
2798 if (!mValue.mCSSValuePairList) {
2799 mUnit = eUnit_Null;
2801 break;
2802 case eUnit_UnparsedString:
2803 NS_ABORT_IF_FALSE(aOther.mValue.mString, "expecting non-null string");
2804 mValue.mString = aOther.mValue.mString;
2805 mValue.mString->AddRef();
2806 break;
2809 return *this;
2812 void
2813 nsStyleAnimation::Value::SetNormalValue()
2815 FreeValue();
2816 mUnit = eUnit_Normal;
2819 void
2820 nsStyleAnimation::Value::SetAutoValue()
2822 FreeValue();
2823 mUnit = eUnit_Auto;
2826 void
2827 nsStyleAnimation::Value::SetNoneValue()
2829 FreeValue();
2830 mUnit = eUnit_None;
2833 void
2834 nsStyleAnimation::Value::SetIntValue(PRInt32 aInt, Unit aUnit)
2836 NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
2837 FreeValue();
2838 mUnit = aUnit;
2839 mValue.mInt = aInt;
2842 void
2843 nsStyleAnimation::Value::SetCoordValue(nscoord aLength)
2845 FreeValue();
2846 mUnit = eUnit_Coord;
2847 mValue.mCoord = aLength;
2850 void
2851 nsStyleAnimation::Value::SetPercentValue(float aPercent)
2853 FreeValue();
2854 mUnit = eUnit_Percent;
2855 mValue.mFloat = aPercent;
2858 void
2859 nsStyleAnimation::Value::SetFloatValue(float aFloat)
2861 FreeValue();
2862 mUnit = eUnit_Float;
2863 mValue.mFloat = aFloat;
2866 void
2867 nsStyleAnimation::Value::SetColorValue(nscolor aColor)
2869 FreeValue();
2870 mUnit = eUnit_Color;
2871 mValue.mColor = aColor;
2874 void
2875 nsStyleAnimation::Value::SetUnparsedStringValue(const nsString& aString)
2877 FreeValue();
2878 mUnit = eUnit_UnparsedString;
2879 mValue.mString = nsCSSValue::BufferFromString(aString).get();
2880 if (NS_UNLIKELY(!mValue.mString)) {
2881 // not much we can do here; just make sure that our promise of a
2882 // non-null mValue.mString holds for string units.
2883 mUnit = eUnit_Null;
2887 void
2888 nsStyleAnimation::Value::SetAndAdoptCSSValueValue(nsCSSValue *aValue,
2889 Unit aUnit)
2891 FreeValue();
2892 NS_ABORT_IF_FALSE(IsCSSValueUnit(aUnit), "bad unit");
2893 NS_ABORT_IF_FALSE(aValue != nsnull, "values may not be null");
2894 mUnit = aUnit;
2895 mValue.mCSSValue = aValue; // take ownership
2898 void
2899 nsStyleAnimation::Value::SetAndAdoptCSSValuePairValue(
2900 nsCSSValuePair *aValuePair, Unit aUnit)
2902 FreeValue();
2903 NS_ABORT_IF_FALSE(IsCSSValuePairUnit(aUnit), "bad unit");
2904 NS_ABORT_IF_FALSE(aValuePair != nsnull, "value pairs may not be null");
2905 mUnit = aUnit;
2906 mValue.mCSSValuePair = aValuePair; // take ownership
2909 void
2910 nsStyleAnimation::Value::SetAndAdoptCSSRectValue(nsCSSRect *aRect, Unit aUnit)
2912 FreeValue();
2913 NS_ABORT_IF_FALSE(IsCSSRectUnit(aUnit), "bad unit");
2914 NS_ABORT_IF_FALSE(aRect != nsnull, "value pairs may not be null");
2915 mUnit = aUnit;
2916 mValue.mCSSRect = aRect; // take ownership
2919 void
2920 nsStyleAnimation::Value::SetAndAdoptCSSValueListValue(
2921 nsCSSValueList *aValueList, Unit aUnit)
2923 FreeValue();
2924 NS_ABORT_IF_FALSE(IsCSSValueListUnit(aUnit), "bad unit");
2925 NS_ABORT_IF_FALSE(aUnit != eUnit_Dasharray || aValueList != nsnull,
2926 "dasharrays may not be null");
2927 mUnit = aUnit;
2928 mValue.mCSSValueList = aValueList; // take ownership
2931 void
2932 nsStyleAnimation::Value::SetAndAdoptCSSValuePairListValue(
2933 nsCSSValuePairList *aValuePairList)
2935 FreeValue();
2936 NS_ABORT_IF_FALSE(aValuePairList, "may not be null");
2937 mUnit = eUnit_CSSValuePairList;
2938 mValue.mCSSValuePairList = aValuePairList; // take ownership
2941 void
2942 nsStyleAnimation::Value::FreeValue()
2944 if (IsCSSValueUnit(mUnit)) {
2945 delete mValue.mCSSValue;
2946 } else if (IsCSSValueListUnit(mUnit)) {
2947 delete mValue.mCSSValueList;
2948 } else if (IsCSSValuePairUnit(mUnit)) {
2949 delete mValue.mCSSValuePair;
2950 } else if (IsCSSRectUnit(mUnit)) {
2951 delete mValue.mCSSRect;
2952 } else if (IsCSSValuePairListUnit(mUnit)) {
2953 delete mValue.mCSSValuePairList;
2954 } else if (IsStringUnit(mUnit)) {
2955 NS_ABORT_IF_FALSE(mValue.mString, "expecting non-null string");
2956 mValue.mString->Release();
2960 PRBool
2961 nsStyleAnimation::Value::operator==(const Value& aOther) const
2963 if (mUnit != aOther.mUnit) {
2964 return PR_FALSE;
2967 switch (mUnit) {
2968 case eUnit_Null:
2969 case eUnit_Normal:
2970 case eUnit_Auto:
2971 case eUnit_None:
2972 return PR_TRUE;
2973 case eUnit_Enumerated:
2974 case eUnit_Visibility:
2975 case eUnit_Integer:
2976 return mValue.mInt == aOther.mValue.mInt;
2977 case eUnit_Coord:
2978 return mValue.mCoord == aOther.mValue.mCoord;
2979 case eUnit_Percent:
2980 case eUnit_Float:
2981 return mValue.mFloat == aOther.mValue.mFloat;
2982 case eUnit_Color:
2983 return mValue.mColor == aOther.mValue.mColor;
2984 case eUnit_Calc:
2985 return *mValue.mCSSValue == *aOther.mValue.mCSSValue;
2986 case eUnit_CSSValuePair:
2987 return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
2988 case eUnit_CSSRect:
2989 return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
2990 case eUnit_Dasharray:
2991 case eUnit_Shadow:
2992 case eUnit_Transform:
2993 return *mValue.mCSSValueList == *aOther.mValue.mCSSValueList;
2994 case eUnit_CSSValuePairList:
2995 return *mValue.mCSSValuePairList == *aOther.mValue.mCSSValuePairList;
2996 case eUnit_UnparsedString:
2997 return (NS_strcmp(GetStringBufferValue(),
2998 aOther.GetStringBufferValue()) == 0);
3001 NS_NOTREACHED("incomplete case");
3002 return PR_FALSE;