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
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.
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"
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"
56 namespace css
= mozilla::css
;
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.
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
;
98 GetCommonUnit(nsCSSProperty aProperty
,
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
;
119 // Greatest Common Divisor
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");
138 // Least Common Multiple
140 lcm(PRUint32 a
, PRUint32 b
)
142 // Divide first to reduce overflow risk.
143 return (a
/ gcd(a
, b
)) * b
;
147 nscoordToCSSValue(nscoord aCoord
, nsCSSValue
& aCSSValue
)
149 aCSSValue
.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(aCoord
),
153 // Like nsStyleCoord::Calc, but with length in float pixels instead of nscoord.
155 float mLength
, mPercent
;
159 // Requires a canonical calc() value that we generated.
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);
169 if (topval
.GetUnit() == eCSSUnit_Pixel
) {
170 result
.mLength
= topval
.GetFloatValue();
171 result
.mPercent
= 0.0f
;
172 result
.mHasPercent
= PR_FALSE
;
174 NS_ABORT_IF_FALSE(topval
.GetUnit() == eCSSUnit_Calc_Plus
,
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
;
189 // Requires a canonical calc() value that we generated.
191 ExtractCalcValue(const nsStyleAnimation::Value
& aValue
)
194 if (aValue
.GetUnit() == nsStyleAnimation::eUnit_Coord
) {
196 nsPresContext::AppUnitsToFloatCSSPixels(aValue
.GetCoordValue());
197 result
.mPercent
= 0.0f
;
198 result
.mHasPercent
= PR_FALSE
;
201 if (aValue
.GetUnit() == nsStyleAnimation::eUnit_Percent
) {
202 result
.mLength
= 0.0f
;
203 result
.mPercent
= aValue
.GetPercentValue();
204 result
.mHasPercent
= PR_TRUE
;
207 NS_ABORT_IF_FALSE(aValue
.GetUnit() == nsStyleAnimation::eUnit_Calc
,
209 nsCSSValue
*val
= aValue
.GetCSSValueValue();
210 return ExtractCalcValueInternal(*val
);
214 ExtractCalcValue(const nsCSSValue
& aValue
)
217 if (aValue
.GetUnit() == eCSSUnit_Pixel
) {
218 result
.mLength
= aValue
.GetFloatValue();
219 result
.mPercent
= 0.0f
;
220 result
.mHasPercent
= PR_FALSE
;
223 if (aValue
.GetUnit() == eCSSUnit_Percent
) {
224 result
.mLength
= 0.0f
;
225 result
.mPercent
= aValue
.GetPercentValue();
226 result
.mHasPercent
= PR_TRUE
;
229 return ExtractCalcValueInternal(aValue
);
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));
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
));
262 nsStyleAnimation::ComputeDistance(nsCSSProperty aProperty
,
263 const Value
& aStartValue
,
264 const Value
& aEndValue
,
268 GetCommonUnit(aProperty
, aStartValue
.GetUnit(), aEndValue
.GetUnit());
270 switch (commonUnit
) {
275 case eUnit_UnparsedString
:
278 case eUnit_Enumerated
:
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
);
290 case eUnit_Visibility
: {
292 aStartValue
.GetIntValue() == NS_STYLE_VISIBILITY_VISIBLE
;
294 aEndValue
.GetIntValue() == NS_STYLE_VISIBILITY_VISIBLE
;
295 aDistance
= PR_ABS(startVal
- endVal
);
298 case eUnit_Integer
: {
299 PRInt32 startInt
= aStartValue
.GetIntValue();
300 PRInt32 endInt
= aEndValue
.GetIntValue();
301 aDistance
= PR_ABS(endInt
- startInt
);
305 nscoord startCoord
= aStartValue
.GetCoordValue();
306 nscoord endCoord
= aEndValue
.GetCoordValue();
307 aDistance
= fabs(double(endCoord
- startCoord
));
310 case eUnit_Percent
: {
311 float startPct
= aStartValue
.GetPercentValue();
312 float endPct
= aEndValue
.GetPercentValue();
313 aDistance
= fabs(double(endPct
- startPct
));
317 float startFloat
= aStartValue
.GetFloatValue();
318 float endFloat
= aEndValue
.GetFloatValue();
319 aDistance
= fabs(double(endFloat
- startFloat
));
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
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
;
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
);
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
);
367 case eUnit_CSSValuePair
: {
368 const nsCSSValuePair
*pair1
= aStartValue
.GetCSSValuePairValue();
369 const nsCSSValuePair
*pair2
= aEndValue
.GetCSSValuePairValue();
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
) {
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
];
388 case eCSSUnit_Pixel
: {
389 float diff
= (pair1
->*member
).GetFloatValue() -
390 (pair2
->*member
).GetFloatValue();
391 diffsquared
= diff
* diff
;
394 case eCSSUnit_Percent
: {
395 float diff
= (pair1
->*member
).GetPercentValue() -
396 (pair2
->*member
).GetPercentValue();
397 diffsquared
= diff
* diff
;
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
;
409 NS_ABORT_IF_FALSE(PR_FALSE
, "unexpected unit");
412 squareDistance
+= diffsquared
;
415 aDistance
= sqrt(squareDistance
);
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()
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");
436 switch ((rect1
->*member
).GetUnit()) {
438 diff
= (rect1
->*member
).GetFloatValue() -
439 (rect2
->*member
).GetFloatValue();
445 NS_ABORT_IF_FALSE(PR_FALSE
, "unexpected unit");
448 squareDistance
+= diff
* diff
;
451 aDistance
= sqrt(squareDistance
);
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
,
464 !AddWeighted(aProperty
, 0.0, aStartValue
, 1.0, aEndValue
,
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");
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");
481 switch (val1
.GetUnit()) {
482 case eCSSUnit_Percent
:
483 diff
= val1
.GetPercentValue() - val2
.GetPercentValue();
485 case eCSSUnit_Number
:
486 diff
= val1
.GetFloatValue() - val2
.GetFloatValue();
489 NS_ABORT_IF_FALSE(PR_FALSE
, "unexpected unit");
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
);
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
,
507 !AddWeighted(aProperty
, 0.0, aStartValue
, 1.0, aEndValue
,
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");
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
,
523 NS_ABORT_IF_FALSE(array2
->Item(i
).GetUnit() == eCSSUnit_Pixel
,
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);
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() &&
542 "AddWeighted should have failed");
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
;
556 nsStyleAnimation::ComputeDistance(eCSSProperty_color
,
557 color1Value
, color2Value
,
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
);
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
,
577 !AddWeighted(aProperty
, 0.0, aStartValue
, 1.0, aEndValue
,
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
) {
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
,
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
,
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());
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
,
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
,
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
,
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
;
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
);
647 case eUnit_CSSValuePairList
: {
648 const nsCSSValuePairList
*list1
= aStartValue
.GetCSSValuePairListValue();
649 const nsCSSValuePairList
*list2
= aEndValue
.GetCSSValuePairListValue();
650 double squareDistance
= 0.0;
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
]);
660 GetCommonUnit(aProperty
, v1
.GetUnit(), v2
.GetUnit());
661 if (unit
== eCSSUnit_Null
) {
664 double diffsquared
= 0.0;
666 case eCSSUnit_Pixel
: {
667 float diff
= v1
.GetFloatValue() - v2
.GetFloatValue();
668 diffsquared
= diff
* diff
;
671 case eCSSUnit_Percent
: {
672 float diff
= v1
.GetPercentValue() - v2
.GetPercentValue();
673 diffsquared
= diff
* diff
;
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
;
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.
699 aDistance
= sqrt(squareDistance
);
704 NS_ABORT_IF_FALSE(false, "Can't compute distance using the given common unit");
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
;
716 return NSToIntRound(aColor
);
720 AddCSSValuePixel(double aCoeff1
, const nsCSSValue
&aValue1
,
721 double aCoeff2
, const nsCSSValue
&aValue2
,
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(),
732 AddCSSValueNumber(double aCoeff1
, const nsCSSValue
&aValue1
,
733 double aCoeff2
, const nsCSSValue
&aValue2
,
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(),
744 AddCSSValuePercent(double aCoeff1
, const nsCSSValue
&aValue1
,
745 double aCoeff2
, const nsCSSValue
&aValue2
,
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.
757 AddCSSValueCanonicalCalc(double aCoeff1
, const nsCSSValue
&aValue1
,
758 double aCoeff2
, const nsCSSValue
&aValue2
,
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
,
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
);
775 AddCSSValueAngle(const nsCSSValue
&aValue1
, double aCoeff1
,
776 const nsCSSValue
&aValue2
, double aCoeff2
,
779 aResult
.SetFloatValue(aCoeff1
* aValue1
.GetAngleValueInRadians() +
780 aCoeff2
* aValue2
.GetAngleValueInRadians(),
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
,
792 NS_ABORT_IF_FALSE(aValue2
.GetUnit() == eCSSUnit_Array
,
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.
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
;
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
;
836 resultItem
->mValue
.SetArrayValue(resultArray
, eCSSUnit_Array
);
837 *aResultTail
= resultItem
;
838 aResultTail
= &resultItem
->mNext
;
843 AddTransformTranslate(const nsCSSValue
&aValue1
, double aCoeff1
,
844 const nsCSSValue
&aValue2
, double aCoeff2
,
847 NS_ABORT_IF_FALSE(aValue1
.GetUnit() == eCSSUnit_Percent
||
848 aValue1
.GetUnit() == eCSSUnit_Pixel
||
849 aValue1
.IsCalcUnit(),
851 NS_ABORT_IF_FALSE(aValue2
.GetUnit() == eCSSUnit_Percent
||
852 aValue2
.GetUnit() == eCSSUnit_Pixel
||
853 aValue2
.IsCalcUnit(),
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
) {
861 AddCSSValuePercent(aCoeff1
, aValue1
, aCoeff2
, aValue2
, aResult
);
864 AddCSSValuePixel(aCoeff1
, aValue1
, aCoeff2
, aValue2
, aResult
);
869 AddTransformScale(const nsCSSValue
&aValue1
, double aCoeff1
,
870 const nsCSSValue
&aValue2
, double aCoeff2
,
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
)
891 if (aTransformFunction
== eCSSKeyword_matrix
) {
893 } else if (aTransformFunction
== eCSSKeyword_translate
||
894 aTransformFunction
== eCSSKeyword_skew
||
895 aTransformFunction
== eCSSKeyword_scale
) {
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");
909 nsRefPtr
<nsCSSValue::Array
> arr
= nsCSSValue::Array::Create(nargs
+ 1);
910 arr
->Item(0).SetStringValue(
911 NS_ConvertUTF8toUTF16(nsCSSKeywords::GetStringValue(aTransformFunction
)),
914 nsCSSValueList
*item
= new nsCSSValueList
;
915 item
->mValue
.SetArrayValue(arr
, eCSSUnit_Function
);
918 aListTail
= &item
->mNext
;
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:
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
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
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
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²(φ))
1031 * sin(φ)tan(θ + φ) + cos(φ)
1032 * = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ)
1033 * = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ)
1034 * = sec(φ) (sin²(φ) + cos²(φ))
1037 * [ cos(φ) tan(θ) sec(φ) ] [ sec(φ) 0 ]
1038 * [ sin(φ) sec(φ) ] [ 0 cos(φ) ]
1045 * DecomposeMatrix implements the non-translation parts of the above
1046 * decomposition algorithm.
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
) {
1061 float scaleX
= sqrt(A
* A
+ B
* B
);
1065 float XYshear
= A
* C
+ B
* D
;
1069 float scaleY
= sqrt(C
* C
+ D
* D
);
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
) {
1086 float rotation
= atan2f(B
, A
);
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.
1153 ((scaleX1
- 1.0f
) * aCoeff1
+ (scaleX2
- 1.0f
) * aCoeff2
) + 1.0f
;
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
);
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
);
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
1212 nsCSSValue
zero(0.0f
, eCSSUnit_Pixel
);
1213 // Add Y component of translation.
1214 AddTransformTranslate(a1
->Count() == 3 ? a1
->Item(2) : zero
,
1216 a2
->Count() == 3 ? a2
->Item(2) : zero
,
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
,
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
,
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
1243 // Add Y component of scale.
1244 AddTransformScale(a1
->Count() == 3 ? a1
->Item(2) : a1
->Item(1),
1246 a2
->Count() == 3 ? a2
->Item(2) : a2
->Item(1),
1250 // Add X component of scale (which can be merged with case below
1252 AddTransformScale(a1
->Item(1), aCoeff1
, a2
->Item(1), aCoeff2
,
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
,
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
,
1282 a2
->Count() == 3 ? a2
->Item(2) : zero
,
1286 // Add X component of skew (which can be merged with case below
1288 AddCSSValueAngle(a1
->Item(1), aCoeff1
, a2
->Item(1), aCoeff2
,
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
,
1304 case eCSSKeyword_matrix
: {
1305 NS_ABORT_IF_FALSE(a1
->Count() == 7, "unexpected count");
1306 NS_ABORT_IF_FALSE(a2
->Count() == 7, "unexpected count");
1309 nsStyleTransformMatrix matrix1
, matrix2
;
1310 matrix1
.SetToTransformFunction(a1
, nsnull
, nsnull
, dummy
);
1311 matrix2
.SetToTransformFunction(a2
, nsnull
, nsnull
, dummy
);
1314 AddTransformMatrix(matrix1
, aCoeff1
, matrix2
, aCoeff2
);
1316 while ((*resultTail
)->mNext
) {
1317 resultTail
= &(*resultTail
)->mNext
;
1323 NS_ABORT_IF_FALSE(PR_FALSE
, "unknown transform function");
1326 aList1
= aList1
->mNext
;
1327 aList2
= aList2
->mNext
;
1329 NS_ABORT_IF_FALSE(!aList2
, "list length mismatch");
1331 return result
.forget();
1335 nsStyleAnimation::AddWeighted(nsCSSProperty aProperty
,
1336 double aCoeff1
, const Value
& aValue1
,
1337 double aCoeff2
, const Value
& aValue2
,
1338 Value
& aResultValue
)
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
1347 switch (commonUnit
) {
1352 case eUnit_UnparsedString
:
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
);
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
);
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
);
1389 aResultValue
.SetCoordValue(NSToCoordRound(
1390 aCoeff1
* aValue1
.GetCoordValue() +
1391 aCoeff2
* aValue2
.GetCoordValue()));
1394 case eUnit_Percent
: {
1395 aResultValue
.SetPercentValue(
1396 aCoeff1
* aValue1
.GetPercentValue() +
1397 aCoeff2
* aValue2
.GetPercentValue());
1401 aResultValue
.SetFloatValue(
1402 aCoeff1
* aValue1
.GetFloatValue() +
1403 aCoeff2
* aValue2
.GetFloatValue());
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
;
1427 resultColor
= NS_RGBA(0, 0, 0, 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
);
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
);
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
);
1458 arr
->Item(0).SetFloatValue(len
, eCSSUnit_Pixel
);
1460 aResultValue
.SetAndAdoptCSSValueValue(val
, eUnit_Calc
);
1463 case eUnit_CSSValuePair
: {
1464 const nsCSSValuePair
*pair1
= aValue1
.GetCSSValuePairValue();
1465 const nsCSSValuePair
*pair2
= aValue2
.GetCSSValuePairValue();
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
) {
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
];
1483 case eCSSUnit_Pixel
:
1484 AddCSSValuePixel(aCoeff1
, pair1
->*member
, aCoeff2
, pair2
->*member
,
1487 case eCSSUnit_Percent
:
1488 AddCSSValuePercent(aCoeff1
, pair1
->*member
,
1489 aCoeff2
, pair2
->*member
,
1493 AddCSSValueCanonicalCalc(aCoeff1
, pair1
->*member
,
1494 aCoeff2
, pair2
->*member
,
1498 NS_ABORT_IF_FALSE(PR_FALSE
, "unexpected unit");
1503 aResultValue
.SetAndAdoptCSSValuePairValue(result
.forget(),
1504 eUnit_CSSValuePair
);
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()
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
,
1530 if (float(aCoeff1
+ aCoeff2
) != 1.0f
) {
1531 // Interpolating between two auto values makes sense;
1532 // adding in other ratios does not.
1535 (result
->*member
).SetAutoValue();
1538 NS_ABORT_IF_FALSE(PR_FALSE
, "unexpected unit");
1543 aResultValue
.SetAndAdoptCSSRectValue(result
.forget(), eUnit_CSSRect
);
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
) {
1554 for (const nsCSSValueList
*v
= list2
; v
; v
= v
->mNext
) {
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.
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");
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()).
1582 nsCSSValueList
*item
= new nsCSSValueList
;
1587 resultTail
= &item
->mNext
;
1589 if (v1
.GetUnit() == eCSSUnit_Number
) {
1590 AddCSSValueNumber(aCoeff1
, v1
, aCoeff2
, v2
, item
->mValue
);
1592 AddCSSValuePercent(aCoeff1
, v1
, aCoeff2
, v2
, item
->mValue
);
1595 list1
= list1
->mNext
;
1597 list1
= aValue1
.GetCSSValueListValue();
1599 list2
= list2
->mNext
;
1601 list2
= aValue2
.GetCSSValueListValue();
1605 aResultValue
.SetAndAdoptCSSValueListValue(result
.forget(),
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
,
1624 shadow1
= shadow1
->mNext
;
1625 shadow2
= shadow2
->mNext
;
1627 if (shadow1
|| shadow2
) {
1628 const nsCSSValueList
*longShadow
;
1631 longShadow
= shadow1
;
1632 longCoeff
= aCoeff1
;
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
,
1648 longShadow
= longShadow
->mNext
;
1651 aResultValue
.SetAndAdoptCSSValueListValue(result
.forget(), eUnit_Shadow
);
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
1662 // (1) if one of the transforms is 'none'
1663 // (2) if the lists have the same length and the transform
1665 nsAutoPtr
<nsCSSValueList
> result
;
1666 if (list1
->mValue
.GetUnit() == eCSSUnit_None
) {
1667 if (list2
->mValue
.GetUnit() == eCSSUnit_None
) {
1668 result
= new nsCSSValueList
;
1670 result
->mValue
.SetNoneValue();
1673 result
= AddTransformLists(list2
, aCoeff2
, list2
, 0);
1676 if (list2
->mValue
.GetUnit() == eCSSUnit_None
) {
1677 result
= AddTransformLists(list1
, aCoeff1
, list1
, 0);
1679 PRBool match
= PR_TRUE
;
1682 const nsCSSValueList
*item1
= list1
, *item2
= list2
;
1684 nsCSSKeyword func1
= nsStyleTransformMatrix::TransformFunctionOf(
1685 item1
->mValue
.GetArrayValue());
1686 nsCSSKeyword func2
= nsStyleTransformMatrix::TransformFunctionOf(
1687 item2
->mValue
.GetArrayValue());
1688 if (func1
!= func2
) {
1692 item1
= item1
->mNext
;
1693 item2
= item2
->mNext
;
1694 } while (item1
&& item2
);
1695 if (item1
|| item2
) {
1696 // Either |break| above or length mismatch.
1702 result
= AddTransformLists(list1
, aCoeff1
, list2
, aCoeff2
);
1705 nsStyleTransformMatrix matrix1
=
1706 nsStyleTransformMatrix::ReadTransforms(list1
, nsnull
, nsnull
,
1709 nsStyleTransformMatrix::ReadTransforms(list2
, nsnull
, nsnull
,
1711 result
= AddTransformMatrix(matrix1
, aCoeff1
, matrix2
, aCoeff2
);
1716 aResultValue
.SetAndAdoptCSSValueListValue(result
.forget(),
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
);
1726 nsCSSValuePairList
*item
= new nsCSSValuePairList
;
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
]);
1742 GetCommonUnit(aProperty
, v1
.GetUnit(), v2
.GetUnit());
1743 if (unit
== eCSSUnit_Null
) {
1747 case eCSSUnit_Pixel
:
1748 AddCSSValuePixel(aCoeff1
, v1
, aCoeff2
, v2
, vr
);
1750 case eCSSUnit_Percent
:
1751 AddCSSValuePercent(aCoeff1
, v1
, aCoeff2
, v2
, vr
);
1754 AddCSSValueCanonicalCalc(aCoeff1
, v1
, aCoeff2
, v2
, vr
);
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.
1772 aResultValue
.SetAndAdoptCSSValuePairListValue(result
.forget());
1777 NS_ABORT_IF_FALSE(false, "Can't interpolate using the given common unit");
1781 already_AddRefed
<nsICSSStyleRule
>
1782 BuildStyleRule(nsCSSProperty aProperty
,
1783 nsIContent
* aTargetElement
,
1784 const nsAString
& aSpecifiedValue
,
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());
1798 parser
.SetSVGMode(PR_TRUE
);
1800 NS_NOTREACHED("aUseSVGMode should not be set");
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.
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");
1820 return NS_NewCSSStyleRule(nsnull
, declaration
.forget());
1824 already_AddRefed
<nsStyleContext
>
1825 LookupStyleContext(nsIContent
* aElement
)
1827 nsIDocument
* doc
= aElement
->GetCurrentDoc();
1828 nsIPresShell
* shell
= doc
->GetShell();
1832 return nsComputedDOMStyle::GetStyleContextForElement(aElement
->AsElement(),
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
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
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
,
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
) {
1873 // Parse specified value into a temporary nsICSSStyleRule
1874 nsCOMPtr
<nsICSSStyleRule
> styleRule
=
1875 BuildStyleRule(aProperty
, aTargetElement
, aSpecifiedValue
, aUseSVGMode
);
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
);
1890 nsStyleAnimation::ComputeValue(nsCSSProperty aProperty
,
1891 nsIContent
* aTargetElement
,
1892 const nsAString
& aSpecifiedValue
,
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
) {
1913 if (nsCSSProps::IsShorthand(aProperty
) ||
1914 nsCSSProps::kAnimTypeTable
[aProperty
] == eStyleAnimType_None
) {
1915 // Just capture the specified value
1916 aComputedValue
.SetUnparsedStringValue(nsString(aSpecifiedValue
));
1919 // Extract computed value of our property from the temporary style rule
1920 return ExtractComputedValue(aProperty
, tmpStyleContext
, aComputedValue
);
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()) {
1933 aSpecifiedValue
.SetNormalValue();
1936 aSpecifiedValue
.SetAutoValue();
1939 aSpecifiedValue
.SetNoneValue();
1941 case eUnit_Enumerated
:
1942 case eUnit_Visibility
:
1944 SetIntValue(aComputedValue
.GetIntValue(), eCSSUnit_Enumerated
);
1948 SetIntValue(aComputedValue
.GetIntValue(), eCSSUnit_Integer
);
1951 nscoordToCSSValue(aComputedValue
.GetCoordValue(), aSpecifiedValue
);
1954 aSpecifiedValue
.SetPercentValue(aComputedValue
.GetPercentValue());
1958 SetFloatValue(aComputedValue
.GetFloatValue(), eCSSUnit_Number
);
1961 // colors can be alone, or part of a paint server
1962 aSpecifiedValue
.SetColorValue(aComputedValue
.GetColorValue());
1965 nsCSSValue
*val
= aComputedValue
.GetCSSValueValue();
1966 NS_ABORT_IF_FALSE(val
->GetUnit() == eCSSUnit_Calc
, "unexpected unit");
1967 aSpecifiedValue
= *val
;
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
;
1979 aSpecifiedValue
.SetPairValue(pair
);
1982 case eUnit_CSSRect
: {
1983 nsCSSRect
& rect
= aSpecifiedValue
.SetRectValue();
1984 rect
= *aComputedValue
.GetCSSRectValue();
1986 case eUnit_Dasharray
:
1988 case eUnit_Transform
:
1990 SetDependentListValue(aComputedValue
.GetCSSValueListValue());
1992 case eUnit_CSSValuePairList
:
1994 SetDependentPairListValue(aComputedValue
.GetCSSValuePairListValue());
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
);
2016 if (!nsStyleAnimation::UncomputeValue(aProperty
, aPresContext
,
2017 aComputedValue
, val
)) {
2021 val
.AppendToString(aProperty
, aSpecifiedValue
);
2026 StyleDataAtOffset(const void* aStyleStruct
, ptrdiff_t aOffset
)
2028 return reinterpret_cast<const char*>(aStyleStruct
) + aOffset
;
2032 StyleDataAtOffset(void* aStyleStruct
, ptrdiff_t aOffset
)
2034 return reinterpret_cast<char*>(aStyleStruct
) + aOffset
;
2038 ExtractBorderColor(nsStyleContext
* aStyleContext
, const void* aStyleBorder
,
2039 mozilla::css::Side aSide
, nsStyleAnimation::Value
& aComputedValue
)
2043 static_cast<const nsStyleBorder
*>(aStyleBorder
)->
2044 GetBorderColor(aSide
, color
, foreground
);
2046 // FIXME: should add test for this
2047 color
= aStyleContext
->GetStyleColor()->mColor
;
2049 aComputedValue
.SetColorValue(color
);
2053 StyleCoordToValue(const nsStyleCoord
& aCoord
, nsStyleAnimation::Value
& aValue
)
2055 switch (aCoord
.GetUnit()) {
2056 case eStyleUnit_Normal
:
2057 aValue
.SetNormalValue();
2059 case eStyleUnit_Auto
:
2060 aValue
.SetAutoValue();
2062 case eStyleUnit_None
:
2063 aValue
.SetNoneValue();
2065 case eStyleUnit_Percent
:
2066 aValue
.SetPercentValue(aCoord
.GetPercentValue());
2068 case eStyleUnit_Factor
:
2069 aValue
.SetFloatValue(aCoord
.GetFactorValue());
2071 case eStyleUnit_Coord
:
2072 aValue
.SetCoordValue(aCoord
.GetCoordValue());
2074 case eStyleUnit_Enumerated
:
2075 aValue
.SetIntValue(aCoord
.GetIntValue(),
2076 nsStyleAnimation::eUnit_Enumerated
);
2078 case eStyleUnit_Integer
:
2079 aValue
.SetIntValue(aCoord
.GetIntValue(),
2080 nsStyleAnimation::eUnit_Integer
);
2082 case eStyleUnit_Calc
: {
2083 nsAutoPtr
<nsCSSValue
> val(new nsCSSValue
);
2084 SetCalcValue(aCoord
.GetCalcValue(), *val
);
2085 aValue
.SetAndAdoptCSSValueValue(val
.forget(),
2086 nsStyleAnimation::eUnit_Calc
);
2096 StyleCoordToCSSValue(const nsStyleCoord
& aCoord
, nsCSSValue
& aCSSValue
)
2098 switch (aCoord
.GetUnit()) {
2099 case eStyleUnit_Coord
:
2100 nscoordToCSSValue(aCoord
.GetCoordValue(), aCSSValue
);
2102 case eStyleUnit_Percent
:
2103 aCSSValue
.SetPercentValue(aCoord
.GetPercentValue());
2105 case eStyleUnit_Calc
:
2106 SetCalcValue(aCoord
.GetCalcValue(), aCSSValue
);
2109 NS_ABORT_IF_FALSE(PR_FALSE
, "unexpected unit");
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.
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
),
2158 nsStyleAnimation::ExtractComputedValue(nsCSSProperty aProperty
,
2159 nsStyleContext
* aStyleContext
,
2160 Value
& aComputedValue
)
2162 NS_ABORT_IF_FALSE(0 <= aProperty
&&
2163 aProperty
< eCSSProperty_COUNT_no_shorthands
,
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");
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_) \
2180 aComputedValue.SetCoordValue( \
2181 static_cast<const nsStyleBorder*>(styleStruct)-> \
2182 GetComputedBorder().side_); \
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());
2196 case eCSSProperty_border_bottom_color
:
2197 ExtractBorderColor(aStyleContext
, styleStruct
, NS_SIDE_BOTTOM
,
2200 case eCSSProperty_border_left_color_value
:
2201 ExtractBorderColor(aStyleContext
, styleStruct
, NS_SIDE_LEFT
,
2204 case eCSSProperty_border_right_color_value
:
2205 ExtractBorderColor(aStyleContext
, styleStruct
, NS_SIDE_RIGHT
,
2208 case eCSSProperty_border_top_color
:
2209 ExtractBorderColor(aStyleContext
, styleStruct
, NS_SIDE_TOP
,
2213 case eCSSProperty_outline_color
: {
2214 const nsStyleOutline
*styleOutline
=
2215 static_cast<const nsStyleOutline
*>(styleStruct
);
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
);
2222 if (!styleOutline
->GetOutlineColor(color
))
2223 color
= aStyleContext
->GetStyleColor()->mColor
;
2225 aComputedValue
.SetColorValue(color
);
2229 case eCSSProperty__moz_column_rule_color
: {
2230 const nsStyleColumn
*styleColumn
=
2231 static_cast<const nsStyleColumn
*>(styleStruct
);
2233 if (styleColumn
->mColumnRuleColorIsForeground
) {
2234 color
= aStyleContext
->GetStyleColor()->mColor
;
2236 color
= styleColumn
->mColumnRuleColor
;
2238 aComputedValue
.SetColorValue(color
);
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();
2248 aComputedValue
.SetIntValue(styleColumn
->mColumnCount
,
2254 case eCSSProperty_border_spacing
: {
2255 const nsStyleTableBorder
*styleTableBorder
=
2256 static_cast<const nsStyleTableBorder
*>(styleStruct
);
2257 nsAutoPtr
<nsCSSValuePair
> pair(new nsCSSValuePair
);
2261 nscoordToCSSValue(styleTableBorder
->mBorderSpacingX
, pair
->mXValue
);
2262 nscoordToCSSValue(styleTableBorder
->mBorderSpacingY
, pair
->mYValue
);
2263 aComputedValue
.SetAndAdoptCSSValuePairValue(pair
.forget(),
2264 eUnit_CSSValuePair
);
2268 case eCSSProperty__moz_transform_origin
: {
2269 const nsStyleDisplay
*styleDisplay
=
2270 static_cast<const nsStyleDisplay
*>(styleStruct
);
2271 nsAutoPtr
<nsCSSValuePair
> pair(new nsCSSValuePair
);
2273 !StyleCoordToCSSValue(styleDisplay
->mTransformOrigin
[0],
2275 !StyleCoordToCSSValue(styleDisplay
->mTransformOrigin
[1],
2279 aComputedValue
.SetAndAdoptCSSValuePairValue(pair
.forget(),
2280 eUnit_CSSValuePair
);
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
;
2296 nsCSSValueList
*item
= new nsCSSValueList
;
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()),
2314 case eStyleUnit_Factor
:
2315 value
.SetFloatValue(coord
.GetFactorValue(),
2318 case eStyleUnit_Percent
:
2319 value
.SetPercentValue(coord
.GetPercentValue());
2322 NS_ABORT_IF_FALSE(PR_FALSE
, "unexpected unit");
2327 result
= new nsCSSValueList
;
2331 result
->mValue
.SetNoneValue();
2333 aComputedValue
.SetAndAdoptCSSValueListValue(result
.forget(),
2338 case eCSSProperty_font_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
) {
2347 aComputedValue
.SetIntValue(stretch
, eUnit_Enumerated
);
2351 case eCSSProperty_font_weight
: {
2353 static_cast<const nsStyleFont
*>(styleStruct
)->mFont
.weight
;
2354 if (weight
% 100 != 0) {
2357 aComputedValue
.SetIntValue(weight
, eUnit_Integer
);
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();
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
);
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();
2385 nsCSSRect
*vrect
= new nsCSSRect
;
2386 const nsRect
&srect
= display
->mClip
;
2387 if (display
->mClipFlags
& NS_STYLE_CLIP_TOP_AUTO
) {
2388 vrect
->mTop
.SetAutoValue();
2390 nscoordToCSSValue(srect
.y
, vrect
->mTop
);
2392 if (display
->mClipFlags
& NS_STYLE_CLIP_RIGHT_AUTO
) {
2393 vrect
->mRight
.SetAutoValue();
2395 nscoordToCSSValue(srect
.XMost(), vrect
->mRight
);
2397 if (display
->mClipFlags
& NS_STYLE_CLIP_BOTTOM_AUTO
) {
2398 vrect
->mBottom
.SetAutoValue();
2400 nscoordToCSSValue(srect
.YMost(), vrect
->mBottom
);
2402 if (display
->mClipFlags
& NS_STYLE_CLIP_LEFT_AUTO
) {
2403 vrect
->mLeft
.SetAutoValue();
2405 nscoordToCSSValue(srect
.x
, vrect
->mLeft
);
2407 aComputedValue
.SetAndAdoptCSSRectValue(vrect
, eUnit_CSSRect
);
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
;
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
);
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
);
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());
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
;
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
);
2470 case nsStyleBackground::Size::eAuto
:
2471 item
->mXValue
.SetAutoValue();
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
);
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
);
2488 switch (size
.mHeightType
) {
2489 case nsStyleBackground::Size::eContain
:
2490 case nsStyleBackground::Size::eCover
:
2493 case nsStyleBackground::Size::eAuto
:
2494 item
->mYValue
.SetAutoValue();
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
);
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
);
2512 aComputedValue
.SetAndAdoptCSSValuePairListValue(result
.forget());
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
;
2525 nsCSSValueList
*clone
= new nsCSSValueList
;
2526 *resultTail
= clone
;
2527 resultTail
= &clone
->mNext
;
2529 SubstitutePixelValues(aStyleContext
, l
->mValue
, clone
->mValue
);
2532 result
= new nsCSSValueList();
2533 result
->mValue
.SetNoneValue();
2536 aComputedValue
.SetAndAdoptCSSValueListValue(result
.forget(),
2542 NS_ABORT_IF_FALSE(PR_FALSE
, "missing property implementation");
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
2556 PR_STATIC_ASSERT(eStyleAnimType_Sides_Bottom
- eStyleAnimType_Sides_Top
2558 PR_STATIC_ASSERT(eStyleAnimType_Sides_Left
- eStyleAnimType_Sides_Top
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
);
2588 !StyleCoordToCSSValue(horiz
, pair
->mXValue
) ||
2589 !StyleCoordToCSSValue(vert
, pair
->mYValue
)) {
2592 aComputedValue
.SetAndAdoptCSSValuePairValue(pair
.forget(),
2593 eUnit_CSSValuePair
);
2596 case eStyleAnimType_nscoord
:
2597 aComputedValue
.SetCoordValue(*static_cast<const nscoord
*>(
2598 StyleDataAtOffset(styleStruct
, ssOffset
)));
2600 case eStyleAnimType_EnumU8
:
2601 aComputedValue
.SetIntValue(*static_cast<const PRUint8
*>(
2602 StyleDataAtOffset(styleStruct
, ssOffset
)), eUnit_Enumerated
);
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();
2616 case eStyleAnimType_Color
:
2617 aComputedValue
.SetColorValue(*static_cast<const nscolor
*>(
2618 StyleDataAtOffset(styleStruct
, ssOffset
)));
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
);
2627 if (paint
.mType
== eStyleSVGPaintType_Server
) {
2628 if (!paint
.mPaint
.mPaintServer
) {
2629 NS_WARNING("Null paint server");
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
,
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
);
2648 NS_ABORT_IF_FALSE(paint
.mType
== eStyleSVGPaintType_None
,
2649 "Unexpected SVG paint type");
2650 aComputedValue
.SetNoneValue();
2653 case eStyleAnimType_Shadow
: {
2654 const nsCSSShadowArray
*shadowArray
=
2655 *static_cast<const nsRefPtr
<nsCSSShadowArray
>*>(
2656 StyleDataAtOffset(styleStruct
, ssOffset
));
2658 aComputedValue
.SetAndAdoptCSSValueListValue(nsnull
, eUnit_Shadow
);
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
;
2685 resultItem
->mValue
.SetArrayValue(arr
, eCSSUnit_Array
);
2686 *resultTail
= resultItem
;
2687 resultTail
= &resultItem
->mNext
;
2689 aComputedValue
.SetAndAdoptCSSValueListValue(result
.forget(),
2693 case eStyleAnimType_None
:
2694 NS_NOTREACHED("shouldn't use on non-animatable properties");
2699 nsStyleAnimation::Value::Value(PRInt32 aInt
, Unit aUnit
,
2700 IntegerConstructorType
)
2702 NS_ASSERTION(IsIntUnit(aUnit
), "unit must be of integer type");
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
)
2736 mUnit
= aOther
.mUnit
;
2743 case eUnit_Enumerated
:
2744 case eUnit_Visibility
:
2746 mValue
.mInt
= aOther
.mValue
.mInt
;
2749 mValue
.mCoord
= aOther
.mValue
.mCoord
;
2753 mValue
.mFloat
= aOther
.mValue
.mFloat
;
2756 mValue
.mColor
= aOther
.mValue
.mColor
;
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
) {
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
) {
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
) {
2780 case eUnit_Dasharray
:
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
) {
2791 mValue
.mCSSValueList
= nsnull
;
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
) {
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();
2813 nsStyleAnimation::Value::SetNormalValue()
2816 mUnit
= eUnit_Normal
;
2820 nsStyleAnimation::Value::SetAutoValue()
2827 nsStyleAnimation::Value::SetNoneValue()
2834 nsStyleAnimation::Value::SetIntValue(PRInt32 aInt
, Unit aUnit
)
2836 NS_ASSERTION(IsIntUnit(aUnit
), "unit must be of integer type");
2843 nsStyleAnimation::Value::SetCoordValue(nscoord aLength
)
2846 mUnit
= eUnit_Coord
;
2847 mValue
.mCoord
= aLength
;
2851 nsStyleAnimation::Value::SetPercentValue(float aPercent
)
2854 mUnit
= eUnit_Percent
;
2855 mValue
.mFloat
= aPercent
;
2859 nsStyleAnimation::Value::SetFloatValue(float aFloat
)
2862 mUnit
= eUnit_Float
;
2863 mValue
.mFloat
= aFloat
;
2867 nsStyleAnimation::Value::SetColorValue(nscolor aColor
)
2870 mUnit
= eUnit_Color
;
2871 mValue
.mColor
= aColor
;
2875 nsStyleAnimation::Value::SetUnparsedStringValue(const nsString
& aString
)
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.
2888 nsStyleAnimation::Value::SetAndAdoptCSSValueValue(nsCSSValue
*aValue
,
2892 NS_ABORT_IF_FALSE(IsCSSValueUnit(aUnit
), "bad unit");
2893 NS_ABORT_IF_FALSE(aValue
!= nsnull
, "values may not be null");
2895 mValue
.mCSSValue
= aValue
; // take ownership
2899 nsStyleAnimation::Value::SetAndAdoptCSSValuePairValue(
2900 nsCSSValuePair
*aValuePair
, Unit aUnit
)
2903 NS_ABORT_IF_FALSE(IsCSSValuePairUnit(aUnit
), "bad unit");
2904 NS_ABORT_IF_FALSE(aValuePair
!= nsnull
, "value pairs may not be null");
2906 mValue
.mCSSValuePair
= aValuePair
; // take ownership
2910 nsStyleAnimation::Value::SetAndAdoptCSSRectValue(nsCSSRect
*aRect
, Unit aUnit
)
2913 NS_ABORT_IF_FALSE(IsCSSRectUnit(aUnit
), "bad unit");
2914 NS_ABORT_IF_FALSE(aRect
!= nsnull
, "value pairs may not be null");
2916 mValue
.mCSSRect
= aRect
; // take ownership
2920 nsStyleAnimation::Value::SetAndAdoptCSSValueListValue(
2921 nsCSSValueList
*aValueList
, Unit aUnit
)
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");
2928 mValue
.mCSSValueList
= aValueList
; // take ownership
2932 nsStyleAnimation::Value::SetAndAdoptCSSValuePairListValue(
2933 nsCSSValuePairList
*aValuePairList
)
2936 NS_ABORT_IF_FALSE(aValuePairList
, "may not be null");
2937 mUnit
= eUnit_CSSValuePairList
;
2938 mValue
.mCSSValuePairList
= aValuePairList
; // take ownership
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();
2961 nsStyleAnimation::Value::operator==(const Value
& aOther
) const
2963 if (mUnit
!= aOther
.mUnit
) {
2973 case eUnit_Enumerated
:
2974 case eUnit_Visibility
:
2976 return mValue
.mInt
== aOther
.mValue
.mInt
;
2978 return mValue
.mCoord
== aOther
.mValue
.mCoord
;
2981 return mValue
.mFloat
== aOther
.mValue
.mFloat
;
2983 return mValue
.mColor
== aOther
.mValue
.mColor
;
2985 return *mValue
.mCSSValue
== *aOther
.mValue
.mCSSValue
;
2986 case eUnit_CSSValuePair
:
2987 return *mValue
.mCSSValuePair
== *aOther
.mValue
.mCSSValuePair
;
2989 return *mValue
.mCSSRect
== *aOther
.mValue
.mCSSRect
;
2990 case eUnit_Dasharray
:
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");