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