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