Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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 |