layout/style/nsStyleAnimation.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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

mercurial