michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* Utilities for animation of computed style values */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/MathAlgorithms.h" michael@0: michael@0: #include "nsStyleAnimation.h" michael@0: #include "nsStyleTransformMatrix.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsIStyleRule.h" michael@0: #include "mozilla/css/StyleRule.h" michael@0: #include "nsString.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsComputedDOMStyle.h" michael@0: #include "nsCSSParser.h" michael@0: #include "mozilla/css/Declaration.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/FloatingPoint.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "gfxMatrix.h" michael@0: #include "gfxQuaternion.h" michael@0: #include "nsIDocument.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // HELPER METHODS michael@0: // -------------- michael@0: /* michael@0: * Given two units, this method returns a common unit that they can both be michael@0: * converted into, if possible. This is intended to facilitate michael@0: * interpolation, distance-computation, and addition between "similar" units. michael@0: * michael@0: * The ordering of the arguments should not affect the output of this method. michael@0: * michael@0: * If there's no sensible common unit, this method returns eUnit_Null. michael@0: * michael@0: * @param aFirstUnit One unit to resolve. michael@0: * @param aFirstUnit The other unit to resolve. michael@0: * @return A "common" unit that both source units can be converted into, or michael@0: * eUnit_Null if that's not possible. michael@0: */ michael@0: static michael@0: nsStyleAnimation::Unit michael@0: GetCommonUnit(nsCSSProperty aProperty, michael@0: nsStyleAnimation::Unit aFirstUnit, michael@0: nsStyleAnimation::Unit aSecondUnit) michael@0: { michael@0: if (aFirstUnit != aSecondUnit) { michael@0: if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) && michael@0: (aFirstUnit == nsStyleAnimation::eUnit_Coord || michael@0: aFirstUnit == nsStyleAnimation::eUnit_Percent || michael@0: aFirstUnit == nsStyleAnimation::eUnit_Calc) && michael@0: (aSecondUnit == nsStyleAnimation::eUnit_Coord || michael@0: aSecondUnit == nsStyleAnimation::eUnit_Percent || michael@0: aSecondUnit == nsStyleAnimation::eUnit_Calc)) { michael@0: // We can use calc() as the common unit. michael@0: return nsStyleAnimation::eUnit_Calc; michael@0: } michael@0: return nsStyleAnimation::eUnit_Null; michael@0: } michael@0: return aFirstUnit; michael@0: } michael@0: michael@0: static michael@0: nsCSSUnit michael@0: GetCommonUnit(nsCSSProperty aProperty, michael@0: nsCSSUnit aFirstUnit, michael@0: nsCSSUnit aSecondUnit) michael@0: { michael@0: if (aFirstUnit != aSecondUnit) { michael@0: if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) && michael@0: (aFirstUnit == eCSSUnit_Pixel || michael@0: aFirstUnit == eCSSUnit_Percent || michael@0: aFirstUnit == eCSSUnit_Calc) && michael@0: (aSecondUnit == eCSSUnit_Pixel || michael@0: aSecondUnit == eCSSUnit_Percent || michael@0: aSecondUnit == eCSSUnit_Calc)) { michael@0: // We can use calc() as the common unit. michael@0: return eCSSUnit_Calc; michael@0: } michael@0: return eCSSUnit_Null; michael@0: } michael@0: return aFirstUnit; michael@0: } michael@0: michael@0: static nsCSSKeyword michael@0: ToPrimitive(nsCSSKeyword aKeyword) michael@0: { michael@0: switch (aKeyword) { michael@0: case eCSSKeyword_translatex: michael@0: case eCSSKeyword_translatey: michael@0: case eCSSKeyword_translatez: michael@0: case eCSSKeyword_translate: michael@0: return eCSSKeyword_translate3d; michael@0: case eCSSKeyword_scalex: michael@0: case eCSSKeyword_scaley: michael@0: case eCSSKeyword_scalez: michael@0: case eCSSKeyword_scale: michael@0: return eCSSKeyword_scale3d; michael@0: default: michael@0: return aKeyword; michael@0: } michael@0: } michael@0: michael@0: static already_AddRefed michael@0: AppendFunction(nsCSSKeyword aTransformFunction) michael@0: { michael@0: uint32_t nargs; michael@0: switch (aTransformFunction) { michael@0: case eCSSKeyword_matrix3d: michael@0: nargs = 16; michael@0: break; michael@0: case eCSSKeyword_matrix: michael@0: nargs = 6; michael@0: break; michael@0: case eCSSKeyword_rotate3d: michael@0: nargs = 4; michael@0: break; michael@0: case eCSSKeyword_interpolatematrix: michael@0: case eCSSKeyword_translate3d: michael@0: case eCSSKeyword_scale3d: michael@0: nargs = 3; michael@0: break; michael@0: case eCSSKeyword_translate: michael@0: case eCSSKeyword_skew: michael@0: case eCSSKeyword_scale: michael@0: nargs = 2; michael@0: break; michael@0: default: michael@0: NS_ERROR("must be a transform function"); michael@0: case eCSSKeyword_translatex: michael@0: case eCSSKeyword_translatey: michael@0: case eCSSKeyword_translatez: michael@0: case eCSSKeyword_scalex: michael@0: case eCSSKeyword_scaley: michael@0: case eCSSKeyword_scalez: michael@0: case eCSSKeyword_skewx: michael@0: case eCSSKeyword_skewy: michael@0: case eCSSKeyword_rotate: michael@0: case eCSSKeyword_rotatex: michael@0: case eCSSKeyword_rotatey: michael@0: case eCSSKeyword_rotatez: michael@0: case eCSSKeyword_perspective: michael@0: nargs = 1; michael@0: break; michael@0: } michael@0: michael@0: nsRefPtr arr = nsCSSValue::Array::Create(nargs + 1); michael@0: arr->Item(0).SetIntValue(aTransformFunction, eCSSUnit_Enumerated); michael@0: michael@0: return arr.forget(); michael@0: } michael@0: michael@0: static already_AddRefed michael@0: ToPrimitive(nsCSSValue::Array* aArray) michael@0: { michael@0: nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(aArray); michael@0: nsCSSKeyword primitive = ToPrimitive(tfunc); michael@0: nsRefPtr arr = AppendFunction(primitive); michael@0: michael@0: // FIXME: This would produce fewer calc() expressions if the michael@0: // zero were of compatible type (length vs. percent) when michael@0: // needed. michael@0: michael@0: nsCSSValue zero(0.0f, eCSSUnit_Pixel); michael@0: nsCSSValue one(1.0f, eCSSUnit_Number); michael@0: switch(tfunc) { michael@0: case eCSSKeyword_translate: michael@0: { michael@0: NS_ABORT_IF_FALSE(aArray->Count() == 2 || aArray->Count() == 3, michael@0: "unexpected count"); michael@0: arr->Item(1) = aArray->Item(1); michael@0: arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : zero; michael@0: arr->Item(3) = zero; michael@0: break; michael@0: } michael@0: case eCSSKeyword_translatex: michael@0: { michael@0: NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count"); michael@0: arr->Item(1) = aArray->Item(1); michael@0: arr->Item(2) = zero; michael@0: arr->Item(3) = zero; michael@0: break; michael@0: } michael@0: case eCSSKeyword_translatey: michael@0: { michael@0: NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count"); michael@0: arr->Item(1) = zero; michael@0: arr->Item(2) = aArray->Item(1); michael@0: arr->Item(3) = zero; michael@0: break; michael@0: } michael@0: case eCSSKeyword_translatez: michael@0: { michael@0: NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count"); michael@0: arr->Item(1) = zero; michael@0: arr->Item(2) = zero; michael@0: arr->Item(3) = aArray->Item(1); michael@0: break; michael@0: } michael@0: case eCSSKeyword_scale: michael@0: { michael@0: NS_ABORT_IF_FALSE(aArray->Count() == 2 || aArray->Count() == 3, michael@0: "unexpected count"); michael@0: arr->Item(1) = aArray->Item(1); michael@0: arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : aArray->Item(1); michael@0: arr->Item(3) = one; michael@0: break; michael@0: } michael@0: case eCSSKeyword_scalex: michael@0: { michael@0: NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count"); michael@0: arr->Item(1) = aArray->Item(1); michael@0: arr->Item(2) = one; michael@0: arr->Item(3) = one; michael@0: break; michael@0: } michael@0: case eCSSKeyword_scaley: michael@0: { michael@0: NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count"); michael@0: arr->Item(1) = one; michael@0: arr->Item(2) = aArray->Item(1); michael@0: arr->Item(3) = one; michael@0: break; michael@0: } michael@0: case eCSSKeyword_scalez: michael@0: { michael@0: NS_ABORT_IF_FALSE(aArray->Count() == 2, "unexpected count"); michael@0: arr->Item(1) = one; michael@0: arr->Item(2) = one; michael@0: arr->Item(3) = aArray->Item(1); michael@0: break; michael@0: } michael@0: default: michael@0: arr = aArray; michael@0: } michael@0: return arr.forget(); michael@0: } michael@0: michael@0: inline void michael@0: nscoordToCSSValue(nscoord aCoord, nsCSSValue& aCSSValue) michael@0: { michael@0: aCSSValue.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(aCoord), michael@0: eCSSUnit_Pixel); michael@0: } michael@0: michael@0: static void michael@0: AppendCSSShadowValue(const nsCSSShadowItem *aShadow, michael@0: nsCSSValueList **&aResultTail) michael@0: { michael@0: NS_ABORT_IF_FALSE(aShadow, "shadow expected"); michael@0: michael@0: // X, Y, Radius, Spread, Color, Inset michael@0: nsRefPtr arr = nsCSSValue::Array::Create(6); michael@0: nscoordToCSSValue(aShadow->mXOffset, arr->Item(0)); michael@0: nscoordToCSSValue(aShadow->mYOffset, arr->Item(1)); michael@0: nscoordToCSSValue(aShadow->mRadius, arr->Item(2)); michael@0: // NOTE: This code sometimes stores mSpread: 0 even when michael@0: // the parser would be required to leave it null. michael@0: nscoordToCSSValue(aShadow->mSpread, arr->Item(3)); michael@0: if (aShadow->mHasColor) { michael@0: arr->Item(4).SetColorValue(aShadow->mColor); michael@0: } michael@0: if (aShadow->mInset) { michael@0: arr->Item(5).SetIntValue(NS_STYLE_BOX_SHADOW_INSET, michael@0: eCSSUnit_Enumerated); michael@0: } michael@0: michael@0: nsCSSValueList *resultItem = new nsCSSValueList; michael@0: resultItem->mValue.SetArrayValue(arr, eCSSUnit_Array); michael@0: *aResultTail = resultItem; michael@0: aResultTail = &resultItem->mNext; michael@0: } michael@0: michael@0: // Like nsStyleCoord::Calc, but with length in float pixels instead of nscoord. michael@0: struct CalcValue { michael@0: float mLength, mPercent; michael@0: bool mHasPercent; michael@0: }; michael@0: michael@0: // Requires a canonical calc() value that we generated. michael@0: static CalcValue michael@0: ExtractCalcValueInternal(const nsCSSValue& aValue) michael@0: { michael@0: NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Calc, "unexpected unit"); michael@0: nsCSSValue::Array *arr = aValue.GetArrayValue(); michael@0: NS_ABORT_IF_FALSE(arr->Count() == 1, "unexpected length"); michael@0: michael@0: const nsCSSValue &topval = arr->Item(0); michael@0: CalcValue result; michael@0: if (topval.GetUnit() == eCSSUnit_Pixel) { michael@0: result.mLength = topval.GetFloatValue(); michael@0: result.mPercent = 0.0f; michael@0: result.mHasPercent = false; michael@0: } else { michael@0: NS_ABORT_IF_FALSE(topval.GetUnit() == eCSSUnit_Calc_Plus, michael@0: "unexpected unit"); michael@0: nsCSSValue::Array *arr2 = topval.GetArrayValue(); michael@0: const nsCSSValue &len = arr2->Item(0); michael@0: const nsCSSValue &pct = arr2->Item(1); michael@0: NS_ABORT_IF_FALSE(len.GetUnit() == eCSSUnit_Pixel, "unexpected unit"); michael@0: NS_ABORT_IF_FALSE(pct.GetUnit() == eCSSUnit_Percent, "unexpected unit"); michael@0: result.mLength = len.GetFloatValue(); michael@0: result.mPercent = pct.GetPercentValue(); michael@0: result.mHasPercent = true; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: // Requires a canonical calc() value that we generated. michael@0: static CalcValue michael@0: ExtractCalcValue(const nsStyleAnimation::Value& aValue) michael@0: { michael@0: CalcValue result; michael@0: if (aValue.GetUnit() == nsStyleAnimation::eUnit_Coord) { michael@0: result.mLength = michael@0: nsPresContext::AppUnitsToFloatCSSPixels(aValue.GetCoordValue()); michael@0: result.mPercent = 0.0f; michael@0: result.mHasPercent = false; michael@0: return result; michael@0: } michael@0: if (aValue.GetUnit() == nsStyleAnimation::eUnit_Percent) { michael@0: result.mLength = 0.0f; michael@0: result.mPercent = aValue.GetPercentValue(); michael@0: result.mHasPercent = true; michael@0: return result; michael@0: } michael@0: NS_ABORT_IF_FALSE(aValue.GetUnit() == nsStyleAnimation::eUnit_Calc, michael@0: "unexpected unit"); michael@0: nsCSSValue *val = aValue.GetCSSValueValue(); michael@0: return ExtractCalcValueInternal(*val); michael@0: } michael@0: michael@0: static CalcValue michael@0: ExtractCalcValue(const nsCSSValue& aValue) michael@0: { michael@0: CalcValue result; michael@0: if (aValue.GetUnit() == eCSSUnit_Pixel) { michael@0: result.mLength = aValue.GetFloatValue(); michael@0: result.mPercent = 0.0f; michael@0: result.mHasPercent = false; michael@0: return result; michael@0: } michael@0: if (aValue.GetUnit() == eCSSUnit_Percent) { michael@0: result.mLength = 0.0f; michael@0: result.mPercent = aValue.GetPercentValue(); michael@0: result.mHasPercent = true; michael@0: return result; michael@0: } michael@0: return ExtractCalcValueInternal(aValue); michael@0: } michael@0: michael@0: static void michael@0: SetCalcValue(const nsStyleCoord::Calc* aCalc, nsCSSValue& aValue) michael@0: { michael@0: nsRefPtr arr = nsCSSValue::Array::Create(1); michael@0: if (!aCalc->mHasPercent) { michael@0: nscoordToCSSValue(aCalc->mLength, arr->Item(0)); michael@0: } else { michael@0: nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2); michael@0: arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus); michael@0: nscoordToCSSValue(aCalc->mLength, arr2->Item(0)); michael@0: arr2->Item(1).SetPercentValue(aCalc->mPercent); michael@0: } michael@0: michael@0: aValue.SetArrayValue(arr, eCSSUnit_Calc); michael@0: } michael@0: michael@0: static void michael@0: SetCalcValue(const CalcValue& aCalc, nsCSSValue& aValue) michael@0: { michael@0: nsRefPtr arr = nsCSSValue::Array::Create(1); michael@0: if (!aCalc.mHasPercent) { michael@0: arr->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel); michael@0: } else { michael@0: nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2); michael@0: arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus); michael@0: arr2->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel); michael@0: arr2->Item(1).SetPercentValue(aCalc.mPercent); michael@0: } michael@0: michael@0: aValue.SetArrayValue(arr, eCSSUnit_Calc); michael@0: } michael@0: michael@0: static already_AddRefed michael@0: GetURIAsUtf16StringBuffer(nsIURI* aUri) michael@0: { michael@0: nsAutoCString utf8String; michael@0: nsresult rv = aUri->GetSpec(utf8String); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: return nsCSSValue::BufferFromString(NS_ConvertUTF8toUTF16(utf8String)); michael@0: } michael@0: michael@0: // CLASS METHODS michael@0: // ------------- michael@0: michael@0: bool michael@0: nsStyleAnimation::ComputeDistance(nsCSSProperty aProperty, michael@0: const Value& aStartValue, michael@0: const Value& aEndValue, michael@0: double& aDistance) michael@0: { michael@0: Unit commonUnit = michael@0: GetCommonUnit(aProperty, aStartValue.GetUnit(), aEndValue.GetUnit()); michael@0: michael@0: switch (commonUnit) { michael@0: case eUnit_Null: michael@0: case eUnit_Auto: michael@0: case eUnit_None: michael@0: case eUnit_Normal: michael@0: case eUnit_UnparsedString: michael@0: return false; michael@0: michael@0: case eUnit_Enumerated: michael@0: switch (aProperty) { michael@0: case eCSSProperty_font_stretch: { michael@0: // just like eUnit_Integer. michael@0: int32_t startInt = aStartValue.GetIntValue(); michael@0: int32_t endInt = aEndValue.GetIntValue(); michael@0: aDistance = Abs(endInt - startInt); michael@0: return true; michael@0: } michael@0: default: michael@0: return false; michael@0: } michael@0: case eUnit_Visibility: { michael@0: int32_t startEnum = aStartValue.GetIntValue(); michael@0: int32_t endEnum = aEndValue.GetIntValue(); michael@0: if (startEnum == endEnum) { michael@0: aDistance = 0; michael@0: return true; michael@0: } michael@0: if ((startEnum == NS_STYLE_VISIBILITY_VISIBLE) == michael@0: (endEnum == NS_STYLE_VISIBILITY_VISIBLE)) { michael@0: return false; michael@0: } michael@0: aDistance = 1; michael@0: return true; michael@0: } michael@0: case eUnit_Integer: { michael@0: int32_t startInt = aStartValue.GetIntValue(); michael@0: int32_t endInt = aEndValue.GetIntValue(); michael@0: aDistance = Abs(double(endInt) - double(startInt)); michael@0: return true; michael@0: } michael@0: case eUnit_Coord: { michael@0: nscoord startCoord = aStartValue.GetCoordValue(); michael@0: nscoord endCoord = aEndValue.GetCoordValue(); michael@0: aDistance = Abs(double(endCoord) - double(startCoord)); michael@0: return true; michael@0: } michael@0: case eUnit_Percent: { michael@0: float startPct = aStartValue.GetPercentValue(); michael@0: float endPct = aEndValue.GetPercentValue(); michael@0: aDistance = Abs(double(endPct) - double(startPct)); michael@0: return true; michael@0: } michael@0: case eUnit_Float: { michael@0: // Special case for flex-grow and flex-shrink: animations are michael@0: // disallowed between 0 and other values. michael@0: if ((aProperty == eCSSProperty_flex_grow || michael@0: aProperty == eCSSProperty_flex_shrink) && michael@0: (aStartValue.GetFloatValue() == 0.0f || michael@0: aEndValue.GetFloatValue() == 0.0f) && michael@0: aStartValue.GetFloatValue() != aEndValue.GetFloatValue()) { michael@0: return false; michael@0: } michael@0: michael@0: float startFloat = aStartValue.GetFloatValue(); michael@0: float endFloat = aEndValue.GetFloatValue(); michael@0: aDistance = Abs(double(endFloat) - double(startFloat)); michael@0: return true; michael@0: } michael@0: case eUnit_Color: { michael@0: // http://www.w3.org/TR/smil-animation/#animateColorElement says michael@0: // that we should use Euclidean RGB cube distance. However, we michael@0: // have to extend that to RGBA. For now, we'll just use the michael@0: // Euclidean distance in the (part of the) 4-cube of premultiplied michael@0: // colors. michael@0: // FIXME (spec): The CSS transitions spec doesn't say whether michael@0: // colors are premultiplied, but things work better when they are, michael@0: // so use premultiplication. Spec issue is still open per michael@0: // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html michael@0: nscolor startColor = aStartValue.GetColorValue(); michael@0: nscolor endColor = aEndValue.GetColorValue(); michael@0: michael@0: // Get a color component on a 0-1 scale, which is much easier to michael@0: // deal with when working with alpha. michael@0: #define GET_COMPONENT(component_, color_) \ michael@0: (NS_GET_##component_(color_) * (1.0 / 255.0)) michael@0: michael@0: double startA = GET_COMPONENT(A, startColor); michael@0: double startR = GET_COMPONENT(R, startColor) * startA; michael@0: double startG = GET_COMPONENT(G, startColor) * startA; michael@0: double startB = GET_COMPONENT(B, startColor) * startA; michael@0: double endA = GET_COMPONENT(A, endColor); michael@0: double endR = GET_COMPONENT(R, endColor) * endA; michael@0: double endG = GET_COMPONENT(G, endColor) * endA; michael@0: double endB = GET_COMPONENT(B, endColor) * endA; michael@0: michael@0: #undef GET_COMPONENT michael@0: michael@0: double diffA = startA - endA; michael@0: double diffR = startR - endR; michael@0: double diffG = startG - endG; michael@0: double diffB = startB - endB; michael@0: aDistance = sqrt(diffA * diffA + diffR * diffR + michael@0: diffG * diffG + diffB * diffB); michael@0: return true; michael@0: } michael@0: case eUnit_Calc: { michael@0: CalcValue v1 = ExtractCalcValue(aStartValue); michael@0: CalcValue v2 = ExtractCalcValue(aEndValue); michael@0: float difflen = v2.mLength - v1.mLength; michael@0: float diffpct = v2.mPercent - v1.mPercent; michael@0: aDistance = sqrt(difflen * difflen + diffpct * diffpct); michael@0: return true; michael@0: } michael@0: case eUnit_CSSValuePair: { michael@0: const nsCSSValuePair *pair1 = aStartValue.GetCSSValuePairValue(); michael@0: const nsCSSValuePair *pair2 = aEndValue.GetCSSValuePairValue(); michael@0: nsCSSUnit unit[2]; michael@0: unit[0] = GetCommonUnit(aProperty, pair1->mXValue.GetUnit(), michael@0: pair2->mXValue.GetUnit()); michael@0: unit[1] = GetCommonUnit(aProperty, pair1->mYValue.GetUnit(), michael@0: pair2->mYValue.GetUnit()); michael@0: if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null || michael@0: unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) { michael@0: return false; michael@0: } michael@0: michael@0: double squareDistance = 0.0; michael@0: static nsCSSValue nsCSSValuePair::* const pairValues[2] = { michael@0: &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue michael@0: }; michael@0: for (uint32_t i = 0; i < 2; ++i) { michael@0: nsCSSValue nsCSSValuePair::*member = pairValues[i]; michael@0: double diffsquared; michael@0: switch (unit[i]) { michael@0: case eCSSUnit_Pixel: { michael@0: float diff = (pair1->*member).GetFloatValue() - michael@0: (pair2->*member).GetFloatValue(); michael@0: diffsquared = diff * diff; michael@0: break; michael@0: } michael@0: case eCSSUnit_Percent: { michael@0: float diff = (pair1->*member).GetPercentValue() - michael@0: (pair2->*member).GetPercentValue(); michael@0: diffsquared = diff * diff; michael@0: break; michael@0: } michael@0: case eCSSUnit_Calc: { michael@0: CalcValue v1 = ExtractCalcValue(pair1->*member); michael@0: CalcValue v2 = ExtractCalcValue(pair2->*member); michael@0: float difflen = v2.mLength - v1.mLength; michael@0: float diffpct = v2.mPercent - v1.mPercent; michael@0: diffsquared = difflen * difflen + diffpct * diffpct; michael@0: break; michael@0: } michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return false; michael@0: } michael@0: squareDistance += diffsquared; michael@0: } michael@0: michael@0: aDistance = sqrt(squareDistance); michael@0: return true; michael@0: } michael@0: case eUnit_CSSValueTriplet: { michael@0: const nsCSSValueTriplet *triplet1 = aStartValue.GetCSSValueTripletValue(); michael@0: const nsCSSValueTriplet *triplet2 = aEndValue.GetCSSValueTripletValue(); michael@0: nsCSSUnit unit[3]; michael@0: unit[0] = GetCommonUnit(aProperty, triplet1->mXValue.GetUnit(), michael@0: triplet2->mXValue.GetUnit()); michael@0: unit[1] = GetCommonUnit(aProperty, triplet1->mYValue.GetUnit(), michael@0: triplet2->mYValue.GetUnit()); michael@0: unit[2] = GetCommonUnit(aProperty, triplet1->mZValue.GetUnit(), michael@0: triplet2->mZValue.GetUnit()); michael@0: if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null || michael@0: unit[2] == eCSSUnit_Null) { michael@0: return false; michael@0: } michael@0: michael@0: double squareDistance = 0.0; michael@0: static nsCSSValue nsCSSValueTriplet::* const pairValues[3] = { michael@0: &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue michael@0: }; michael@0: for (uint32_t i = 0; i < 3; ++i) { michael@0: nsCSSValue nsCSSValueTriplet::*member = pairValues[i]; michael@0: double diffsquared; michael@0: switch (unit[i]) { michael@0: case eCSSUnit_Pixel: { michael@0: float diff = (triplet1->*member).GetFloatValue() - michael@0: (triplet2->*member).GetFloatValue(); michael@0: diffsquared = diff * diff; michael@0: break; michael@0: } michael@0: case eCSSUnit_Percent: { michael@0: float diff = (triplet1->*member).GetPercentValue() - michael@0: (triplet2->*member).GetPercentValue(); michael@0: diffsquared = diff * diff; michael@0: break; michael@0: } michael@0: case eCSSUnit_Calc: { michael@0: CalcValue v1 = ExtractCalcValue(triplet1->*member); michael@0: CalcValue v2 = ExtractCalcValue(triplet2->*member); michael@0: float difflen = v2.mLength - v1.mLength; michael@0: float diffpct = v2.mPercent - v1.mPercent; michael@0: diffsquared = difflen * difflen + diffpct * diffpct; michael@0: break; michael@0: } michael@0: case eCSSUnit_Null: michael@0: diffsquared = 0; michael@0: break; michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return false; michael@0: } michael@0: squareDistance += diffsquared; michael@0: } michael@0: michael@0: aDistance = sqrt(squareDistance); michael@0: return true; michael@0: } michael@0: case eUnit_CSSRect: { michael@0: const nsCSSRect *rect1 = aStartValue.GetCSSRectValue(); michael@0: const nsCSSRect *rect2 = aEndValue.GetCSSRectValue(); michael@0: if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() || michael@0: rect1->mRight.GetUnit() != rect2->mRight.GetUnit() || michael@0: rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() || michael@0: rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) { michael@0: // At least until we have calc() michael@0: return false; michael@0: } michael@0: michael@0: double squareDistance = 0.0; michael@0: for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) { michael@0: nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i]; michael@0: NS_ABORT_IF_FALSE((rect1->*member).GetUnit() == michael@0: (rect2->*member).GetUnit(), michael@0: "should have returned above"); michael@0: double diff; michael@0: switch ((rect1->*member).GetUnit()) { michael@0: case eCSSUnit_Pixel: michael@0: diff = (rect1->*member).GetFloatValue() - michael@0: (rect2->*member).GetFloatValue(); michael@0: break; michael@0: case eCSSUnit_Auto: michael@0: diff = 0; michael@0: break; michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return false; michael@0: } michael@0: squareDistance += diff * diff; michael@0: } michael@0: michael@0: aDistance = sqrt(squareDistance); michael@0: return true; michael@0: } michael@0: case eUnit_Dasharray: { michael@0: // NOTE: This produces results on substantially different scales michael@0: // for length values and percentage values, which might even be michael@0: // mixed in the same property value. This means the result isn't michael@0: // particularly useful for paced animation. michael@0: michael@0: // Call AddWeighted to make us lists of the same length. michael@0: Value normValue1, normValue2; michael@0: if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue, michael@0: normValue1) || michael@0: !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue, michael@0: normValue2)) { michael@0: return false; michael@0: } michael@0: michael@0: double squareDistance = 0.0; michael@0: const nsCSSValueList *list1 = normValue1.GetCSSValueListValue(); michael@0: const nsCSSValueList *list2 = normValue2.GetCSSValueListValue(); michael@0: michael@0: NS_ABORT_IF_FALSE(!list1 == !list2, "lists should be same length"); michael@0: while (list1) { michael@0: const nsCSSValue &val1 = list1->mValue; michael@0: const nsCSSValue &val2 = list2->mValue; michael@0: michael@0: NS_ABORT_IF_FALSE(val1.GetUnit() == val2.GetUnit(), michael@0: "unit match should be assured by AddWeighted"); michael@0: double diff; michael@0: switch (val1.GetUnit()) { michael@0: case eCSSUnit_Percent: michael@0: diff = val1.GetPercentValue() - val2.GetPercentValue(); michael@0: break; michael@0: case eCSSUnit_Number: michael@0: diff = val1.GetFloatValue() - val2.GetFloatValue(); michael@0: break; michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return false; michael@0: } michael@0: squareDistance += diff * diff; michael@0: michael@0: list1 = list1->mNext; michael@0: list2 = list2->mNext; michael@0: NS_ABORT_IF_FALSE(!list1 == !list2, "lists should be same length"); michael@0: } michael@0: michael@0: aDistance = sqrt(squareDistance); michael@0: return true; michael@0: } michael@0: case eUnit_Shadow: { michael@0: // Call AddWeighted to make us lists of the same length. michael@0: Value normValue1, normValue2; michael@0: if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue, michael@0: normValue1) || michael@0: !AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue, michael@0: normValue2)) { michael@0: return false; michael@0: } michael@0: michael@0: const nsCSSValueList *shadow1 = normValue1.GetCSSValueListValue(); michael@0: const nsCSSValueList *shadow2 = normValue2.GetCSSValueListValue(); michael@0: michael@0: double squareDistance = 0.0; michael@0: NS_ABORT_IF_FALSE(!shadow1 == !shadow2, "lists should be same length"); michael@0: while (shadow1) { michael@0: nsCSSValue::Array *array1 = shadow1->mValue.GetArrayValue(); michael@0: nsCSSValue::Array *array2 = shadow2->mValue.GetArrayValue(); michael@0: for (size_t i = 0; i < 4; ++i) { michael@0: NS_ABORT_IF_FALSE(array1->Item(i).GetUnit() == eCSSUnit_Pixel, michael@0: "unexpected unit"); michael@0: NS_ABORT_IF_FALSE(array2->Item(i).GetUnit() == eCSSUnit_Pixel, michael@0: "unexpected unit"); michael@0: double diff = array1->Item(i).GetFloatValue() - michael@0: array2->Item(i).GetFloatValue(); michael@0: squareDistance += diff * diff; michael@0: } michael@0: michael@0: const nsCSSValue &color1 = array1->Item(4); michael@0: const nsCSSValue &color2 = array2->Item(4); michael@0: #ifdef DEBUG michael@0: { michael@0: const nsCSSValue &inset1 = array1->Item(5); michael@0: const nsCSSValue &inset2 = array2->Item(5); michael@0: // There are only two possible states of the inset value: michael@0: // (1) GetUnit() == eCSSUnit_Null michael@0: // (2) GetUnit() == eCSSUnit_Enumerated && michael@0: // GetIntValue() == NS_STYLE_BOX_SHADOW_INSET michael@0: NS_ABORT_IF_FALSE(((color1.IsNumericColorUnit() && michael@0: color2.IsNumericColorUnit()) || michael@0: (color1.GetUnit() == color2.GetUnit())) && michael@0: inset1 == inset2, michael@0: "AddWeighted should have failed"); michael@0: } michael@0: #endif michael@0: michael@0: if (color1.GetUnit() != eCSSUnit_Null) { michael@0: nsStyleAnimation::Value color1Value michael@0: (color1.GetColorValue(), nsStyleAnimation::Value::ColorConstructor); michael@0: nsStyleAnimation::Value color2Value michael@0: (color2.GetColorValue(), nsStyleAnimation::Value::ColorConstructor); michael@0: double colorDistance; michael@0: michael@0: #ifdef DEBUG michael@0: bool ok = michael@0: #endif michael@0: nsStyleAnimation::ComputeDistance(eCSSProperty_color, michael@0: color1Value, color2Value, michael@0: colorDistance); michael@0: NS_ABORT_IF_FALSE(ok, "should not fail"); michael@0: squareDistance += colorDistance * colorDistance; michael@0: } michael@0: michael@0: shadow1 = shadow1->mNext; michael@0: shadow2 = shadow2->mNext; michael@0: NS_ABORT_IF_FALSE(!shadow1 == !shadow2, "lists should be same length"); michael@0: } michael@0: aDistance = sqrt(squareDistance); michael@0: return true; michael@0: } michael@0: case eUnit_Filter: michael@0: // FIXME: Support paced animations for filter function interpolation. michael@0: case eUnit_Transform: { michael@0: return false; michael@0: } michael@0: case eUnit_BackgroundPosition: { michael@0: const nsCSSValueList *position1 = aStartValue.GetCSSValueListValue(); michael@0: const nsCSSValueList *position2 = aEndValue.GetCSSValueListValue(); michael@0: michael@0: double squareDistance = 0.0; michael@0: NS_ABORT_IF_FALSE(!position1 == !position2, "lists should be same length"); michael@0: michael@0: while (position1 && position2) { michael@0: NS_ASSERTION(position1->mValue.GetUnit() == eCSSUnit_Array && michael@0: position2->mValue.GetUnit() == eCSSUnit_Array, michael@0: "Expected two arrays"); michael@0: michael@0: CalcValue calcVal[4]; michael@0: michael@0: nsCSSValue::Array* bgArray = position1->mValue.GetArrayValue(); michael@0: NS_ABORT_IF_FALSE(bgArray->Count() == 4, "Invalid background-position"); michael@0: NS_ASSERTION(bgArray->Item(0).GetUnit() == eCSSUnit_Null && michael@0: bgArray->Item(2).GetUnit() == eCSSUnit_Null, michael@0: "Invalid list used"); michael@0: for (int i = 0; i < 2; ++i) { michael@0: NS_ABORT_IF_FALSE(bgArray->Item(i*2+1).GetUnit() != eCSSUnit_Null, michael@0: "Invalid background-position"); michael@0: calcVal[i] = ExtractCalcValue(bgArray->Item(i*2+1)); michael@0: } michael@0: michael@0: bgArray = position2->mValue.GetArrayValue(); michael@0: NS_ABORT_IF_FALSE(bgArray->Count() == 4, "Invalid background-position"); michael@0: NS_ASSERTION(bgArray->Item(0).GetUnit() == eCSSUnit_Null && michael@0: bgArray->Item(2).GetUnit() == eCSSUnit_Null, michael@0: "Invalid list used"); michael@0: for (int i = 0; i < 2; ++i) { michael@0: NS_ABORT_IF_FALSE(bgArray->Item(i*2+1).GetUnit() != eCSSUnit_Null, michael@0: "Invalid background-position"); michael@0: calcVal[i+2] = ExtractCalcValue(bgArray->Item(i*2+1)); michael@0: } michael@0: michael@0: for (int i = 0; i < 2; ++i) { michael@0: float difflen = calcVal[i+2].mLength - calcVal[i].mLength; michael@0: float diffpct = calcVal[i+2].mPercent - calcVal[i].mPercent; michael@0: squareDistance += difflen * difflen + diffpct * diffpct; michael@0: } michael@0: michael@0: position1 = position1->mNext; michael@0: position2 = position2->mNext; michael@0: } michael@0: // fail if lists differ in length. michael@0: if (position1 || position2) { michael@0: return false; michael@0: } michael@0: michael@0: aDistance = sqrt(squareDistance); michael@0: return true; michael@0: } michael@0: case eUnit_CSSValuePairList: { michael@0: const nsCSSValuePairList *list1 = aStartValue.GetCSSValuePairListValue(); michael@0: const nsCSSValuePairList *list2 = aEndValue.GetCSSValuePairListValue(); michael@0: double squareDistance = 0.0; michael@0: do { michael@0: static nsCSSValue nsCSSValuePairList::* const pairListValues[] = { michael@0: &nsCSSValuePairList::mXValue, michael@0: &nsCSSValuePairList::mYValue, michael@0: }; michael@0: for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) { michael@0: const nsCSSValue &v1 = list1->*(pairListValues[i]); michael@0: const nsCSSValue &v2 = list2->*(pairListValues[i]); michael@0: nsCSSUnit unit = michael@0: GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit()); michael@0: if (unit == eCSSUnit_Null) { michael@0: return false; michael@0: } michael@0: double diffsquared = 0.0; michael@0: switch (unit) { michael@0: case eCSSUnit_Pixel: { michael@0: float diff = v1.GetFloatValue() - v2.GetFloatValue(); michael@0: diffsquared = diff * diff; michael@0: break; michael@0: } michael@0: case eCSSUnit_Percent: { michael@0: float diff = v1.GetPercentValue() - v2.GetPercentValue(); michael@0: diffsquared = diff * diff; michael@0: break; michael@0: } michael@0: case eCSSUnit_Calc: { michael@0: CalcValue val1 = ExtractCalcValue(v1); michael@0: CalcValue val2 = ExtractCalcValue(v2); michael@0: float difflen = val2.mLength - val1.mLength; michael@0: float diffpct = val2.mPercent - val1.mPercent; michael@0: diffsquared = difflen * difflen + diffpct * diffpct; michael@0: break; michael@0: } michael@0: default: michael@0: if (v1 != v2) { michael@0: return false; michael@0: } michael@0: break; michael@0: } michael@0: squareDistance += diffsquared; michael@0: } michael@0: list1 = list1->mNext; michael@0: list2 = list2->mNext; michael@0: } while (list1 && list2); michael@0: if (list1 || list2) { michael@0: // We can't interpolate lists of different lengths. michael@0: return false; michael@0: } michael@0: aDistance = sqrt(squareDistance); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: NS_ABORT_IF_FALSE(false, "Can't compute distance using the given common unit"); michael@0: return false; michael@0: } michael@0: michael@0: #define MAX_PACKED_COLOR_COMPONENT 255 michael@0: michael@0: inline uint8_t ClampColor(double aColor) michael@0: { michael@0: if (aColor >= MAX_PACKED_COLOR_COMPONENT) michael@0: return MAX_PACKED_COLOR_COMPONENT; michael@0: if (aColor <= 0.0) michael@0: return 0; michael@0: return NSToIntRound(aColor); michael@0: } michael@0: michael@0: // Ensure that a float/double value isn't NaN by returning zero instead michael@0: // (NaN doesn't have a sign) as a general restriction for floating point michael@0: // values in RestrictValue. michael@0: template michael@0: MOZ_ALWAYS_INLINE T michael@0: EnsureNotNan(T aValue) michael@0: { michael@0: return aValue; michael@0: } michael@0: template<> michael@0: MOZ_ALWAYS_INLINE float michael@0: EnsureNotNan(float aValue) michael@0: { michael@0: // This would benefit from a MOZ_FLOAT_IS_NaN if we had one. michael@0: return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0; michael@0: } michael@0: template<> michael@0: MOZ_ALWAYS_INLINE double michael@0: EnsureNotNan(double aValue) michael@0: { michael@0: return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0; michael@0: } michael@0: michael@0: template michael@0: T michael@0: RestrictValue(uint32_t aRestrictions, T aValue) michael@0: { michael@0: T result = EnsureNotNan(aValue); michael@0: switch (aRestrictions) { michael@0: case 0: michael@0: break; michael@0: case CSS_PROPERTY_VALUE_NONNEGATIVE: michael@0: if (result < 0) { michael@0: result = 0; michael@0: } michael@0: break; michael@0: case CSS_PROPERTY_VALUE_AT_LEAST_ONE: michael@0: if (result < 1) { michael@0: result = 1; michael@0: } michael@0: break; michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "bad value restriction"); michael@0: break; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: template michael@0: T michael@0: RestrictValue(nsCSSProperty aProperty, T aValue) michael@0: { michael@0: return RestrictValue(nsCSSProps::ValueRestrictions(aProperty), aValue); michael@0: } michael@0: michael@0: static inline void michael@0: AddCSSValuePixel(double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValue &aResult, uint32_t aValueRestrictions = 0) michael@0: { michael@0: NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Pixel, "unexpected unit"); michael@0: NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Pixel, "unexpected unit"); michael@0: aResult.SetFloatValue(RestrictValue(aValueRestrictions, michael@0: aCoeff1 * aValue1.GetFloatValue() + michael@0: aCoeff2 * aValue2.GetFloatValue()), michael@0: eCSSUnit_Pixel); michael@0: } michael@0: michael@0: static inline void michael@0: AddCSSValueNumber(double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValue &aResult, uint32_t aValueRestrictions = 0) michael@0: { michael@0: NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit"); michael@0: NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit"); michael@0: aResult.SetFloatValue(RestrictValue(aValueRestrictions, michael@0: aCoeff1 * aValue1.GetFloatValue() + michael@0: aCoeff2 * aValue2.GetFloatValue()), michael@0: eCSSUnit_Number); michael@0: } michael@0: michael@0: static inline void michael@0: AddCSSValuePercent(double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValue &aResult, uint32_t aValueRestrictions = 0) michael@0: { michael@0: NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Percent, "unexpected unit"); michael@0: NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Percent, "unexpected unit"); michael@0: aResult.SetPercentValue(RestrictValue(aValueRestrictions, michael@0: aCoeff1 * aValue1.GetPercentValue() + michael@0: aCoeff2 * aValue2.GetPercentValue())); michael@0: } michael@0: michael@0: // Add two canonical-form calc values (eUnit_Calc) to make another michael@0: // canonical-form calc value. michael@0: static void michael@0: AddCSSValueCanonicalCalc(double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValue &aResult) michael@0: { michael@0: CalcValue v1 = ExtractCalcValue(aValue1); michael@0: CalcValue v2 = ExtractCalcValue(aValue2); michael@0: CalcValue result; michael@0: result.mLength = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength; michael@0: result.mPercent = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent; michael@0: result.mHasPercent = v1.mHasPercent || v2.mHasPercent; michael@0: MOZ_ASSERT(result.mHasPercent || result.mPercent == 0.0f, michael@0: "can't have a nonzero percentage part without having percentages"); michael@0: SetCalcValue(result, aResult); michael@0: } michael@0: michael@0: static void michael@0: AddCSSValueAngle(double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValue &aResult) michael@0: { michael@0: aResult.SetFloatValue(aCoeff1 * aValue1.GetAngleValueInRadians() + michael@0: aCoeff2 * aValue2.GetAngleValueInRadians(), michael@0: eCSSUnit_Radian); michael@0: } michael@0: michael@0: static bool michael@0: AddCSSValuePixelPercentCalc(const uint32_t aValueRestrictions, michael@0: const nsCSSUnit aCommonUnit, michael@0: double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValue &aResult) michael@0: { michael@0: switch (aCommonUnit) { michael@0: case eCSSUnit_Pixel: michael@0: AddCSSValuePixel(aCoeff1, aValue1, michael@0: aCoeff2, aValue2, michael@0: aResult, aValueRestrictions); michael@0: break; michael@0: case eCSSUnit_Percent: michael@0: AddCSSValuePercent(aCoeff1, aValue1, michael@0: aCoeff2, aValue2, michael@0: aResult, aValueRestrictions); michael@0: break; michael@0: case eCSSUnit_Calc: michael@0: AddCSSValueCanonicalCalc(aCoeff1, aValue1, michael@0: aCoeff2, aValue2, michael@0: aResult); michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static inline float michael@0: GetNumberOrPercent(const nsCSSValue &aValue) michael@0: { michael@0: nsCSSUnit unit = aValue.GetUnit(); michael@0: NS_ABORT_IF_FALSE(unit == eCSSUnit_Number || unit == eCSSUnit_Percent, michael@0: "unexpected unit"); michael@0: return (unit == eCSSUnit_Number) ? michael@0: aValue.GetFloatValue() : aValue.GetPercentValue(); michael@0: } michael@0: michael@0: static inline void michael@0: AddCSSValuePercentNumber(const uint32_t aValueRestrictions, michael@0: double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValue &aResult, float aInitialVal) michael@0: { michael@0: float n1 = GetNumberOrPercent(aValue1); michael@0: float n2 = GetNumberOrPercent(aValue2); michael@0: michael@0: // Rather than interpolating aValue1 and aValue2 directly, we michael@0: // interpolate their *distances from aInitialVal* (the initial value, michael@0: // which is either 1 or 0 for "filter" functions). This matters in michael@0: // cases where aInitialVal is nonzero and the coefficients don't add michael@0: // up to 1. For example, if initialVal is 1, aCoeff1 is 0.5, and michael@0: // aCoeff2 is 0, then we'll return the value halfway between 1 and michael@0: // aValue1, rather than the value halfway between 0 and aValue1. michael@0: // Note that we do something similar in AddTransformScale(). michael@0: float result = (n1 - aInitialVal) * aCoeff1 + (n2 - aInitialVal) * aCoeff2; michael@0: aResult.SetFloatValue(RestrictValue(aValueRestrictions, result + aInitialVal), michael@0: eCSSUnit_Number); michael@0: } michael@0: michael@0: static bool michael@0: AddShadowItems(double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValueList **&aResultTail) michael@0: { michael@0: // X, Y, Radius, Spread, Color, Inset michael@0: NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Array, michael@0: "wrong unit"); michael@0: NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Array, michael@0: "wrong unit"); michael@0: nsCSSValue::Array *array1 = aValue1.GetArrayValue(); michael@0: nsCSSValue::Array *array2 = aValue2.GetArrayValue(); michael@0: nsRefPtr resultArray = nsCSSValue::Array::Create(6); michael@0: michael@0: for (size_t i = 0; i < 4; ++i) { michael@0: AddCSSValuePixel(aCoeff1, array1->Item(i), aCoeff2, array2->Item(i), michael@0: resultArray->Item(i), michael@0: // blur radius must be nonnegative michael@0: (i == 2) ? CSS_PROPERTY_VALUE_NONNEGATIVE : 0); michael@0: } michael@0: michael@0: const nsCSSValue& color1 = array1->Item(4); michael@0: const nsCSSValue& color2 = array2->Item(4); michael@0: const nsCSSValue& inset1 = array1->Item(5); michael@0: const nsCSSValue& inset2 = array2->Item(5); michael@0: if (color1.GetUnit() != color2.GetUnit() || michael@0: inset1.GetUnit() != inset2.GetUnit()) { michael@0: // We don't know how to animate between color and no-color, or michael@0: // between inset and not-inset. michael@0: return false; michael@0: } michael@0: michael@0: if (color1.GetUnit() != eCSSUnit_Null) { michael@0: nsStyleAnimation::Value color1Value michael@0: (color1.GetColorValue(), nsStyleAnimation::Value::ColorConstructor); michael@0: nsStyleAnimation::Value color2Value michael@0: (color2.GetColorValue(), nsStyleAnimation::Value::ColorConstructor); michael@0: nsStyleAnimation::Value resultColorValue; michael@0: #ifdef DEBUG michael@0: bool ok = michael@0: #endif michael@0: nsStyleAnimation::AddWeighted(eCSSProperty_color, aCoeff1, color1Value, michael@0: aCoeff2, color2Value, resultColorValue); michael@0: NS_ABORT_IF_FALSE(ok, "should not fail"); michael@0: resultArray->Item(4).SetColorValue(resultColorValue.GetColorValue()); michael@0: } michael@0: michael@0: NS_ABORT_IF_FALSE(inset1 == inset2, "should match"); michael@0: resultArray->Item(5) = inset1; michael@0: michael@0: nsCSSValueList *resultItem = new nsCSSValueList; michael@0: if (!resultItem) { michael@0: return false; michael@0: } michael@0: resultItem->mValue.SetArrayValue(resultArray, eCSSUnit_Array); michael@0: *aResultTail = resultItem; michael@0: aResultTail = &resultItem->mNext; michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: AddTransformTranslate(double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValue &aResult) michael@0: { michael@0: NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Percent || michael@0: aValue1.GetUnit() == eCSSUnit_Pixel || michael@0: aValue1.IsCalcUnit(), michael@0: "unexpected unit"); michael@0: NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Percent || michael@0: aValue2.GetUnit() == eCSSUnit_Pixel || michael@0: aValue2.IsCalcUnit(), michael@0: "unexpected unit"); michael@0: michael@0: if (aValue1.GetUnit() != aValue2.GetUnit() || aValue1.IsCalcUnit()) { michael@0: // different units; create a calc() expression michael@0: AddCSSValueCanonicalCalc(aCoeff1, aValue1, aCoeff2, aValue2, aResult); michael@0: } else if (aValue1.GetUnit() == eCSSUnit_Percent) { michael@0: // both percent michael@0: AddCSSValuePercent(aCoeff1, aValue1, aCoeff2, aValue2, aResult); michael@0: } else { michael@0: // both pixels michael@0: AddCSSValuePixel(aCoeff1, aValue1, aCoeff2, aValue2, aResult); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: AddTransformScale(double aCoeff1, const nsCSSValue &aValue1, michael@0: double aCoeff2, const nsCSSValue &aValue2, michael@0: nsCSSValue &aResult) michael@0: { michael@0: // Handle scale, and the two matrix components where identity is 1, by michael@0: // subtracting 1, multiplying by the coefficients, and then adding 1 michael@0: // back. This gets the right AddWeighted behavior and gets us the michael@0: // interpolation-against-identity behavior for free. michael@0: NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit"); michael@0: NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit"); michael@0: michael@0: float v1 = aValue1.GetFloatValue() - 1.0f, michael@0: v2 = aValue2.GetFloatValue() - 1.0f; michael@0: float result = v1 * aCoeff1 + v2 * aCoeff2; michael@0: aResult.SetFloatValue(result + 1.0f, eCSSUnit_Number); michael@0: } michael@0: michael@0: /* static */ already_AddRefed michael@0: nsStyleAnimation::AppendTransformFunction(nsCSSKeyword aTransformFunction, michael@0: nsCSSValueList**& aListTail) michael@0: { michael@0: nsRefPtr arr = AppendFunction(aTransformFunction); michael@0: nsCSSValueList *item = new nsCSSValueList; michael@0: item->mValue.SetArrayValue(arr, eCSSUnit_Function); michael@0: michael@0: *aListTail = item; michael@0: aListTail = &item->mNext; michael@0: michael@0: return arr.forget(); michael@0: } michael@0: michael@0: /* michael@0: * The relevant section of the transitions specification: michael@0: * http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types- michael@0: * defers all of the details to the 2-D and 3-D transforms specifications. michael@0: * For the 2-D transforms specification (all that's relevant for us, right michael@0: * now), the relevant section is: michael@0: * http://dev.w3.org/csswg/css3-2d-transforms/#animation michael@0: * This, in turn, refers to the unmatrix program in Graphics Gems, michael@0: * available from http://tog.acm.org/resources/GraphicsGems/ , and in michael@0: * particular as the file GraphicsGems/gemsii/unmatrix.c michael@0: * in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz michael@0: * michael@0: * The unmatrix reference is for general 3-D transform matrices (any of the michael@0: * 16 components can have any value). michael@0: * michael@0: * For CSS 2-D transforms, we have a 2-D matrix with the bottom row constant: michael@0: * michael@0: * [ A C E ] michael@0: * [ B D F ] michael@0: * [ 0 0 1 ] michael@0: * michael@0: * For that case, I believe the algorithm in unmatrix reduces to: michael@0: * michael@0: * (1) If A * D - B * C == 0, the matrix is singular. Fail. michael@0: * michael@0: * (2) Set translation components (Tx and Ty) to the translation parts of michael@0: * the matrix (E and F) and then ignore them for the rest of the time. michael@0: * (For us, E and F each actually consist of three constants: a michael@0: * length, a multiplier for the width, and a multiplier for the michael@0: * height. This actually requires its own decomposition, but I'll michael@0: * keep that separate.) michael@0: * michael@0: * (3) Let the X scale (Sx) be sqrt(A^2 + B^2). Then divide both A and B michael@0: * by it. michael@0: * michael@0: * (4) Let the XY shear (K) be A * C + B * D. From C, subtract A times michael@0: * the XY shear. From D, subtract B times the XY shear. michael@0: * michael@0: * (5) Let the Y scale (Sy) be sqrt(C^2 + D^2). Divide C, D, and the XY michael@0: * shear (K) by it. michael@0: * michael@0: * (6) At this point, A * D - B * C is either 1 or -1. If it is -1, michael@0: * negate the XY shear (K), the X scale (Sx), and A, B, C, and D. michael@0: * (Alternatively, we could negate the XY shear (K) and the Y scale michael@0: * (Sy).) michael@0: * michael@0: * (7) Let the rotation be R = atan2(B, A). michael@0: * michael@0: * Then the resulting decomposed transformation is: michael@0: * michael@0: * translate(Tx, Ty) rotate(R) skewX(atan(K)) scale(Sx, Sy) michael@0: * michael@0: * An interesting result of this is that all of the simple transform michael@0: * functions (i.e., all functions other than matrix()), in isolation, michael@0: * decompose back to themselves except for: michael@0: * 'skewY(φ)', which is 'matrix(1, tan(φ), 0, 1, 0, 0)', which decomposes michael@0: * to 'rotate(φ) skewX(φ) scale(sec(φ), cos(φ))' since (ignoring the michael@0: * alternate sign possibilities that would get fixed in step 6): michael@0: * In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ). michael@0: * Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ). michael@0: * In step 4, the XY shear is sin(φ). michael@0: * Thus, after step 4, C = -cos(φ)sin(φ) and D = 1 - sin²(φ) = cos²(φ). michael@0: * Thus, in step 5, the Y scale is sqrt(cos²(φ)(sin²(φ) + cos²(φ)) = cos(φ). michael@0: * Thus, after step 5, C = -sin(φ), D = cos(φ), and the XY shear is tan(φ). michael@0: * Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1. michael@0: * In step 7, the rotation is thus φ. michael@0: * michael@0: * skew(θ, φ), which is matrix(1, tan(φ), tan(θ), 1, 0, 0), which decomposes michael@0: * to 'rotate(φ) skewX(θ + φ) scale(sec(φ), cos(φ))' since (ignoring michael@0: * the alternate sign possibilities that would get fixed in step 6): michael@0: * In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ). michael@0: * Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ). michael@0: * In step 4, the XY shear is cos(φ)tan(θ) + sin(φ). michael@0: * Thus, after step 4, michael@0: * C = tan(θ) - cos(φ)(cos(φ)tan(θ) + sin(φ)) = tan(θ)sin²(φ) - cos(φ)sin(φ) michael@0: * D = 1 - sin(φ)(cos(φ)tan(θ) + sin(φ)) = cos²(φ) - sin(φ)cos(φ)tan(θ) michael@0: * Thus, in step 5, the Y scale is sqrt(C² + D²) = michael@0: * sqrt(tan²(θ)(sin⁴(φ) + sin²(φ)cos²(φ)) - michael@0: * 2 tan(θ)(sin³(φ)cos(φ) + sin(φ)cos³(φ)) + michael@0: * (sin²(φ)cos²(φ) + cos⁴(φ))) = michael@0: * sqrt(tan²(θ)sin²(φ) - 2 tan(θ)sin(φ)cos(φ) + cos²(φ)) = michael@0: * cos(φ) - tan(θ)sin(φ) (taking the negative of the obvious solution so michael@0: * we avoid flipping in step 6). michael@0: * After step 5, C = -sin(φ) and D = cos(φ), and the XY shear is michael@0: * (cos(φ)tan(θ) + sin(φ)) / (cos(φ) - tan(θ)sin(φ)) = michael@0: * (dividing both numerator and denominator by cos(φ)) michael@0: * (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)) = tan(θ + φ). michael@0: * (See http://en.wikipedia.org/wiki/List_of_trigonometric_identities .) michael@0: * Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1. michael@0: * In step 7, the rotation is thus φ. michael@0: * michael@0: * To check this result, we can multiply things back together: michael@0: * michael@0: * [ cos(φ) -sin(φ) ] [ 1 tan(θ + φ) ] [ sec(φ) 0 ] michael@0: * [ sin(φ) cos(φ) ] [ 0 1 ] [ 0 cos(φ) ] michael@0: * michael@0: * [ cos(φ) cos(φ)tan(θ + φ) - sin(φ) ] [ sec(φ) 0 ] michael@0: * [ sin(φ) sin(φ)tan(θ + φ) + cos(φ) ] [ 0 cos(φ) ] michael@0: * michael@0: * but since tan(θ + φ) = (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)), michael@0: * cos(φ)tan(θ + φ) - sin(φ) michael@0: * = cos(φ)(tan(θ) + tan(φ)) - sin(φ) + sin(φ)tan(θ)tan(φ) michael@0: * = cos(φ)tan(θ) + sin(φ) - sin(φ) + sin(φ)tan(θ)tan(φ) michael@0: * = cos(φ)tan(θ) + sin(φ)tan(θ)tan(φ) michael@0: * = tan(θ) (cos(φ) + sin(φ)tan(φ)) michael@0: * = tan(θ) sec(φ) (cos²(φ) + sin²(φ)) michael@0: * = tan(θ) sec(φ) michael@0: * and michael@0: * sin(φ)tan(θ + φ) + cos(φ) michael@0: * = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ) michael@0: * = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ) michael@0: * = sec(φ) (sin²(φ) + cos²(φ)) michael@0: * = sec(φ) michael@0: * so the above is: michael@0: * [ cos(φ) tan(θ) sec(φ) ] [ sec(φ) 0 ] michael@0: * [ sin(φ) sec(φ) ] [ 0 cos(φ) ] michael@0: * michael@0: * [ 1 tan(θ) ] michael@0: * [ tan(φ) 1 ] michael@0: */ michael@0: michael@0: /* michael@0: * Decompose2DMatrix implements the above decomposition algorithm. michael@0: */ michael@0: michael@0: #define XYSHEAR 0 michael@0: #define XZSHEAR 1 michael@0: #define YZSHEAR 2 michael@0: michael@0: static bool michael@0: Decompose2DMatrix(const gfxMatrix &aMatrix, gfxPoint3D &aScale, michael@0: float aShear[3], gfxQuaternion &aRotate, michael@0: gfxPoint3D &aTranslate) michael@0: { michael@0: float A = aMatrix.xx, michael@0: B = aMatrix.yx, michael@0: C = aMatrix.xy, michael@0: D = aMatrix.yy; michael@0: if (A * D == B * C) { michael@0: // singular matrix michael@0: return false; michael@0: } michael@0: michael@0: float scaleX = sqrt(A * A + B * B); michael@0: A /= scaleX; michael@0: B /= scaleX; michael@0: michael@0: float XYshear = A * C + B * D; michael@0: C -= A * XYshear; michael@0: D -= B * XYshear; michael@0: michael@0: float scaleY = sqrt(C * C + D * D); michael@0: C /= scaleY; michael@0: D /= scaleY; michael@0: XYshear /= scaleY; michael@0: michael@0: // A*D - B*C should now be 1 or -1 michael@0: NS_ASSERTION(0.99 < Abs(A*D - B*C) && Abs(A*D - B*C) < 1.01, michael@0: "determinant should now be 1 or -1"); michael@0: if (A * D < B * C) { michael@0: A = -A; michael@0: B = -B; michael@0: C = -C; michael@0: D = -D; michael@0: XYshear = -XYshear; michael@0: scaleX = -scaleX; michael@0: } michael@0: michael@0: float rotate = atan2f(B, A); michael@0: aRotate = gfxQuaternion(0, 0, sin(rotate/2), cos(rotate/2)); michael@0: aShear[XYSHEAR] = XYshear; michael@0: aScale.x = scaleX; michael@0: aScale.y = scaleY; michael@0: aTranslate.x = aMatrix.x0; michael@0: aTranslate.y = aMatrix.y0; michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Implementation of the unmatrix algorithm, specified by: michael@0: * michael@0: * http://dev.w3.org/csswg/css3-2d-transforms/#unmatrix michael@0: * michael@0: * This, in turn, refers to the unmatrix program in Graphics Gems, michael@0: * available from http://tog.acm.org/resources/GraphicsGems/ , and in michael@0: * particular as the file GraphicsGems/gemsii/unmatrix.c michael@0: * in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz michael@0: */ michael@0: static bool michael@0: Decompose3DMatrix(const gfx3DMatrix &aMatrix, gfxPoint3D &aScale, michael@0: float aShear[3], gfxQuaternion &aRotate, michael@0: gfxPoint3D &aTranslate, gfxPointH3D &aPerspective) michael@0: { michael@0: gfx3DMatrix local = aMatrix; michael@0: michael@0: if (local[3][3] == 0) { michael@0: return false; michael@0: } michael@0: /* Normalize the matrix */ michael@0: local.Normalize(); michael@0: michael@0: /** michael@0: * perspective is used to solve for perspective, but it also provides michael@0: * an easy way to test for singularity of the upper 3x3 component. michael@0: */ michael@0: gfx3DMatrix perspective = local; michael@0: gfxPointH3D empty(0, 0, 0, 1); michael@0: perspective.SetTransposedVector(3, empty); michael@0: michael@0: if (perspective.Determinant() == 0.0) { michael@0: return false; michael@0: } michael@0: michael@0: /* First, isolate perspective. */ michael@0: if (local[0][3] != 0 || local[1][3] != 0 || michael@0: local[2][3] != 0) { michael@0: /* aPerspective is the right hand side of the equation. */ michael@0: aPerspective = local.TransposedVector(3); michael@0: michael@0: /** michael@0: * Solve the equation by inverting perspective and multiplying michael@0: * aPerspective by the inverse. michael@0: */ michael@0: perspective.Invert(); michael@0: aPerspective = perspective.TransposeTransform4D(aPerspective); michael@0: michael@0: /* Clear the perspective partition */ michael@0: local.SetTransposedVector(3, empty); michael@0: } else { michael@0: aPerspective = gfxPointH3D(0, 0, 0, 1); michael@0: } michael@0: michael@0: /* Next take care of translation */ michael@0: for (int i = 0; i < 3; i++) { michael@0: aTranslate[i] = local[3][i]; michael@0: local[3][i] = 0; michael@0: } michael@0: michael@0: /* Now get scale and shear. */ michael@0: michael@0: /* Compute X scale factor and normalize first row. */ michael@0: aScale.x = local[0].Length(); michael@0: local[0] /= aScale.x; michael@0: michael@0: /* Compute XY shear factor and make 2nd local orthogonal to 1st. */ michael@0: aShear[XYSHEAR] = local[0].DotProduct(local[1]); michael@0: local[1] -= local[0] * aShear[XYSHEAR]; michael@0: michael@0: /* Now, compute Y scale and normalize 2nd local. */ michael@0: aScale.y = local[1].Length(); michael@0: local[1] /= aScale.y; michael@0: aShear[XYSHEAR] /= aScale.y; michael@0: michael@0: /* Compute XZ and YZ shears, make 3rd local orthogonal */ michael@0: aShear[XZSHEAR] = local[0].DotProduct(local[2]); michael@0: local[2] -= local[0] * aShear[XZSHEAR]; michael@0: aShear[YZSHEAR] = local[1].DotProduct(local[2]); michael@0: local[2] -= local[1] * aShear[YZSHEAR]; michael@0: michael@0: /* Next, get Z scale and normalize 3rd local. */ michael@0: aScale.z = local[2].Length(); michael@0: local[2] /= aScale.z; michael@0: michael@0: aShear[XZSHEAR] /= aScale.z; michael@0: aShear[YZSHEAR] /= aScale.z; michael@0: michael@0: /** michael@0: * At this point, the matrix (in locals) is orthonormal. michael@0: * Check for a coordinate system flip. If the determinant michael@0: * is -1, then negate the matrix and the scaling factors. michael@0: */ michael@0: if (local[0].DotProduct(local[1].CrossProduct(local[2])) < 0) { michael@0: aScale *= -1; michael@0: for (int i = 0; i < 3; i++) { michael@0: local[i] *= -1; michael@0: } michael@0: } michael@0: michael@0: /* Now, get the rotations out */ michael@0: aRotate = gfxQuaternion(local); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: T InterpolateNumerically(const T& aOne, const T& aTwo, double aCoeff) michael@0: { michael@0: return aOne + (aTwo - aOne) * aCoeff; michael@0: } michael@0: michael@0: michael@0: /* static */ gfx3DMatrix michael@0: nsStyleAnimation::InterpolateTransformMatrix(const gfx3DMatrix &aMatrix1, michael@0: const gfx3DMatrix &aMatrix2, michael@0: double aProgress) michael@0: { michael@0: // Decompose both matrices michael@0: michael@0: // TODO: What do we do if one of these returns false (singular matrix) michael@0: michael@0: gfxPoint3D scale1(1, 1, 1), translate1; michael@0: gfxPointH3D perspective1(0, 0, 0, 1); michael@0: gfxQuaternion rotate1; michael@0: float shear1[3] = { 0.0f, 0.0f, 0.0f}; michael@0: michael@0: gfxPoint3D scale2(1, 1, 1), translate2; michael@0: gfxPointH3D perspective2(0, 0, 0, 1); michael@0: gfxQuaternion rotate2; michael@0: float shear2[3] = { 0.0f, 0.0f, 0.0f}; michael@0: michael@0: gfxMatrix matrix2d1, matrix2d2; michael@0: if (aMatrix1.Is2D(&matrix2d1) && aMatrix2.Is2D(&matrix2d2)) { michael@0: Decompose2DMatrix(matrix2d1, scale1, shear1, rotate1, translate1); michael@0: Decompose2DMatrix(matrix2d2, scale2, shear2, rotate2, translate2); michael@0: } else { michael@0: Decompose3DMatrix(aMatrix1, scale1, shear1, michael@0: rotate1, translate1, perspective1); michael@0: Decompose3DMatrix(aMatrix2, scale2, shear2, michael@0: rotate2, translate2, perspective2); michael@0: } michael@0: michael@0: // Interpolate each of the pieces michael@0: gfx3DMatrix result; michael@0: michael@0: gfxPointH3D perspective = michael@0: InterpolateNumerically(perspective1, perspective2, aProgress); michael@0: result.SetTransposedVector(3, perspective); michael@0: michael@0: gfxPoint3D translate = michael@0: InterpolateNumerically(translate1, translate2, aProgress); michael@0: result.Translate(translate); michael@0: michael@0: gfxQuaternion q3 = rotate1.Slerp(rotate2, aProgress); michael@0: gfx3DMatrix rotate = q3.ToMatrix(); michael@0: if (!rotate.IsIdentity()) { michael@0: result = rotate * result; michael@0: } michael@0: michael@0: // TODO: Would it be better to interpolate these as angles? How do we convert back to angles? michael@0: float yzshear = michael@0: InterpolateNumerically(shear1[YZSHEAR], shear2[YZSHEAR], aProgress); michael@0: if (yzshear != 0.0) { michael@0: result.SkewYZ(yzshear); michael@0: } michael@0: michael@0: float xzshear = michael@0: InterpolateNumerically(shear1[XZSHEAR], shear2[XZSHEAR], aProgress); michael@0: if (xzshear != 0.0) { michael@0: result.SkewXZ(xzshear); michael@0: } michael@0: michael@0: float xyshear = michael@0: InterpolateNumerically(shear1[XYSHEAR], shear2[XYSHEAR], aProgress); michael@0: if (xyshear != 0.0) { michael@0: result.SkewXY(xyshear); michael@0: } michael@0: michael@0: gfxPoint3D scale = michael@0: InterpolateNumerically(scale1, scale2, aProgress); michael@0: if (scale != gfxPoint3D(1.0, 1.0, 1.0)) { michael@0: result.Scale(scale.x, scale.y, scale.z); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: static nsCSSValueList* michael@0: AddDifferentTransformLists(double aCoeff1, const nsCSSValueList* aList1, michael@0: double aCoeff2, const nsCSSValueList* aList2) michael@0: { michael@0: nsAutoPtr result; michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: michael@0: nsRefPtr arr; michael@0: arr = nsStyleAnimation::AppendTransformFunction(eCSSKeyword_interpolatematrix, resultTail); michael@0: michael@0: // FIXME: We should change the other transform code to also only michael@0: // take a single progress value, as having values that don't michael@0: // sum to 1 doesn't make sense for these. michael@0: if (aList1 == aList2) { michael@0: arr->Item(1).Reset(); michael@0: } else { michael@0: aList1->CloneInto(arr->Item(1).SetListValue()); michael@0: } michael@0: michael@0: aList2->CloneInto(arr->Item(2).SetListValue()); michael@0: arr->Item(3).SetPercentValue(aCoeff2); michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: static bool michael@0: TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2) michael@0: { michael@0: return ToPrimitive(func1) == ToPrimitive(func2); michael@0: } michael@0: michael@0: static bool michael@0: AddFilterFunctionImpl(double aCoeff1, const nsCSSValueList* aList1, michael@0: double aCoeff2, const nsCSSValueList* aList2, michael@0: nsCSSValueList**& aResultTail) michael@0: { michael@0: // AddFilterFunction should be our only caller, and it should ensure that both michael@0: // args are non-null. michael@0: NS_ABORT_IF_FALSE(aList1, "expected filter list"); michael@0: NS_ABORT_IF_FALSE(aList2, "expected filter list"); michael@0: NS_ABORT_IF_FALSE(aList1->mValue.GetUnit() == eCSSUnit_Function, michael@0: "expected function"); michael@0: NS_ABORT_IF_FALSE(aList2->mValue.GetUnit() == eCSSUnit_Function, michael@0: "expected function"); michael@0: nsRefPtr a1 = aList1->mValue.GetArrayValue(), michael@0: a2 = aList2->mValue.GetArrayValue(); michael@0: nsCSSKeyword filterFunction = a1->Item(0).GetKeywordValue(); michael@0: if (filterFunction != a2->Item(0).GetKeywordValue()) michael@0: return false; // Can't add two filters of different types. michael@0: michael@0: nsAutoPtr resultListEntry(new nsCSSValueList); michael@0: nsCSSValue::Array* result = michael@0: resultListEntry->mValue.InitFunction(filterFunction, 1); michael@0: michael@0: // "hue-rotate" is the only filter-function that accepts negative values, and michael@0: // we don't use this "restrictions" variable in its clause below. michael@0: const uint32_t restrictions = CSS_PROPERTY_VALUE_NONNEGATIVE; michael@0: const nsCSSValue& funcArg1 = a1->Item(1); michael@0: const nsCSSValue& funcArg2 = a2->Item(1); michael@0: nsCSSValue& resultArg = result->Item(1); michael@0: float initialVal = 1.0f; michael@0: switch (filterFunction) { michael@0: case eCSSKeyword_blur: { michael@0: nsCSSUnit unit; michael@0: if (funcArg1.GetUnit() == funcArg2.GetUnit()) { michael@0: unit = funcArg1.GetUnit(); michael@0: } else { michael@0: // If units differ, we'll just combine them with calc(). michael@0: unit = eCSSUnit_Calc; michael@0: } michael@0: if (!AddCSSValuePixelPercentCalc(restrictions, michael@0: unit, michael@0: aCoeff1, funcArg1, michael@0: aCoeff2, funcArg2, michael@0: resultArg)) { michael@0: return false; michael@0: } michael@0: break; michael@0: } michael@0: case eCSSKeyword_grayscale: michael@0: case eCSSKeyword_invert: michael@0: case eCSSKeyword_sepia: michael@0: initialVal = 0.0f; michael@0: case eCSSKeyword_brightness: michael@0: case eCSSKeyword_contrast: michael@0: case eCSSKeyword_opacity: michael@0: case eCSSKeyword_saturate: michael@0: AddCSSValuePercentNumber(restrictions, michael@0: aCoeff1, funcArg1, michael@0: aCoeff2, funcArg2, michael@0: resultArg, michael@0: initialVal); michael@0: break; michael@0: case eCSSKeyword_hue_rotate: michael@0: AddCSSValueAngle(aCoeff1, funcArg1, michael@0: aCoeff2, funcArg2, michael@0: resultArg); michael@0: break; michael@0: case eCSSKeyword_drop_shadow: { michael@0: nsCSSValueList* resultShadow = resultArg.SetListValue(); michael@0: nsAutoPtr shadowValue; michael@0: nsCSSValueList **shadowTail = getter_Transfers(shadowValue); michael@0: NS_ABORT_IF_FALSE(!funcArg1.GetListValue()->mNext && michael@0: !funcArg2.GetListValue()->mNext, michael@0: "drop-shadow filter func doesn't support lists"); michael@0: if (!AddShadowItems(aCoeff1, funcArg1.GetListValue()->mValue, michael@0: aCoeff2, funcArg2.GetListValue()->mValue, michael@0: shadowTail)) { michael@0: return false; michael@0: } michael@0: *resultShadow = *shadowValue; michael@0: break; michael@0: } michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unknown filter function"); michael@0: return false; michael@0: } michael@0: michael@0: *aResultTail = resultListEntry.forget(); michael@0: aResultTail = &(*aResultTail)->mNext; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: AddFilterFunction(double aCoeff1, const nsCSSValueList* aList1, michael@0: double aCoeff2, const nsCSSValueList* aList2, michael@0: nsCSSValueList**& aResultTail) michael@0: { michael@0: NS_ABORT_IF_FALSE(aList1 || aList2, michael@0: "one function list item must not be null"); michael@0: // Note that one of our arguments could be null, indicating that michael@0: // it's the initial value. Rather than adding special null-handling michael@0: // logic, we just check for null values and replace them with michael@0: // 0 * the other value. That way, AddFilterFunctionImpl can assume michael@0: // its args are non-null. michael@0: if (!aList1) { michael@0: return AddFilterFunctionImpl(aCoeff2, aList2, 0, aList2, aResultTail); michael@0: } michael@0: if (!aList2) { michael@0: return AddFilterFunctionImpl(aCoeff1, aList1, 0, aList1, aResultTail); michael@0: } michael@0: michael@0: return AddFilterFunctionImpl(aCoeff1, aList1, aCoeff2, aList2, aResultTail); michael@0: } michael@0: michael@0: static nsCSSValueList* michael@0: AddTransformLists(double aCoeff1, const nsCSSValueList* aList1, michael@0: double aCoeff2, const nsCSSValueList* aList2) michael@0: { michael@0: nsAutoPtr result; michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: michael@0: do { michael@0: nsRefPtr a1 = ToPrimitive(aList1->mValue.GetArrayValue()), michael@0: a2 = ToPrimitive(aList2->mValue.GetArrayValue()); michael@0: NS_ABORT_IF_FALSE(TransformFunctionsMatch(nsStyleTransformMatrix::TransformFunctionOf(a1), michael@0: nsStyleTransformMatrix::TransformFunctionOf(a2)), michael@0: "transform function mismatch"); michael@0: NS_ABORT_IF_FALSE(!*resultTail, michael@0: "resultTail isn't pointing to the tail (may leak)"); michael@0: michael@0: nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1); michael@0: nsRefPtr arr; michael@0: if (tfunc != eCSSKeyword_matrix && michael@0: tfunc != eCSSKeyword_matrix3d && michael@0: tfunc != eCSSKeyword_interpolatematrix && michael@0: tfunc != eCSSKeyword_rotate3d && michael@0: tfunc != eCSSKeyword_perspective) { michael@0: arr = nsStyleAnimation::AppendTransformFunction(tfunc, resultTail); michael@0: } michael@0: michael@0: switch (tfunc) { michael@0: case eCSSKeyword_translate3d: { michael@0: NS_ABORT_IF_FALSE(a1->Count() == 4, "unexpected count"); michael@0: NS_ABORT_IF_FALSE(a2->Count() == 4, "unexpected count"); michael@0: AddTransformTranslate(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1), michael@0: arr->Item(1)); michael@0: AddTransformTranslate(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2), michael@0: arr->Item(2)); michael@0: AddTransformTranslate(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3), michael@0: arr->Item(3)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_scale3d: { michael@0: NS_ABORT_IF_FALSE(a1->Count() == 4, "unexpected count"); michael@0: NS_ABORT_IF_FALSE(a2->Count() == 4, "unexpected count"); michael@0: michael@0: AddTransformScale(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1), michael@0: arr->Item(1)); michael@0: AddTransformScale(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2), michael@0: arr->Item(2)); michael@0: AddTransformScale(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3), michael@0: arr->Item(3)); michael@0: michael@0: break; michael@0: } michael@0: // It would probably be nicer to animate skew in tangent space michael@0: // rather than angle space. However, it's easy to specify michael@0: // skews with infinite tangents, and behavior changes pretty michael@0: // drastically when crossing such skews (since the direction of michael@0: // animation flips), so interop is probably more important here. michael@0: case eCSSKeyword_skew: { michael@0: NS_ABORT_IF_FALSE(a1->Count() == 2 || a1->Count() == 3, michael@0: "unexpected count"); michael@0: NS_ABORT_IF_FALSE(a2->Count() == 2 || a2->Count() == 3, michael@0: "unexpected count"); michael@0: michael@0: nsCSSValue zero(0.0f, eCSSUnit_Radian); michael@0: // Add Y component of skew. michael@0: AddCSSValueAngle(aCoeff1, michael@0: a1->Count() == 3 ? a1->Item(2) : zero, michael@0: aCoeff2, michael@0: a2->Count() == 3 ? a2->Item(2) : zero, michael@0: arr->Item(2)); michael@0: michael@0: // Add X component of skew (which can be merged with case below michael@0: // in non-DEBUG). michael@0: AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1), michael@0: arr->Item(1)); michael@0: michael@0: break; michael@0: } michael@0: case eCSSKeyword_skewx: michael@0: case eCSSKeyword_skewy: michael@0: case eCSSKeyword_rotate: michael@0: case eCSSKeyword_rotatex: michael@0: case eCSSKeyword_rotatey: michael@0: case eCSSKeyword_rotatez: { michael@0: NS_ABORT_IF_FALSE(a1->Count() == 2, "unexpected count"); michael@0: NS_ABORT_IF_FALSE(a2->Count() == 2, "unexpected count"); michael@0: michael@0: AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1), michael@0: arr->Item(1)); michael@0: michael@0: break; michael@0: } michael@0: case eCSSKeyword_matrix: michael@0: case eCSSKeyword_matrix3d: michael@0: case eCSSKeyword_interpolatematrix: michael@0: case eCSSKeyword_rotate3d: michael@0: case eCSSKeyword_perspective: { michael@0: // FIXME: If the matrix contains only numbers then we could decompose michael@0: // here. michael@0: michael@0: // Construct temporary lists with only this item in them. michael@0: nsCSSValueList tempList1, tempList2; michael@0: tempList1.mValue = aList1->mValue; michael@0: tempList2.mValue = aList2->mValue; michael@0: michael@0: if (aList1 == aList2) { michael@0: *resultTail = michael@0: AddDifferentTransformLists(aCoeff1, &tempList1, aCoeff2, &tempList1); michael@0: } else { michael@0: *resultTail = michael@0: AddDifferentTransformLists(aCoeff1, &tempList1, aCoeff2, &tempList2); michael@0: } michael@0: michael@0: // Now advance resultTail to point to the new tail slot. michael@0: while (*resultTail) { michael@0: resultTail = &(*resultTail)->mNext; michael@0: } michael@0: michael@0: break; michael@0: } michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unknown transform function"); michael@0: } michael@0: michael@0: aList1 = aList1->mNext; michael@0: aList2 = aList2->mNext; michael@0: } while (aList1); michael@0: NS_ABORT_IF_FALSE(!aList2, "list length mismatch"); michael@0: NS_ABORT_IF_FALSE(!*resultTail, michael@0: "resultTail isn't pointing to the tail"); michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: bool michael@0: nsStyleAnimation::AddWeighted(nsCSSProperty aProperty, michael@0: double aCoeff1, const Value& aValue1, michael@0: double aCoeff2, const Value& aValue2, michael@0: Value& aResultValue) michael@0: { michael@0: Unit commonUnit = michael@0: GetCommonUnit(aProperty, aValue1.GetUnit(), aValue2.GetUnit()); michael@0: // Maybe need a followup method to convert the inputs into the common michael@0: // unit-type, if they don't already match it. (Or would it make sense to do michael@0: // that in GetCommonUnit? in which case maybe ConvertToCommonUnit would be michael@0: // better.) michael@0: michael@0: switch (commonUnit) { michael@0: case eUnit_Null: michael@0: case eUnit_Auto: michael@0: case eUnit_None: michael@0: case eUnit_Normal: michael@0: case eUnit_UnparsedString: michael@0: return false; michael@0: michael@0: case eUnit_Enumerated: michael@0: switch (aProperty) { michael@0: case eCSSProperty_font_stretch: { michael@0: // Animate just like eUnit_Integer. michael@0: int32_t result = floor(aCoeff1 * double(aValue1.GetIntValue()) + michael@0: aCoeff2 * double(aValue2.GetIntValue())); michael@0: if (result < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED) { michael@0: result = NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED; michael@0: } else if (result > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) { michael@0: result = NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED; michael@0: } michael@0: aResultValue.SetIntValue(result, eUnit_Enumerated); michael@0: return true; michael@0: } michael@0: default: michael@0: return false; michael@0: } michael@0: case eUnit_Visibility: { michael@0: int32_t enum1 = aValue1.GetIntValue(); michael@0: int32_t enum2 = aValue2.GetIntValue(); michael@0: if (enum1 == enum2) { michael@0: aResultValue.SetIntValue(enum1, eUnit_Visibility); michael@0: return true; michael@0: } michael@0: if ((enum1 == NS_STYLE_VISIBILITY_VISIBLE) == michael@0: (enum2 == NS_STYLE_VISIBILITY_VISIBLE)) { michael@0: return false; michael@0: } michael@0: int32_t val1 = enum1 == NS_STYLE_VISIBILITY_VISIBLE; michael@0: int32_t val2 = enum2 == NS_STYLE_VISIBILITY_VISIBLE; michael@0: double interp = aCoeff1 * val1 + aCoeff2 * val2; michael@0: int32_t result = interp > 0.0 ? NS_STYLE_VISIBILITY_VISIBLE michael@0: : (val1 ? enum2 : enum1); michael@0: aResultValue.SetIntValue(result, eUnit_Visibility); michael@0: return true; michael@0: } michael@0: case eUnit_Integer: { michael@0: // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types- michael@0: // says we should use floor michael@0: int32_t result = floor(aCoeff1 * double(aValue1.GetIntValue()) + michael@0: aCoeff2 * double(aValue2.GetIntValue())); michael@0: if (aProperty == eCSSProperty_font_weight) { michael@0: if (result < 100) { michael@0: result = 100; michael@0: } else if (result > 900) { michael@0: result = 900; michael@0: } michael@0: result -= result % 100; michael@0: } else { michael@0: result = RestrictValue(aProperty, result); michael@0: } michael@0: aResultValue.SetIntValue(result, eUnit_Integer); michael@0: return true; michael@0: } michael@0: case eUnit_Coord: { michael@0: aResultValue.SetCoordValue(RestrictValue(aProperty, NSToCoordRound( michael@0: aCoeff1 * aValue1.GetCoordValue() + michael@0: aCoeff2 * aValue2.GetCoordValue()))); michael@0: return true; michael@0: } michael@0: case eUnit_Percent: { michael@0: aResultValue.SetPercentValue(RestrictValue(aProperty, michael@0: aCoeff1 * aValue1.GetPercentValue() + michael@0: aCoeff2 * aValue2.GetPercentValue())); michael@0: return true; michael@0: } michael@0: case eUnit_Float: { michael@0: // Special case for flex-grow and flex-shrink: animations are michael@0: // disallowed between 0 and other values. michael@0: if ((aProperty == eCSSProperty_flex_grow || michael@0: aProperty == eCSSProperty_flex_shrink) && michael@0: (aValue1.GetFloatValue() == 0.0f || michael@0: aValue2.GetFloatValue() == 0.0f) && michael@0: aValue1.GetFloatValue() != aValue2.GetFloatValue()) { michael@0: return false; michael@0: } michael@0: michael@0: aResultValue.SetFloatValue(RestrictValue(aProperty, michael@0: aCoeff1 * aValue1.GetFloatValue() + michael@0: aCoeff2 * aValue2.GetFloatValue())); michael@0: return true; michael@0: } michael@0: case eUnit_Color: { michael@0: nscolor color1 = aValue1.GetColorValue(); michael@0: nscolor color2 = aValue2.GetColorValue(); michael@0: // FIXME (spec): The CSS transitions spec doesn't say whether michael@0: // colors are premultiplied, but things work better when they are, michael@0: // so use premultiplication. Spec issue is still open per michael@0: // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html michael@0: michael@0: // To save some math, scale the alpha down to a 0-1 scale, but michael@0: // leave the color components on a 0-255 scale. michael@0: double A1 = NS_GET_A(color1) * (1.0 / 255.0); michael@0: double R1 = NS_GET_R(color1) * A1; michael@0: double G1 = NS_GET_G(color1) * A1; michael@0: double B1 = NS_GET_B(color1) * A1; michael@0: double A2 = NS_GET_A(color2) * (1.0 / 255.0); michael@0: double R2 = NS_GET_R(color2) * A2; michael@0: double G2 = NS_GET_G(color2) * A2; michael@0: double B2 = NS_GET_B(color2) * A2; michael@0: double Aresf = (A1 * aCoeff1 + A2 * aCoeff2); michael@0: nscolor resultColor; michael@0: if (Aresf <= 0.0) { michael@0: resultColor = NS_RGBA(0, 0, 0, 0); michael@0: } else { michael@0: if (Aresf > 1.0) { michael@0: Aresf = 1.0; michael@0: } michael@0: double factor = 1.0 / Aresf; michael@0: uint8_t Ares = NSToIntRound(Aresf * 255.0); michael@0: uint8_t Rres = ClampColor((R1 * aCoeff1 + R2 * aCoeff2) * factor); michael@0: uint8_t Gres = ClampColor((G1 * aCoeff1 + G2 * aCoeff2) * factor); michael@0: uint8_t Bres = ClampColor((B1 * aCoeff1 + B2 * aCoeff2) * factor); michael@0: resultColor = NS_RGBA(Rres, Gres, Bres, Ares); michael@0: } michael@0: aResultValue.SetColorValue(resultColor); michael@0: return true; michael@0: } michael@0: case eUnit_Calc: { michael@0: CalcValue v1 = ExtractCalcValue(aValue1); michael@0: CalcValue v2 = ExtractCalcValue(aValue2); michael@0: double len = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength; michael@0: double pct = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent; michael@0: bool hasPct = (aCoeff1 != 0.0 && v1.mHasPercent) || michael@0: (aCoeff2 != 0.0 && v2.mHasPercent); michael@0: nsCSSValue *val = new nsCSSValue(); michael@0: nsCSSValue::Array *arr = nsCSSValue::Array::Create(1); michael@0: val->SetArrayValue(arr, eCSSUnit_Calc); michael@0: if (hasPct) { michael@0: nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2); michael@0: arr2->Item(0).SetFloatValue(len, eCSSUnit_Pixel); michael@0: arr2->Item(1).SetPercentValue(pct); michael@0: arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus); michael@0: } else { michael@0: arr->Item(0).SetFloatValue(len, eCSSUnit_Pixel); michael@0: } michael@0: aResultValue.SetAndAdoptCSSValueValue(val, eUnit_Calc); michael@0: return true; michael@0: } michael@0: case eUnit_CSSValuePair: { michael@0: const nsCSSValuePair *pair1 = aValue1.GetCSSValuePairValue(); michael@0: const nsCSSValuePair *pair2 = aValue2.GetCSSValuePairValue(); michael@0: nsCSSUnit unit[2]; michael@0: unit[0] = GetCommonUnit(aProperty, pair1->mXValue.GetUnit(), michael@0: pair2->mXValue.GetUnit()); michael@0: unit[1] = GetCommonUnit(aProperty, pair1->mYValue.GetUnit(), michael@0: pair2->mYValue.GetUnit()); michael@0: if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null || michael@0: unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) { michael@0: return false; michael@0: } michael@0: michael@0: nsAutoPtr result(new nsCSSValuePair); michael@0: static nsCSSValue nsCSSValuePair::* const pairValues[2] = { michael@0: &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue michael@0: }; michael@0: uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty); michael@0: for (uint32_t i = 0; i < 2; ++i) { michael@0: nsCSSValue nsCSSValuePair::*member = pairValues[i]; michael@0: if (!AddCSSValuePixelPercentCalc(restrictions, unit[i], michael@0: aCoeff1, pair1->*member, michael@0: aCoeff2, pair2->*member, michael@0: result->*member) ) { michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: aResultValue.SetAndAdoptCSSValuePairValue(result.forget(), michael@0: eUnit_CSSValuePair); michael@0: return true; michael@0: } michael@0: case eUnit_CSSValueTriplet: { michael@0: nsCSSValueTriplet triplet1(*aValue1.GetCSSValueTripletValue()); michael@0: nsCSSValueTriplet triplet2(*aValue2.GetCSSValueTripletValue()); michael@0: michael@0: nsCSSUnit unit[3]; michael@0: unit[0] = GetCommonUnit(aProperty, triplet1.mXValue.GetUnit(), michael@0: triplet2.mXValue.GetUnit()); michael@0: unit[1] = GetCommonUnit(aProperty, triplet1.mYValue.GetUnit(), michael@0: triplet2.mYValue.GetUnit()); michael@0: unit[2] = GetCommonUnit(aProperty, triplet1.mZValue.GetUnit(), michael@0: triplet2.mZValue.GetUnit()); michael@0: if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null || michael@0: unit[2] == eCSSUnit_Null) { michael@0: return false; michael@0: } michael@0: michael@0: nsAutoPtr result(new nsCSSValueTriplet); michael@0: static nsCSSValue nsCSSValueTriplet::* const tripletValues[3] = { michael@0: &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue michael@0: }; michael@0: uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty); michael@0: for (uint32_t i = 0; i < 3; ++i) { michael@0: nsCSSValue nsCSSValueTriplet::*member = tripletValues[i]; michael@0: if (!AddCSSValuePixelPercentCalc(restrictions, unit[i], michael@0: aCoeff1, &triplet1->*member, michael@0: aCoeff2, &triplet2->*member, michael@0: result->*member) ) { michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: aResultValue.SetAndAdoptCSSValueTripletValue(result.forget(), michael@0: eUnit_CSSValueTriplet); michael@0: return true; michael@0: } michael@0: case eUnit_CSSRect: { michael@0: NS_ABORT_IF_FALSE(nsCSSProps::ValueRestrictions(aProperty) == 0, michael@0: "must add code for handling value restrictions"); michael@0: const nsCSSRect *rect1 = aValue1.GetCSSRectValue(); michael@0: const nsCSSRect *rect2 = aValue2.GetCSSRectValue(); michael@0: if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() || michael@0: rect1->mRight.GetUnit() != rect2->mRight.GetUnit() || michael@0: rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() || michael@0: rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) { michael@0: // At least until we have calc() michael@0: return false; michael@0: } michael@0: michael@0: nsAutoPtr result(new nsCSSRect); michael@0: for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) { michael@0: nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i]; michael@0: NS_ABORT_IF_FALSE((rect1->*member).GetUnit() == michael@0: (rect2->*member).GetUnit(), michael@0: "should have returned above"); michael@0: switch ((rect1->*member).GetUnit()) { michael@0: case eCSSUnit_Pixel: michael@0: AddCSSValuePixel(aCoeff1, rect1->*member, aCoeff2, rect2->*member, michael@0: result->*member); michael@0: break; michael@0: case eCSSUnit_Auto: michael@0: if (float(aCoeff1 + aCoeff2) != 1.0f) { michael@0: // Interpolating between two auto values makes sense; michael@0: // adding in other ratios does not. michael@0: return false; michael@0: } michael@0: (result->*member).SetAutoValue(); michael@0: break; michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: aResultValue.SetAndAdoptCSSRectValue(result.forget(), eUnit_CSSRect); michael@0: return true; michael@0: } michael@0: case eUnit_Dasharray: { michael@0: const nsCSSValueList *list1 = aValue1.GetCSSValueListValue(); michael@0: const nsCSSValueList *list2 = aValue2.GetCSSValueListValue(); michael@0: michael@0: uint32_t len1 = 0, len2 = 0; michael@0: for (const nsCSSValueList *v = list1; v; v = v->mNext) { michael@0: ++len1; michael@0: } michael@0: for (const nsCSSValueList *v = list2; v; v = v->mNext) { michael@0: ++len2; michael@0: } michael@0: NS_ABORT_IF_FALSE(len1 > 0 && len2 > 0, "unexpected length"); michael@0: if (list1->mValue.GetUnit() == eCSSUnit_None || michael@0: list2->mValue.GetUnit() == eCSSUnit_None) { michael@0: // One of our values is "none". Can't do addition with that. michael@0: NS_ABORT_IF_FALSE( michael@0: (list1->mValue.GetUnit() != eCSSUnit_None || len1 == 1) && michael@0: (list2->mValue.GetUnit() != eCSSUnit_None || len2 == 1), michael@0: "multi-value valuelist with 'none' as first element"); michael@0: return false; michael@0: } michael@0: michael@0: nsAutoPtr result; michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: for (uint32_t i = 0, i_end = EuclidLCM(len1, len2); i != i_end; ++i) { michael@0: const nsCSSValue &v1 = list1->mValue; michael@0: const nsCSSValue &v2 = list2->mValue; michael@0: NS_ABORT_IF_FALSE(v1.GetUnit() == eCSSUnit_Number || michael@0: v1.GetUnit() == eCSSUnit_Percent, "unexpected"); michael@0: NS_ABORT_IF_FALSE(v2.GetUnit() == eCSSUnit_Number || michael@0: v2.GetUnit() == eCSSUnit_Percent, "unexpected"); michael@0: if (v1.GetUnit() != v2.GetUnit()) { michael@0: // Can't animate between lengths and percentages (until calc()). michael@0: return false; michael@0: } michael@0: michael@0: nsCSSValueList *item = new nsCSSValueList; michael@0: if (!item) { michael@0: return false; michael@0: } michael@0: *resultTail = item; michael@0: resultTail = &item->mNext; michael@0: michael@0: if (v1.GetUnit() == eCSSUnit_Number) { michael@0: AddCSSValueNumber(aCoeff1, v1, aCoeff2, v2, item->mValue, michael@0: CSS_PROPERTY_VALUE_NONNEGATIVE); michael@0: } else { michael@0: AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, item->mValue, michael@0: CSS_PROPERTY_VALUE_NONNEGATIVE); michael@0: } michael@0: michael@0: list1 = list1->mNext; michael@0: if (!list1) { michael@0: list1 = aValue1.GetCSSValueListValue(); michael@0: } michael@0: list2 = list2->mNext; michael@0: if (!list2) { michael@0: list2 = aValue2.GetCSSValueListValue(); michael@0: } michael@0: } michael@0: michael@0: aResultValue.SetAndAdoptCSSValueListValue(result.forget(), michael@0: eUnit_Dasharray); michael@0: return true; michael@0: } michael@0: case eUnit_Shadow: { michael@0: // This is implemented according to: michael@0: // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types- michael@0: // and the third item in the summary of: michael@0: // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html michael@0: const nsCSSValueList *shadow1 = aValue1.GetCSSValueListValue(); michael@0: const nsCSSValueList *shadow2 = aValue2.GetCSSValueListValue(); michael@0: nsAutoPtr result; michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: while (shadow1 && shadow2) { michael@0: if (!AddShadowItems(aCoeff1, shadow1->mValue, michael@0: aCoeff2, shadow2->mValue, michael@0: resultTail)) { michael@0: return false; michael@0: } michael@0: shadow1 = shadow1->mNext; michael@0: shadow2 = shadow2->mNext; michael@0: } michael@0: if (shadow1 || shadow2) { michael@0: const nsCSSValueList *longShadow; michael@0: double longCoeff; michael@0: if (shadow1) { michael@0: longShadow = shadow1; michael@0: longCoeff = aCoeff1; michael@0: } else { michael@0: longShadow = shadow2; michael@0: longCoeff = aCoeff2; michael@0: } michael@0: michael@0: while (longShadow) { michael@0: // Passing coefficients that add to less than 1 produces the michael@0: // desired result of interpolating "0 0 0 transparent" with michael@0: // the current shadow. michael@0: if (!AddShadowItems(longCoeff, longShadow->mValue, michael@0: 0.0, longShadow->mValue, michael@0: resultTail)) { michael@0: return false; michael@0: } michael@0: michael@0: longShadow = longShadow->mNext; michael@0: } michael@0: } michael@0: aResultValue.SetAndAdoptCSSValueListValue(result.forget(), eUnit_Shadow); michael@0: return true; michael@0: } michael@0: michael@0: case eUnit_Filter: { michael@0: const nsCSSValueList *list1 = aValue1.GetCSSValueListValue(); michael@0: const nsCSSValueList *list2 = aValue2.GetCSSValueListValue(); michael@0: michael@0: nsAutoPtr result; michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: while (list1 || list2) { michael@0: NS_ABORT_IF_FALSE(!*resultTail, michael@0: "resultTail isn't pointing to the tail (may leak)"); michael@0: if ((list1 && list1->mValue.GetUnit() != eCSSUnit_Function) || michael@0: (list2 && list2->mValue.GetUnit() != eCSSUnit_Function)) { michael@0: // If we don't have filter-functions, we must have filter-URLs, which michael@0: // we can't add or interpolate. michael@0: return false; michael@0: } michael@0: michael@0: if (!AddFilterFunction(aCoeff1, list1, aCoeff2, list2, resultTail)) { michael@0: // filter function mismatch michael@0: return false; michael@0: } michael@0: michael@0: // move to next list items michael@0: if (list1) { michael@0: list1 = list1->mNext; michael@0: } michael@0: if (list2) { michael@0: list2 = list2->mNext; michael@0: } michael@0: } michael@0: NS_ABORT_IF_FALSE(!*resultTail, michael@0: "resultTail isn't pointing to the tail (may leak)"); michael@0: michael@0: aResultValue.SetAndAdoptCSSValueListValue(result.forget(), michael@0: eUnit_Filter); michael@0: return true; michael@0: } michael@0: michael@0: case eUnit_Transform: { michael@0: const nsCSSValueList* list1 = aValue1.GetCSSValueSharedListValue()->mHead; michael@0: const nsCSSValueList* list2 = aValue2.GetCSSValueSharedListValue()->mHead; michael@0: michael@0: MOZ_ASSERT(list1); michael@0: MOZ_ASSERT(list2); michael@0: michael@0: // We want to avoid the matrix decomposition when we can, since michael@0: // avoiding it can produce better results both for compound michael@0: // transforms and for skew and skewY (see below). We can do this michael@0: // in two cases: michael@0: // (1) if one of the transforms is 'none' michael@0: // (2) if the lists have the same length and the transform michael@0: // functions match michael@0: nsAutoPtr result; michael@0: if (list1->mValue.GetUnit() == eCSSUnit_None) { michael@0: if (list2->mValue.GetUnit() == eCSSUnit_None) { michael@0: result = new nsCSSValueList; michael@0: if (result) { michael@0: result->mValue.SetNoneValue(); michael@0: } michael@0: } else { michael@0: result = AddTransformLists(0, list2, aCoeff2, list2); michael@0: } michael@0: } else { michael@0: if (list2->mValue.GetUnit() == eCSSUnit_None) { michael@0: result = AddTransformLists(0, list1, aCoeff1, list1); michael@0: } else { michael@0: bool match = true; michael@0: michael@0: { michael@0: const nsCSSValueList *item1 = list1, *item2 = list2; michael@0: do { michael@0: nsCSSKeyword func1 = nsStyleTransformMatrix::TransformFunctionOf( michael@0: item1->mValue.GetArrayValue()); michael@0: nsCSSKeyword func2 = nsStyleTransformMatrix::TransformFunctionOf( michael@0: item2->mValue.GetArrayValue()); michael@0: michael@0: if (!TransformFunctionsMatch(func1, func2)) { michael@0: break; michael@0: } michael@0: michael@0: item1 = item1->mNext; michael@0: item2 = item2->mNext; michael@0: } while (item1 && item2); michael@0: if (item1 || item2) { michael@0: // Either |break| above or length mismatch. michael@0: match = false; michael@0: } michael@0: } michael@0: michael@0: if (match) { michael@0: result = AddTransformLists(aCoeff1, list1, aCoeff2, list2); michael@0: } else { michael@0: result = AddDifferentTransformLists(aCoeff1, list1, aCoeff2, list2); michael@0: } michael@0: } michael@0: } michael@0: michael@0: aResultValue.SetTransformValue(new nsCSSValueSharedList(result.forget())); michael@0: return true; michael@0: } michael@0: case eUnit_BackgroundPosition: { michael@0: const nsCSSValueList *position1 = aValue1.GetCSSValueListValue(); michael@0: const nsCSSValueList *position2 = aValue2.GetCSSValueListValue(); michael@0: nsAutoPtr result; michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: while (position1 && position2) { michael@0: nsCSSValueList *item = new nsCSSValueList; michael@0: if (!item) { michael@0: return false; michael@0: } michael@0: *resultTail = item; michael@0: resultTail = &item->mNext; michael@0: michael@0: nsCSSValue::Array* bgPos1 = position1->mValue.GetArrayValue(); michael@0: nsCSSValue::Array* bgPos2 = position2->mValue.GetArrayValue(); michael@0: nsCSSValue::Array* bgPosRes = nsCSSValue::Array::Create(4); michael@0: item->mValue.SetArrayValue(bgPosRes, eCSSUnit_Array); michael@0: michael@0: uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty); michael@0: michael@0: /* Only iterate over elements 1 and 3. The background position is michael@0: * 'uncomputed' to only those elements. michael@0: */ michael@0: for (int i = 1; i < 4; i+=2) { michael@0: const nsCSSValue& v1 = bgPos1->Item(i); michael@0: const nsCSSValue& v2 = bgPos2->Item(i); michael@0: nsCSSValue& vr = bgPosRes->Item(i); michael@0: michael@0: nsCSSUnit unit = GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit()); michael@0: michael@0: if (!AddCSSValuePixelPercentCalc(restrictions, unit, aCoeff1, v1, michael@0: aCoeff2, v2, vr) ) { michael@0: if (v1 != v2) { michael@0: return false; michael@0: } michael@0: vr = v1; michael@0: } michael@0: } michael@0: michael@0: position1 = position1->mNext; michael@0: position2 = position2->mNext; michael@0: } michael@0: michael@0: // Check for different lengths michael@0: if (position1 || position2) { michael@0: return false; michael@0: } michael@0: michael@0: aResultValue.SetAndAdoptCSSValueListValue(result.forget(), michael@0: eUnit_BackgroundPosition); michael@0: return true; michael@0: } michael@0: case eUnit_CSSValuePairList: { michael@0: const nsCSSValuePairList *list1 = aValue1.GetCSSValuePairListValue(); michael@0: const nsCSSValuePairList *list2 = aValue2.GetCSSValuePairListValue(); michael@0: nsAutoPtr result; michael@0: nsCSSValuePairList **resultTail = getter_Transfers(result); michael@0: do { michael@0: nsCSSValuePairList *item = new nsCSSValuePairList; michael@0: if (!item) { michael@0: return false; michael@0: } michael@0: *resultTail = item; michael@0: resultTail = &item->mNext; michael@0: michael@0: static nsCSSValue nsCSSValuePairList::* const pairListValues[] = { michael@0: &nsCSSValuePairList::mXValue, michael@0: &nsCSSValuePairList::mYValue, michael@0: }; michael@0: uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty); michael@0: for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) { michael@0: const nsCSSValue &v1 = list1->*(pairListValues[i]); michael@0: const nsCSSValue &v2 = list2->*(pairListValues[i]); michael@0: nsCSSValue &vr = item->*(pairListValues[i]); michael@0: nsCSSUnit unit = michael@0: GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit()); michael@0: if (unit == eCSSUnit_Null) { michael@0: return false; michael@0: } michael@0: if (!AddCSSValuePixelPercentCalc(restrictions, unit, aCoeff1, v1, michael@0: aCoeff2, v2, vr) ) { michael@0: if (v1 != v2) { michael@0: return false; michael@0: } michael@0: vr = v1; michael@0: } michael@0: } michael@0: list1 = list1->mNext; michael@0: list2 = list2->mNext; michael@0: } while (list1 && list2); michael@0: if (list1 || list2) { michael@0: // We can't interpolate lists of different lengths. michael@0: return false; michael@0: } michael@0: michael@0: aResultValue.SetAndAdoptCSSValuePairListValue(result.forget()); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: NS_ABORT_IF_FALSE(false, "Can't interpolate using the given common unit"); michael@0: return false; michael@0: } michael@0: michael@0: already_AddRefed michael@0: BuildStyleRule(nsCSSProperty aProperty, michael@0: dom::Element* aTargetElement, michael@0: const nsAString& aSpecifiedValue, michael@0: bool aUseSVGMode) michael@0: { michael@0: // Set up an empty CSS Declaration michael@0: nsAutoPtr declaration(new css::Declaration()); michael@0: declaration->InitializeEmpty(); michael@0: michael@0: bool changed; // ignored, but needed as outparam for ParseProperty michael@0: nsIDocument* doc = aTargetElement->OwnerDoc(); michael@0: nsCOMPtr baseURI = aTargetElement->GetBaseURI(); michael@0: nsCSSParser parser(doc->CSSLoader()); michael@0: michael@0: nsCSSProperty propertyToCheck = nsCSSProps::IsShorthand(aProperty) ? michael@0: nsCSSProps::SubpropertyEntryFor(aProperty)[0] : aProperty; michael@0: michael@0: // Get a parser, parse the property, and check for CSS parsing errors. michael@0: // If any of these steps fails, we bail out and delete the declaration. michael@0: if (NS_FAILED(parser.ParseProperty(aProperty, aSpecifiedValue, michael@0: doc->GetDocumentURI(), baseURI, michael@0: aTargetElement->NodePrincipal(), michael@0: declaration, &changed, false, michael@0: aUseSVGMode)) || michael@0: // check whether property parsed without CSS parsing errors michael@0: !declaration->HasNonImportantValueFor(propertyToCheck)) { michael@0: NS_WARNING("failure in BuildStyleRule"); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr rule = new css::StyleRule(nullptr, declaration.forget()); michael@0: return rule.forget(); michael@0: } michael@0: michael@0: inline michael@0: already_AddRefed michael@0: LookupStyleContext(dom::Element* aElement) michael@0: { michael@0: nsIDocument* doc = aElement->GetCurrentDoc(); michael@0: nsIPresShell* shell = doc->GetShell(); michael@0: if (!shell) { michael@0: return nullptr; michael@0: } michael@0: return nsComputedDOMStyle::GetStyleContextForElement(aElement, nullptr, shell); michael@0: } michael@0: michael@0: bool michael@0: nsStyleAnimation::ComputeValue(nsCSSProperty aProperty, michael@0: dom::Element* aTargetElement, michael@0: const nsAString& aSpecifiedValue, michael@0: bool aUseSVGMode, michael@0: Value& aComputedValue, michael@0: bool* aIsContextSensitive) michael@0: { michael@0: NS_ABORT_IF_FALSE(aTargetElement, "null target element"); michael@0: NS_ABORT_IF_FALSE(aTargetElement->GetCurrentDoc(), michael@0: "we should only be able to actively animate nodes that " michael@0: "are in a document"); michael@0: michael@0: nsCSSProperty propToParse = michael@0: nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_REPORT_OTHER_NAME) michael@0: ? nsCSSProps::OtherNameFor(aProperty) : aProperty; michael@0: michael@0: // Parse specified value into a temporary css::StyleRule michael@0: nsRefPtr styleRule = michael@0: BuildStyleRule(propToParse, aTargetElement, aSpecifiedValue, aUseSVGMode); michael@0: if (!styleRule) { michael@0: return false; michael@0: } michael@0: michael@0: if (nsCSSProps::IsShorthand(aProperty) || michael@0: nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) { michael@0: // Just capture the specified value michael@0: aComputedValue.SetUnparsedStringValue(nsString(aSpecifiedValue)); michael@0: if (aIsContextSensitive) { michael@0: // Since we're just returning the string as-is, aComputedValue isn't going michael@0: // to change depending on the context michael@0: *aIsContextSensitive = false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // Look up style context for our target element michael@0: nsRefPtr styleContext = LookupStyleContext(aTargetElement); michael@0: if (!styleContext) { michael@0: return false; michael@0: } michael@0: nsStyleSet* styleSet = styleContext->PresContext()->StyleSet(); michael@0: michael@0: nsRefPtr tmpStyleContext; michael@0: if (aIsContextSensitive) { michael@0: nsCOMArray ruleArray; michael@0: ruleArray.AppendObject(styleSet->InitialStyleRule()); michael@0: ruleArray.AppendObject(styleRule); michael@0: styleRule->RuleMatched(); michael@0: tmpStyleContext = michael@0: styleSet->ResolveStyleByAddingRules(styleContext, ruleArray); michael@0: if (!tmpStyleContext) { michael@0: return false; michael@0: } michael@0: michael@0: // Force walk of rule tree michael@0: nsStyleStructID sid = nsCSSProps::kSIDTable[aProperty]; michael@0: tmpStyleContext->StyleData(sid); michael@0: michael@0: // If the rule node will have cached style data if the value is not michael@0: // context-sensitive. So if there's nothing cached, it's not context michael@0: // sensitive. michael@0: *aIsContextSensitive = michael@0: !tmpStyleContext->RuleNode()->NodeHasCachedData(sid); michael@0: } michael@0: michael@0: // If we're not concerned whether the property is context sensitive then just michael@0: // add the rule to a new temporary style context alongside the target michael@0: // element's style context. michael@0: // Also, if we previously discovered that this property IS context-sensitive michael@0: // then we need to throw the temporary style context out since the property's michael@0: // value may have been biased by the 'initial' values supplied. michael@0: if (!aIsContextSensitive || *aIsContextSensitive) { michael@0: nsCOMArray ruleArray; michael@0: ruleArray.AppendObject(styleRule); michael@0: styleRule->RuleMatched(); michael@0: tmpStyleContext = michael@0: styleSet->ResolveStyleByAddingRules(styleContext, ruleArray); michael@0: if (!tmpStyleContext) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Extract computed value of our property from the temporary style rule michael@0: return ExtractComputedValue(aProperty, tmpStyleContext, aComputedValue); michael@0: } michael@0: michael@0: bool michael@0: nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty, michael@0: const Value& aComputedValue, michael@0: nsCSSValue& aSpecifiedValue) michael@0: { michael@0: switch (aComputedValue.GetUnit()) { michael@0: case eUnit_Normal: michael@0: aSpecifiedValue.SetNormalValue(); michael@0: break; michael@0: case eUnit_Auto: michael@0: aSpecifiedValue.SetAutoValue(); michael@0: break; michael@0: case eUnit_None: michael@0: aSpecifiedValue.SetNoneValue(); michael@0: break; michael@0: case eUnit_Enumerated: michael@0: case eUnit_Visibility: michael@0: aSpecifiedValue. michael@0: SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Enumerated); michael@0: break; michael@0: case eUnit_Integer: michael@0: aSpecifiedValue. michael@0: SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Integer); michael@0: break; michael@0: case eUnit_Coord: michael@0: nscoordToCSSValue(aComputedValue.GetCoordValue(), aSpecifiedValue); michael@0: break; michael@0: case eUnit_Percent: michael@0: aSpecifiedValue.SetPercentValue(aComputedValue.GetPercentValue()); michael@0: break; michael@0: case eUnit_Float: michael@0: aSpecifiedValue. michael@0: SetFloatValue(aComputedValue.GetFloatValue(), eCSSUnit_Number); michael@0: break; michael@0: case eUnit_Color: michael@0: // colors can be alone, or part of a paint server michael@0: aSpecifiedValue.SetColorValue(aComputedValue.GetColorValue()); michael@0: break; michael@0: case eUnit_Calc: { michael@0: nsCSSValue *val = aComputedValue.GetCSSValueValue(); michael@0: NS_ABORT_IF_FALSE(val->GetUnit() == eCSSUnit_Calc, "unexpected unit"); michael@0: aSpecifiedValue = *val; michael@0: break; michael@0: } michael@0: case eUnit_CSSValuePair: { michael@0: // Rule node processing expects pair values to be collapsed to a michael@0: // single value if both halves would be equal, for most but not michael@0: // all properties. At present, all animatable properties that michael@0: // use pairs do expect collapsing. michael@0: const nsCSSValuePair* pair = aComputedValue.GetCSSValuePairValue(); michael@0: if (pair->mXValue == pair->mYValue) { michael@0: aSpecifiedValue = pair->mXValue; michael@0: } else { michael@0: aSpecifiedValue.SetPairValue(pair); michael@0: } michael@0: } break; michael@0: case eUnit_CSSValueTriplet: { michael@0: // Rule node processing expects triplet values to be collapsed to a michael@0: // single value if both halves would be equal, for most but not michael@0: // all properties. At present, all animatable properties that michael@0: // use pairs do expect collapsing. michael@0: const nsCSSValueTriplet* triplet = aComputedValue.GetCSSValueTripletValue(); michael@0: if (triplet->mXValue == triplet->mYValue && triplet->mYValue == triplet->mZValue) { michael@0: aSpecifiedValue = triplet->mXValue; michael@0: } else { michael@0: aSpecifiedValue.SetTripletValue(triplet); michael@0: } michael@0: } break; michael@0: case eUnit_CSSRect: { michael@0: nsCSSRect& rect = aSpecifiedValue.SetRectValue(); michael@0: rect = *aComputedValue.GetCSSRectValue(); michael@0: } break; michael@0: case eUnit_Dasharray: michael@0: case eUnit_Shadow: michael@0: case eUnit_Filter: michael@0: case eUnit_BackgroundPosition: michael@0: aSpecifiedValue. michael@0: SetDependentListValue(aComputedValue.GetCSSValueListValue()); michael@0: break; michael@0: case eUnit_Transform: michael@0: aSpecifiedValue. michael@0: SetSharedListValue(aComputedValue.GetCSSValueSharedListValue()); michael@0: break; michael@0: case eUnit_CSSValuePairList: michael@0: aSpecifiedValue. michael@0: SetDependentPairListValue(aComputedValue.GetCSSValuePairListValue()); michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty, michael@0: const Value& aComputedValue, michael@0: nsAString& aSpecifiedValue) michael@0: { michael@0: aSpecifiedValue.Truncate(); // Clear outparam, if it's not already empty michael@0: michael@0: if (aComputedValue.GetUnit() == eUnit_UnparsedString) { michael@0: aComputedValue.GetStringValue(aSpecifiedValue); michael@0: return true; michael@0: } michael@0: nsCSSValue val; michael@0: if (!nsStyleAnimation::UncomputeValue(aProperty, aComputedValue, val)) { michael@0: return false; michael@0: } michael@0: michael@0: val.AppendToString(aProperty, aSpecifiedValue, nsCSSValue::eNormalized); michael@0: return true; michael@0: } michael@0: michael@0: inline const void* michael@0: StyleDataAtOffset(const void* aStyleStruct, ptrdiff_t aOffset) michael@0: { michael@0: return reinterpret_cast(aStyleStruct) + aOffset; michael@0: } michael@0: michael@0: inline void* michael@0: StyleDataAtOffset(void* aStyleStruct, ptrdiff_t aOffset) michael@0: { michael@0: return reinterpret_cast(aStyleStruct) + aOffset; michael@0: } michael@0: michael@0: static void michael@0: ExtractBorderColor(nsStyleContext* aStyleContext, const void* aStyleBorder, michael@0: mozilla::css::Side aSide, nsStyleAnimation::Value& aComputedValue) michael@0: { michael@0: nscolor color; michael@0: bool foreground; michael@0: static_cast(aStyleBorder)-> michael@0: GetBorderColor(aSide, color, foreground); michael@0: if (foreground) { michael@0: // FIXME: should add test for this michael@0: color = aStyleContext->StyleColor()->mColor; michael@0: } michael@0: aComputedValue.SetColorValue(color); michael@0: } michael@0: michael@0: static bool michael@0: StyleCoordToValue(const nsStyleCoord& aCoord, nsStyleAnimation::Value& aValue) michael@0: { michael@0: switch (aCoord.GetUnit()) { michael@0: case eStyleUnit_Normal: michael@0: aValue.SetNormalValue(); michael@0: break; michael@0: case eStyleUnit_Auto: michael@0: aValue.SetAutoValue(); michael@0: break; michael@0: case eStyleUnit_None: michael@0: aValue.SetNoneValue(); michael@0: break; michael@0: case eStyleUnit_Percent: michael@0: aValue.SetPercentValue(aCoord.GetPercentValue()); michael@0: break; michael@0: case eStyleUnit_Factor: michael@0: aValue.SetFloatValue(aCoord.GetFactorValue()); michael@0: break; michael@0: case eStyleUnit_Coord: michael@0: aValue.SetCoordValue(aCoord.GetCoordValue()); michael@0: break; michael@0: case eStyleUnit_Enumerated: michael@0: aValue.SetIntValue(aCoord.GetIntValue(), michael@0: nsStyleAnimation::eUnit_Enumerated); michael@0: break; michael@0: case eStyleUnit_Integer: michael@0: aValue.SetIntValue(aCoord.GetIntValue(), michael@0: nsStyleAnimation::eUnit_Integer); michael@0: break; michael@0: case eStyleUnit_Calc: { michael@0: nsAutoPtr val(new nsCSSValue); michael@0: SetCalcValue(aCoord.GetCalcValue(), *val); michael@0: aValue.SetAndAdoptCSSValueValue(val.forget(), michael@0: nsStyleAnimation::eUnit_Calc); michael@0: break; michael@0: } michael@0: default: michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue) michael@0: { michael@0: switch (aCoord.GetUnit()) { michael@0: case eStyleUnit_Coord: michael@0: nscoordToCSSValue(aCoord.GetCoordValue(), aCSSValue); michael@0: break; michael@0: case eStyleUnit_Factor: michael@0: aCSSValue.SetFloatValue(aCoord.GetFactorValue(), eCSSUnit_Number); michael@0: break; michael@0: case eStyleUnit_Percent: michael@0: aCSSValue.SetPercentValue(aCoord.GetPercentValue()); michael@0: break; michael@0: case eStyleUnit_Calc: michael@0: SetCalcValue(aCoord.GetCalcValue(), aCSSValue); michael@0: break; michael@0: case eStyleUnit_Degree: michael@0: aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Degree); michael@0: break; michael@0: case eStyleUnit_Grad: michael@0: aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Grad); michael@0: break; michael@0: case eStyleUnit_Radian: michael@0: aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Radian); michael@0: break; michael@0: case eStyleUnit_Turn: michael@0: aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Turn); michael@0: break; michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Assign |aOutput = aInput|, except with any non-pixel lengths michael@0: * replaced with the equivalent in pixels, and any non-canonical calc() michael@0: * expressions replaced with canonical ones. michael@0: */ michael@0: static void michael@0: SubstitutePixelValues(nsStyleContext* aStyleContext, michael@0: const nsCSSValue& aInput, nsCSSValue& aOutput) michael@0: { michael@0: if (aInput.IsCalcUnit()) { michael@0: bool canStoreInRuleTree = true; michael@0: nsRuleNode::ComputedCalc c = michael@0: nsRuleNode::SpecifiedCalcToComputedCalc(aInput, aStyleContext, michael@0: aStyleContext->PresContext(), michael@0: canStoreInRuleTree); michael@0: nsStyleCoord::Calc c2; michael@0: c2.mLength = c.mLength; michael@0: c2.mPercent = c.mPercent; michael@0: c2.mHasPercent = true; // doesn't matter for transform translate michael@0: SetCalcValue(&c2, aOutput); michael@0: } else if (aInput.UnitHasArrayValue()) { michael@0: const nsCSSValue::Array *inputArray = aInput.GetArrayValue(); michael@0: nsRefPtr outputArray = michael@0: nsCSSValue::Array::Create(inputArray->Count()); michael@0: for (size_t i = 0, i_end = inputArray->Count(); i < i_end; ++i) { michael@0: SubstitutePixelValues(aStyleContext, michael@0: inputArray->Item(i), outputArray->Item(i)); michael@0: } michael@0: aOutput.SetArrayValue(outputArray, aInput.GetUnit()); michael@0: } else if (aInput.IsLengthUnit() && michael@0: aInput.GetUnit() != eCSSUnit_Pixel) { michael@0: bool canStoreInRuleTree = true; michael@0: nscoord len = nsRuleNode::CalcLength(aInput, aStyleContext, michael@0: aStyleContext->PresContext(), michael@0: canStoreInRuleTree); michael@0: aOutput.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(len), michael@0: eCSSUnit_Pixel); michael@0: } else { michael@0: aOutput = aInput; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsStyleAnimation::ExtractComputedValue(nsCSSProperty aProperty, michael@0: nsStyleContext* aStyleContext, michael@0: Value& aComputedValue) michael@0: { michael@0: NS_ABORT_IF_FALSE(0 <= aProperty && michael@0: aProperty < eCSSProperty_COUNT_no_shorthands, michael@0: "bad property"); michael@0: const void* styleStruct = michael@0: aStyleContext->StyleData(nsCSSProps::kSIDTable[aProperty]); michael@0: ptrdiff_t ssOffset = nsCSSProps::kStyleStructOffsetTable[aProperty]; michael@0: nsStyleAnimType animType = nsCSSProps::kAnimTypeTable[aProperty]; michael@0: NS_ABORT_IF_FALSE(0 <= ssOffset || animType == eStyleAnimType_Custom, michael@0: "must be dealing with animatable property"); michael@0: switch (animType) { michael@0: case eStyleAnimType_Custom: michael@0: switch (aProperty) { michael@0: // For border-width, ignore the border-image business (which michael@0: // only exists until we update our implementation to the current michael@0: // spec) and use GetComputedBorder michael@0: michael@0: #define BORDER_WIDTH_CASE(prop_, side_) \ michael@0: case prop_: \ michael@0: aComputedValue.SetCoordValue( \ michael@0: static_cast(styleStruct)-> \ michael@0: GetComputedBorder().side_); \ michael@0: break; michael@0: BORDER_WIDTH_CASE(eCSSProperty_border_bottom_width, bottom) michael@0: BORDER_WIDTH_CASE(eCSSProperty_border_left_width_value, left) michael@0: BORDER_WIDTH_CASE(eCSSProperty_border_right_width_value, right) michael@0: BORDER_WIDTH_CASE(eCSSProperty_border_top_width, top) michael@0: #undef BORDER_WIDTH_CASE michael@0: michael@0: case eCSSProperty__moz_column_rule_width: michael@0: aComputedValue.SetCoordValue( michael@0: static_cast(styleStruct)-> michael@0: GetComputedColumnRuleWidth()); michael@0: break; michael@0: michael@0: case eCSSProperty_border_bottom_color: michael@0: ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_BOTTOM, michael@0: aComputedValue); michael@0: break; michael@0: case eCSSProperty_border_left_color_value: michael@0: ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_LEFT, michael@0: aComputedValue); michael@0: break; michael@0: case eCSSProperty_border_right_color_value: michael@0: ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_RIGHT, michael@0: aComputedValue); michael@0: break; michael@0: case eCSSProperty_border_top_color: michael@0: ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_TOP, michael@0: aComputedValue); michael@0: break; michael@0: michael@0: case eCSSProperty_outline_color: { michael@0: const nsStyleOutline *styleOutline = michael@0: static_cast(styleStruct); michael@0: nscolor color; michael@0: if (!styleOutline->GetOutlineColor(color)) michael@0: color = aStyleContext->StyleColor()->mColor; michael@0: aComputedValue.SetColorValue(color); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty__moz_column_rule_color: { michael@0: const nsStyleColumn *styleColumn = michael@0: static_cast(styleStruct); michael@0: nscolor color; michael@0: if (styleColumn->mColumnRuleColorIsForeground) { michael@0: color = aStyleContext->StyleColor()->mColor; michael@0: } else { michael@0: color = styleColumn->mColumnRuleColor; michael@0: } michael@0: aComputedValue.SetColorValue(color); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty__moz_column_count: { michael@0: const nsStyleColumn *styleColumn = michael@0: static_cast(styleStruct); michael@0: if (styleColumn->mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO) { michael@0: aComputedValue.SetAutoValue(); michael@0: } else { michael@0: aComputedValue.SetIntValue(styleColumn->mColumnCount, michael@0: eUnit_Integer); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_order: { michael@0: const nsStylePosition *stylePosition = michael@0: static_cast(styleStruct); michael@0: aComputedValue.SetIntValue(stylePosition->mOrder, michael@0: eUnit_Integer); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_text_decoration_color: { michael@0: const nsStyleTextReset *styleTextReset = michael@0: static_cast(styleStruct); michael@0: nscolor color; michael@0: bool isForeground; michael@0: styleTextReset->GetDecorationColor(color, isForeground); michael@0: if (isForeground) { michael@0: color = aStyleContext->StyleColor()->mColor; michael@0: } michael@0: aComputedValue.SetColorValue(color); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_text_decoration_style: { michael@0: uint8_t decorationStyle = michael@0: static_cast(styleStruct)-> michael@0: GetDecorationStyle(); michael@0: aComputedValue.SetIntValue(decorationStyle, eUnit_Enumerated); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_border_spacing: { michael@0: const nsStyleTableBorder *styleTableBorder = michael@0: static_cast(styleStruct); michael@0: nsAutoPtr pair(new nsCSSValuePair); michael@0: if (!pair) { michael@0: return false; michael@0: } michael@0: nscoordToCSSValue(styleTableBorder->mBorderSpacingX, pair->mXValue); michael@0: nscoordToCSSValue(styleTableBorder->mBorderSpacingY, pair->mYValue); michael@0: aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(), michael@0: eUnit_CSSValuePair); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_transform_origin: { michael@0: const nsStyleDisplay *styleDisplay = michael@0: static_cast(styleStruct); michael@0: nsAutoPtr triplet(new nsCSSValueTriplet); michael@0: if (!triplet || michael@0: !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[0], michael@0: triplet->mXValue) || michael@0: !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[1], michael@0: triplet->mYValue) || michael@0: !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[2], michael@0: triplet->mZValue)) { michael@0: return false; michael@0: } michael@0: aComputedValue.SetAndAdoptCSSValueTripletValue(triplet.forget(), michael@0: eUnit_CSSValueTriplet); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_perspective_origin: { michael@0: const nsStyleDisplay *styleDisplay = michael@0: static_cast(styleStruct); michael@0: nsAutoPtr pair(new nsCSSValuePair); michael@0: if (!pair || michael@0: !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[0], michael@0: pair->mXValue) || michael@0: !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[1], michael@0: pair->mYValue)) { michael@0: return false; michael@0: } michael@0: aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(), michael@0: eUnit_CSSValuePair); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_stroke_dasharray: { michael@0: const nsStyleSVG *svg = static_cast(styleStruct); michael@0: NS_ABORT_IF_FALSE((svg->mStrokeDasharray != nullptr) == michael@0: (svg->mStrokeDasharrayLength != 0), michael@0: "pointer/length mismatch"); michael@0: nsAutoPtr result; michael@0: if (svg->mStrokeDasharray) { michael@0: NS_ABORT_IF_FALSE(svg->mStrokeDasharrayLength > 0, michael@0: "non-null list should have positive length"); michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: for (uint32_t i = 0, i_end = svg->mStrokeDasharrayLength; michael@0: i != i_end; ++i) { michael@0: nsCSSValueList *item = new nsCSSValueList; michael@0: if (!item) { michael@0: return false; michael@0: } michael@0: *resultTail = item; michael@0: resultTail = &item->mNext; michael@0: michael@0: const nsStyleCoord &coord = svg->mStrokeDasharray[i]; michael@0: nsCSSValue &value = item->mValue; michael@0: switch (coord.GetUnit()) { michael@0: case eStyleUnit_Coord: michael@0: // Number means the same thing as length; we want to michael@0: // animate them the same way. Normalize both to number michael@0: // since it has more accuracy (float vs nscoord). michael@0: value.SetFloatValue(nsPresContext:: michael@0: AppUnitsToFloatCSSPixels(coord.GetCoordValue()), michael@0: eCSSUnit_Number); michael@0: break; michael@0: case eStyleUnit_Factor: michael@0: value.SetFloatValue(coord.GetFactorValue(), michael@0: eCSSUnit_Number); michael@0: break; michael@0: case eStyleUnit_Percent: michael@0: value.SetPercentValue(coord.GetPercentValue()); michael@0: break; michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return false; michael@0: } michael@0: } michael@0: } else { michael@0: result = new nsCSSValueList; michael@0: if (!result) { michael@0: return false; michael@0: } michael@0: result->mValue.SetNoneValue(); michael@0: } michael@0: aComputedValue.SetAndAdoptCSSValueListValue(result.forget(), michael@0: eUnit_Dasharray); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_font_stretch: { michael@0: int16_t stretch = michael@0: static_cast(styleStruct)->mFont.stretch; michael@0: static_assert(NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED == -4 && michael@0: NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED == 4, michael@0: "font stretch constants not as expected"); michael@0: if (stretch < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED || michael@0: stretch > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) { michael@0: return false; michael@0: } michael@0: aComputedValue.SetIntValue(stretch, eUnit_Enumerated); michael@0: return true; michael@0: } michael@0: michael@0: case eCSSProperty_font_weight: { michael@0: uint16_t weight = michael@0: static_cast(styleStruct)->mFont.weight; michael@0: if (weight % 100 != 0) { michael@0: return false; michael@0: } michael@0: aComputedValue.SetIntValue(weight, eUnit_Integer); michael@0: return true; michael@0: } michael@0: michael@0: case eCSSProperty_image_region: { michael@0: const nsStyleList *list = michael@0: static_cast(styleStruct); michael@0: const nsRect &srect = list->mImageRegion; michael@0: if (srect.IsEmpty()) { michael@0: aComputedValue.SetAutoValue(); michael@0: break; michael@0: } michael@0: michael@0: nsCSSRect *vrect = new nsCSSRect; michael@0: nscoordToCSSValue(srect.x, vrect->mLeft); michael@0: nscoordToCSSValue(srect.y, vrect->mTop); michael@0: nscoordToCSSValue(srect.XMost(), vrect->mRight); michael@0: nscoordToCSSValue(srect.YMost(), vrect->mBottom); michael@0: aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_clip: { michael@0: const nsStyleDisplay *display = michael@0: static_cast(styleStruct); michael@0: if (!(display->mClipFlags & NS_STYLE_CLIP_RECT)) { michael@0: aComputedValue.SetAutoValue(); michael@0: } else { michael@0: nsCSSRect *vrect = new nsCSSRect; michael@0: const nsRect &srect = display->mClip; michael@0: if (display->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) { michael@0: vrect->mTop.SetAutoValue(); michael@0: } else { michael@0: nscoordToCSSValue(srect.y, vrect->mTop); michael@0: } michael@0: if (display->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) { michael@0: vrect->mRight.SetAutoValue(); michael@0: } else { michael@0: nscoordToCSSValue(srect.XMost(), vrect->mRight); michael@0: } michael@0: if (display->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) { michael@0: vrect->mBottom.SetAutoValue(); michael@0: } else { michael@0: nscoordToCSSValue(srect.YMost(), vrect->mBottom); michael@0: } michael@0: if (display->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) { michael@0: vrect->mLeft.SetAutoValue(); michael@0: } else { michael@0: nscoordToCSSValue(srect.x, vrect->mLeft); michael@0: } michael@0: aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_background_position: { michael@0: const nsStyleBackground *bg = michael@0: static_cast(styleStruct); michael@0: nsAutoPtr result; michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: NS_ABORT_IF_FALSE(bg->mPositionCount > 0, "unexpected count"); michael@0: for (uint32_t i = 0, i_end = bg->mPositionCount; i != i_end; ++i) { michael@0: nsCSSValueList *item = new nsCSSValueList; michael@0: *resultTail = item; michael@0: resultTail = &item->mNext; michael@0: nsRefPtr bgArray = nsCSSValue::Array::Create(4); michael@0: item->mValue.SetArrayValue(bgArray.get(), eCSSUnit_Array); michael@0: michael@0: const nsStyleBackground::Position &pos = bg->mLayers[i].mPosition; michael@0: // XXXbz is there a good reason we can't just michael@0: // SetCalcValue(&pos.mXPosition, item->mXValue) here? michael@0: nsCSSValue &xValue = bgArray->Item(1), michael@0: &yValue = bgArray->Item(3); michael@0: if (!pos.mXPosition.mHasPercent) { michael@0: NS_ABORT_IF_FALSE(pos.mXPosition.mPercent == 0.0f, michael@0: "Shouldn't have mPercent!"); michael@0: nscoordToCSSValue(pos.mXPosition.mLength, xValue); michael@0: } else if (pos.mXPosition.mLength == 0) { michael@0: xValue.SetPercentValue(pos.mXPosition.mPercent); michael@0: } else { michael@0: SetCalcValue(&pos.mXPosition, xValue); michael@0: } michael@0: michael@0: if (!pos.mYPosition.mHasPercent) { michael@0: NS_ABORT_IF_FALSE(pos.mYPosition.mPercent == 0.0f, michael@0: "Shouldn't have mPercent!"); michael@0: nscoordToCSSValue(pos.mYPosition.mLength, yValue); michael@0: } else if (pos.mYPosition.mLength == 0) { michael@0: yValue.SetPercentValue(pos.mYPosition.mPercent); michael@0: } else { michael@0: SetCalcValue(&pos.mYPosition, yValue); michael@0: } michael@0: } michael@0: michael@0: aComputedValue.SetAndAdoptCSSValueListValue(result.forget(), michael@0: eUnit_BackgroundPosition); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_background_size: { michael@0: const nsStyleBackground *bg = michael@0: static_cast(styleStruct); michael@0: nsAutoPtr result; michael@0: nsCSSValuePairList **resultTail = getter_Transfers(result); michael@0: NS_ABORT_IF_FALSE(bg->mSizeCount > 0, "unexpected count"); michael@0: for (uint32_t i = 0, i_end = bg->mSizeCount; i != i_end; ++i) { michael@0: nsCSSValuePairList *item = new nsCSSValuePairList; michael@0: *resultTail = item; michael@0: resultTail = &item->mNext; michael@0: michael@0: const nsStyleBackground::Size &size = bg->mLayers[i].mSize; michael@0: switch (size.mWidthType) { michael@0: case nsStyleBackground::Size::eContain: michael@0: case nsStyleBackground::Size::eCover: michael@0: item->mXValue.SetIntValue(size.mWidthType, michael@0: eCSSUnit_Enumerated); michael@0: break; michael@0: case nsStyleBackground::Size::eAuto: michael@0: item->mXValue.SetAutoValue(); michael@0: break; michael@0: case nsStyleBackground::Size::eLengthPercentage: michael@0: // XXXbz is there a good reason we can't just michael@0: // SetCalcValue(&size.mWidth, item->mXValue) here? michael@0: if (!size.mWidth.mHasPercent && michael@0: // negative values must have come from calc() michael@0: size.mWidth.mLength >= 0) { michael@0: NS_ABORT_IF_FALSE(size.mWidth.mPercent == 0.0f, michael@0: "Shouldn't have mPercent"); michael@0: nscoordToCSSValue(size.mWidth.mLength, item->mXValue); michael@0: } else if (size.mWidth.mLength == 0 && michael@0: // negative values must have come from calc() michael@0: size.mWidth.mPercent >= 0.0f) { michael@0: item->mXValue.SetPercentValue(size.mWidth.mPercent); michael@0: } else { michael@0: SetCalcValue(&size.mWidth, item->mXValue); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: switch (size.mHeightType) { michael@0: case nsStyleBackground::Size::eContain: michael@0: case nsStyleBackground::Size::eCover: michael@0: // leave it null michael@0: break; michael@0: case nsStyleBackground::Size::eAuto: michael@0: item->mYValue.SetAutoValue(); michael@0: break; michael@0: case nsStyleBackground::Size::eLengthPercentage: michael@0: // XXXbz is there a good reason we can't just michael@0: // SetCalcValue(&size.mHeight, item->mYValue) here? michael@0: if (!size.mHeight.mHasPercent && michael@0: // negative values must have come from calc() michael@0: size.mHeight.mLength >= 0) { michael@0: NS_ABORT_IF_FALSE(size.mHeight.mPercent == 0.0f, michael@0: "Shouldn't have mPercent"); michael@0: nscoordToCSSValue(size.mHeight.mLength, item->mYValue); michael@0: } else if (size.mHeight.mLength == 0 && michael@0: // negative values must have come from calc() michael@0: size.mHeight.mPercent >= 0.0f) { michael@0: item->mYValue.SetPercentValue(size.mHeight.mPercent); michael@0: } else { michael@0: SetCalcValue(&size.mHeight, item->mYValue); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: aComputedValue.SetAndAdoptCSSValuePairListValue(result.forget()); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_filter: { michael@0: const nsStyleSVGReset *svgReset = michael@0: static_cast(styleStruct); michael@0: const nsTArray& filters = svgReset->mFilters; michael@0: nsAutoPtr result; michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: for (uint32_t i = 0; i < filters.Length(); ++i) { michael@0: nsCSSValueList *item = new nsCSSValueList; michael@0: *resultTail = item; michael@0: resultTail = &item->mNext; michael@0: const nsStyleFilter& filter = filters[i]; michael@0: int32_t type = filter.GetType(); michael@0: if (type == NS_STYLE_FILTER_URL) { michael@0: nsIDocument* doc = aStyleContext->PresContext()->Document(); michael@0: nsRefPtr uriAsStringBuffer = michael@0: GetURIAsUtf16StringBuffer(filter.GetURL()); michael@0: nsRefPtr url = michael@0: new mozilla::css::URLValue(filter.GetURL(), michael@0: uriAsStringBuffer, michael@0: doc->GetDocumentURI(), michael@0: doc->NodePrincipal()); michael@0: item->mValue.SetURLValue(url); michael@0: } else { michael@0: nsCSSKeyword functionName = michael@0: nsCSSProps::ValueToKeywordEnum(type, michael@0: nsCSSProps::kFilterFunctionKTable); michael@0: nsCSSValue::Array* filterArray = michael@0: item->mValue.InitFunction(functionName, 1); michael@0: if (type >= NS_STYLE_FILTER_BLUR && type <= NS_STYLE_FILTER_HUE_ROTATE) { michael@0: if (!StyleCoordToCSSValue( michael@0: filter.GetFilterParameter(), michael@0: filterArray->Item(1))) { michael@0: return false; michael@0: } michael@0: } else if (type == NS_STYLE_FILTER_DROP_SHADOW) { michael@0: nsCSSValueList* shadowResult = filterArray->Item(1).SetListValue(); michael@0: nsAutoPtr tmpShadowValue; michael@0: nsCSSValueList **tmpShadowResultTail = getter_Transfers(tmpShadowValue); michael@0: nsCSSShadowArray* shadowArray = filter.GetDropShadow(); michael@0: NS_ABORT_IF_FALSE(shadowArray->Length() == 1, michael@0: "expected exactly one shadow"); michael@0: AppendCSSShadowValue(shadowArray->ShadowAt(0), tmpShadowResultTail); michael@0: *shadowResult = *tmpShadowValue; michael@0: } else { michael@0: // We checked all possible nsStyleFilter types but michael@0: // NS_STYLE_FILTER_NULL before. We should never enter this path. michael@0: NS_NOTREACHED("no other filter functions defined"); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: aComputedValue.SetAndAdoptCSSValueListValue(result.forget(), michael@0: eUnit_Filter); michael@0: break; michael@0: } michael@0: michael@0: case eCSSProperty_transform: { michael@0: const nsStyleDisplay *display = michael@0: static_cast(styleStruct); michael@0: nsAutoPtr result; michael@0: if (display->mSpecifiedTransform) { michael@0: // Clone, and convert all lengths (not percents) to pixels. michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: for (const nsCSSValueList *l = display->mSpecifiedTransform->mHead; michael@0: l; l = l->mNext) { michael@0: nsCSSValueList *clone = new nsCSSValueList; michael@0: *resultTail = clone; michael@0: resultTail = &clone->mNext; michael@0: michael@0: SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue); michael@0: } michael@0: } else { michael@0: result = new nsCSSValueList(); michael@0: result->mValue.SetNoneValue(); michael@0: } michael@0: michael@0: aComputedValue.SetTransformValue( michael@0: new nsCSSValueSharedList(result.forget())); michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "missing property implementation"); michael@0: return false; michael@0: }; michael@0: return true; michael@0: case eStyleAnimType_Coord: michael@0: return StyleCoordToValue(*static_cast( michael@0: StyleDataAtOffset(styleStruct, ssOffset)), aComputedValue); michael@0: case eStyleAnimType_Sides_Top: michael@0: case eStyleAnimType_Sides_Right: michael@0: case eStyleAnimType_Sides_Bottom: michael@0: case eStyleAnimType_Sides_Left: { michael@0: static_assert( michael@0: NS_SIDE_TOP == eStyleAnimType_Sides_Top -eStyleAnimType_Sides_Top && michael@0: NS_SIDE_RIGHT == eStyleAnimType_Sides_Right -eStyleAnimType_Sides_Top && michael@0: NS_SIDE_BOTTOM == eStyleAnimType_Sides_Bottom-eStyleAnimType_Sides_Top && michael@0: NS_SIDE_LEFT == eStyleAnimType_Sides_Left -eStyleAnimType_Sides_Top, michael@0: "box side constants out of sync with animation side constants"); michael@0: michael@0: const nsStyleCoord &coord = static_cast( michael@0: StyleDataAtOffset(styleStruct, ssOffset))-> michael@0: Get(mozilla::css::Side(animType - eStyleAnimType_Sides_Top)); michael@0: return StyleCoordToValue(coord, aComputedValue); michael@0: } michael@0: case eStyleAnimType_Corner_TopLeft: michael@0: case eStyleAnimType_Corner_TopRight: michael@0: case eStyleAnimType_Corner_BottomRight: michael@0: case eStyleAnimType_Corner_BottomLeft: { michael@0: static_assert( michael@0: NS_CORNER_TOP_LEFT == eStyleAnimType_Corner_TopLeft - michael@0: eStyleAnimType_Corner_TopLeft && michael@0: NS_CORNER_TOP_RIGHT == eStyleAnimType_Corner_TopRight - michael@0: eStyleAnimType_Corner_TopLeft && michael@0: NS_CORNER_BOTTOM_RIGHT == eStyleAnimType_Corner_BottomRight - michael@0: eStyleAnimType_Corner_TopLeft && michael@0: NS_CORNER_BOTTOM_LEFT == eStyleAnimType_Corner_BottomLeft - michael@0: eStyleAnimType_Corner_TopLeft, michael@0: "box corner constants out of sync with animation corner constants"); michael@0: michael@0: const nsStyleCorners *corners = static_cast( michael@0: StyleDataAtOffset(styleStruct, ssOffset)); michael@0: uint8_t fullCorner = animType - eStyleAnimType_Corner_TopLeft; michael@0: const nsStyleCoord &horiz = michael@0: corners->Get(NS_FULL_TO_HALF_CORNER(fullCorner, false)); michael@0: const nsStyleCoord &vert = michael@0: corners->Get(NS_FULL_TO_HALF_CORNER(fullCorner, true)); michael@0: nsAutoPtr pair(new nsCSSValuePair); michael@0: if (!pair || michael@0: !StyleCoordToCSSValue(horiz, pair->mXValue) || michael@0: !StyleCoordToCSSValue(vert, pair->mYValue)) { michael@0: return false; michael@0: } michael@0: aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(), michael@0: eUnit_CSSValuePair); michael@0: return true; michael@0: } michael@0: case eStyleAnimType_nscoord: michael@0: aComputedValue.SetCoordValue(*static_cast( michael@0: StyleDataAtOffset(styleStruct, ssOffset))); michael@0: return true; michael@0: case eStyleAnimType_EnumU8: michael@0: aComputedValue.SetIntValue(*static_cast( michael@0: StyleDataAtOffset(styleStruct, ssOffset)), eUnit_Enumerated); michael@0: return true; michael@0: case eStyleAnimType_float: michael@0: aComputedValue.SetFloatValue(*static_cast( michael@0: StyleDataAtOffset(styleStruct, ssOffset))); michael@0: if (aProperty == eCSSProperty_font_size_adjust && michael@0: aComputedValue.GetFloatValue() == 0.0f) { michael@0: // In nsStyleFont, we set mFont.sizeAdjust to 0 to represent michael@0: // font-size-adjust: none. Here, we have to treat this as a keyword michael@0: // instead of a float value, to make sure we don't end up doing michael@0: // interpolation with it. michael@0: aComputedValue.SetNoneValue(); michael@0: } michael@0: return true; michael@0: case eStyleAnimType_Color: michael@0: aComputedValue.SetColorValue(*static_cast( michael@0: StyleDataAtOffset(styleStruct, ssOffset))); michael@0: return true; michael@0: case eStyleAnimType_PaintServer: { michael@0: const nsStyleSVGPaint &paint = *static_cast( michael@0: StyleDataAtOffset(styleStruct, ssOffset)); michael@0: if (paint.mType == eStyleSVGPaintType_Color) { michael@0: aComputedValue.SetColorValue(paint.mPaint.mColor); michael@0: return true; michael@0: } michael@0: if (paint.mType == eStyleSVGPaintType_Server) { michael@0: if (!paint.mPaint.mPaintServer) { michael@0: NS_WARNING("Null paint server"); michael@0: return false; michael@0: } michael@0: nsAutoPtr pair(new nsCSSValuePair); michael@0: nsRefPtr uriAsStringBuffer = michael@0: GetURIAsUtf16StringBuffer(paint.mPaint.mPaintServer); michael@0: NS_ENSURE_TRUE(!!uriAsStringBuffer, false); michael@0: nsIDocument* doc = aStyleContext->PresContext()->Document(); michael@0: nsRefPtr url = michael@0: new mozilla::css::URLValue(paint.mPaint.mPaintServer, michael@0: uriAsStringBuffer, michael@0: doc->GetDocumentURI(), michael@0: doc->NodePrincipal()); michael@0: pair->mXValue.SetURLValue(url); michael@0: pair->mYValue.SetColorValue(paint.mFallbackColor); michael@0: aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(), michael@0: eUnit_CSSValuePair); michael@0: return true; michael@0: } michael@0: if (paint.mType == eStyleSVGPaintType_ContextFill || michael@0: paint.mType == eStyleSVGPaintType_ContextStroke) { michael@0: nsAutoPtr pair(new nsCSSValuePair); michael@0: pair->mXValue.SetIntValue(paint.mType == eStyleSVGPaintType_ContextFill ? michael@0: NS_COLOR_CONTEXT_FILL : NS_COLOR_CONTEXT_STROKE, michael@0: eCSSUnit_Enumerated); michael@0: pair->mYValue.SetColorValue(paint.mFallbackColor); michael@0: aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(), michael@0: eUnit_CSSValuePair); michael@0: return true; michael@0: } michael@0: NS_ABORT_IF_FALSE(paint.mType == eStyleSVGPaintType_None, michael@0: "Unexpected SVG paint type"); michael@0: aComputedValue.SetNoneValue(); michael@0: return true; michael@0: } michael@0: case eStyleAnimType_Shadow: { michael@0: const nsCSSShadowArray *shadowArray = michael@0: *static_cast*>( michael@0: StyleDataAtOffset(styleStruct, ssOffset)); michael@0: if (!shadowArray) { michael@0: aComputedValue.SetAndAdoptCSSValueListValue(nullptr, eUnit_Shadow); michael@0: return true; michael@0: } michael@0: nsAutoPtr result; michael@0: nsCSSValueList **resultTail = getter_Transfers(result); michael@0: for (uint32_t i = 0, i_end = shadowArray->Length(); i < i_end; ++i) { michael@0: AppendCSSShadowValue(shadowArray->ShadowAt(i), resultTail); michael@0: } michael@0: aComputedValue.SetAndAdoptCSSValueListValue(result.forget(), michael@0: eUnit_Shadow); michael@0: return true; michael@0: } michael@0: case eStyleAnimType_None: michael@0: NS_NOTREACHED("shouldn't use on non-animatable properties"); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsStyleAnimation::Value::Value(int32_t aInt, Unit aUnit, michael@0: IntegerConstructorType) michael@0: { michael@0: NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type"); michael@0: mUnit = aUnit; michael@0: mValue.mInt = aInt; michael@0: } michael@0: michael@0: nsStyleAnimation::Value::Value(nscoord aLength, CoordConstructorType) michael@0: { michael@0: mUnit = eUnit_Coord; michael@0: mValue.mCoord = aLength; michael@0: } michael@0: michael@0: nsStyleAnimation::Value::Value(float aPercent, PercentConstructorType) michael@0: { michael@0: mUnit = eUnit_Percent; michael@0: mValue.mFloat = aPercent; michael@0: MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat)); michael@0: } michael@0: michael@0: nsStyleAnimation::Value::Value(float aFloat, FloatConstructorType) michael@0: { michael@0: mUnit = eUnit_Float; michael@0: mValue.mFloat = aFloat; michael@0: MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat)); michael@0: } michael@0: michael@0: nsStyleAnimation::Value::Value(nscolor aColor, ColorConstructorType) michael@0: { michael@0: mUnit = eUnit_Color; michael@0: mValue.mColor = aColor; michael@0: } michael@0: michael@0: nsStyleAnimation::Value& michael@0: nsStyleAnimation::Value::operator=(const Value& aOther) michael@0: { michael@0: FreeValue(); michael@0: michael@0: mUnit = aOther.mUnit; michael@0: switch (mUnit) { michael@0: case eUnit_Null: michael@0: case eUnit_Normal: michael@0: case eUnit_Auto: michael@0: case eUnit_None: michael@0: break; michael@0: case eUnit_Enumerated: michael@0: case eUnit_Visibility: michael@0: case eUnit_Integer: michael@0: mValue.mInt = aOther.mValue.mInt; michael@0: break; michael@0: case eUnit_Coord: michael@0: mValue.mCoord = aOther.mValue.mCoord; michael@0: break; michael@0: case eUnit_Percent: michael@0: case eUnit_Float: michael@0: mValue.mFloat = aOther.mValue.mFloat; michael@0: MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat)); michael@0: break; michael@0: case eUnit_Color: michael@0: mValue.mColor = aOther.mValue.mColor; michael@0: break; michael@0: case eUnit_Calc: michael@0: NS_ABORT_IF_FALSE(aOther.mValue.mCSSValue, "values may not be null"); michael@0: mValue.mCSSValue = new nsCSSValue(*aOther.mValue.mCSSValue); michael@0: if (!mValue.mCSSValue) { michael@0: mUnit = eUnit_Null; michael@0: } michael@0: break; michael@0: case eUnit_CSSValuePair: michael@0: NS_ABORT_IF_FALSE(aOther.mValue.mCSSValuePair, michael@0: "value pairs may not be null"); michael@0: mValue.mCSSValuePair = new nsCSSValuePair(*aOther.mValue.mCSSValuePair); michael@0: if (!mValue.mCSSValuePair) { michael@0: mUnit = eUnit_Null; michael@0: } michael@0: break; michael@0: case eUnit_CSSValueTriplet: michael@0: NS_ABORT_IF_FALSE(aOther.mValue.mCSSValueTriplet, michael@0: "value triplets may not be null"); michael@0: mValue.mCSSValueTriplet = new nsCSSValueTriplet(*aOther.mValue.mCSSValueTriplet); michael@0: if (!mValue.mCSSValueTriplet) { michael@0: mUnit = eUnit_Null; michael@0: } michael@0: break; michael@0: case eUnit_CSSRect: michael@0: NS_ABORT_IF_FALSE(aOther.mValue.mCSSRect, "rects may not be null"); michael@0: mValue.mCSSRect = new nsCSSRect(*aOther.mValue.mCSSRect); michael@0: if (!mValue.mCSSRect) { michael@0: mUnit = eUnit_Null; michael@0: } michael@0: break; michael@0: case eUnit_Filter: michael@0: case eUnit_Dasharray: michael@0: case eUnit_Shadow: michael@0: case eUnit_BackgroundPosition: michael@0: NS_ABORT_IF_FALSE(mUnit == eUnit_Shadow || mUnit == eUnit_Filter || michael@0: aOther.mValue.mCSSValueList, michael@0: "value lists other than shadows and filters may not be null"); michael@0: if (aOther.mValue.mCSSValueList) { michael@0: mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone(); michael@0: if (!mValue.mCSSValueList) { michael@0: mUnit = eUnit_Null; michael@0: } michael@0: } else { michael@0: mValue.mCSSValueList = nullptr; michael@0: } michael@0: break; michael@0: case eUnit_Transform: michael@0: mValue.mCSSValueSharedList = aOther.mValue.mCSSValueSharedList; michael@0: mValue.mCSSValueSharedList->AddRef(); michael@0: break; michael@0: case eUnit_CSSValuePairList: michael@0: NS_ABORT_IF_FALSE(aOther.mValue.mCSSValuePairList, michael@0: "value pair lists may not be null"); michael@0: mValue.mCSSValuePairList = aOther.mValue.mCSSValuePairList->Clone(); michael@0: if (!mValue.mCSSValuePairList) { michael@0: mUnit = eUnit_Null; michael@0: } michael@0: break; michael@0: case eUnit_UnparsedString: michael@0: NS_ABORT_IF_FALSE(aOther.mValue.mString, "expecting non-null string"); michael@0: mValue.mString = aOther.mValue.mString; michael@0: mValue.mString->AddRef(); michael@0: break; michael@0: } michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetNormalValue() michael@0: { michael@0: FreeValue(); michael@0: mUnit = eUnit_Normal; michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetAutoValue() michael@0: { michael@0: FreeValue(); michael@0: mUnit = eUnit_Auto; michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetNoneValue() michael@0: { michael@0: FreeValue(); michael@0: mUnit = eUnit_None; michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetIntValue(int32_t aInt, Unit aUnit) michael@0: { michael@0: NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type"); michael@0: FreeValue(); michael@0: mUnit = aUnit; michael@0: mValue.mInt = aInt; michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetCoordValue(nscoord aLength) michael@0: { michael@0: FreeValue(); michael@0: mUnit = eUnit_Coord; michael@0: mValue.mCoord = aLength; michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetPercentValue(float aPercent) michael@0: { michael@0: FreeValue(); michael@0: mUnit = eUnit_Percent; michael@0: mValue.mFloat = aPercent; michael@0: MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat)); michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetFloatValue(float aFloat) michael@0: { michael@0: FreeValue(); michael@0: mUnit = eUnit_Float; michael@0: mValue.mFloat = aFloat; michael@0: MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat)); michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetColorValue(nscolor aColor) michael@0: { michael@0: FreeValue(); michael@0: mUnit = eUnit_Color; michael@0: mValue.mColor = aColor; michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetUnparsedStringValue(const nsString& aString) michael@0: { michael@0: FreeValue(); michael@0: mUnit = eUnit_UnparsedString; michael@0: mValue.mString = nsCSSValue::BufferFromString(aString).take(); michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetAndAdoptCSSValueValue(nsCSSValue *aValue, michael@0: Unit aUnit) michael@0: { michael@0: FreeValue(); michael@0: NS_ABORT_IF_FALSE(IsCSSValueUnit(aUnit), "bad unit"); michael@0: NS_ABORT_IF_FALSE(aValue != nullptr, "values may not be null"); michael@0: mUnit = aUnit; michael@0: mValue.mCSSValue = aValue; // take ownership michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetAndAdoptCSSValuePairValue( michael@0: nsCSSValuePair *aValuePair, Unit aUnit) michael@0: { michael@0: FreeValue(); michael@0: NS_ABORT_IF_FALSE(IsCSSValuePairUnit(aUnit), "bad unit"); michael@0: NS_ABORT_IF_FALSE(aValuePair != nullptr, "value pairs may not be null"); michael@0: mUnit = aUnit; michael@0: mValue.mCSSValuePair = aValuePair; // take ownership michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetAndAdoptCSSValueTripletValue( michael@0: nsCSSValueTriplet *aValueTriplet, Unit aUnit) michael@0: { michael@0: FreeValue(); michael@0: NS_ABORT_IF_FALSE(IsCSSValueTripletUnit(aUnit), "bad unit"); michael@0: NS_ABORT_IF_FALSE(aValueTriplet != nullptr, "value pairs may not be null"); michael@0: mUnit = aUnit; michael@0: mValue.mCSSValueTriplet = aValueTriplet; // take ownership michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetAndAdoptCSSRectValue(nsCSSRect *aRect, Unit aUnit) michael@0: { michael@0: FreeValue(); michael@0: NS_ABORT_IF_FALSE(IsCSSRectUnit(aUnit), "bad unit"); michael@0: NS_ABORT_IF_FALSE(aRect != nullptr, "value pairs may not be null"); michael@0: mUnit = aUnit; michael@0: mValue.mCSSRect = aRect; // take ownership michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetAndAdoptCSSValueListValue( michael@0: nsCSSValueList *aValueList, Unit aUnit) michael@0: { michael@0: FreeValue(); michael@0: NS_ABORT_IF_FALSE(IsCSSValueListUnit(aUnit), "bad unit"); michael@0: NS_ABORT_IF_FALSE(aUnit == eUnit_Shadow || aUnit == eUnit_Filter || michael@0: aValueList != nullptr, michael@0: "value lists other than shadows and filters may not be null"); michael@0: mUnit = aUnit; michael@0: mValue.mCSSValueList = aValueList; // take ownership michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetTransformValue(nsCSSValueSharedList* aList) michael@0: { michael@0: FreeValue(); michael@0: mUnit = eUnit_Transform; michael@0: mValue.mCSSValueSharedList = aList; michael@0: mValue.mCSSValueSharedList->AddRef(); michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::SetAndAdoptCSSValuePairListValue( michael@0: nsCSSValuePairList *aValuePairList) michael@0: { michael@0: FreeValue(); michael@0: NS_ABORT_IF_FALSE(aValuePairList, "may not be null"); michael@0: mUnit = eUnit_CSSValuePairList; michael@0: mValue.mCSSValuePairList = aValuePairList; // take ownership michael@0: } michael@0: michael@0: void michael@0: nsStyleAnimation::Value::FreeValue() michael@0: { michael@0: if (IsCSSValueUnit(mUnit)) { michael@0: delete mValue.mCSSValue; michael@0: } else if (IsCSSValueListUnit(mUnit)) { michael@0: delete mValue.mCSSValueList; michael@0: } else if (IsCSSValueSharedListValue(mUnit)) { michael@0: mValue.mCSSValueSharedList->Release(); michael@0: } else if (IsCSSValuePairUnit(mUnit)) { michael@0: delete mValue.mCSSValuePair; michael@0: } else if (IsCSSValueTripletUnit(mUnit)) { michael@0: delete mValue.mCSSValueTriplet; michael@0: } else if (IsCSSRectUnit(mUnit)) { michael@0: delete mValue.mCSSRect; michael@0: } else if (IsCSSValuePairListUnit(mUnit)) { michael@0: delete mValue.mCSSValuePairList; michael@0: } else if (IsStringUnit(mUnit)) { michael@0: NS_ABORT_IF_FALSE(mValue.mString, "expecting non-null string"); michael@0: mValue.mString->Release(); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsStyleAnimation::Value::operator==(const Value& aOther) const michael@0: { michael@0: if (mUnit != aOther.mUnit) { michael@0: return false; michael@0: } michael@0: michael@0: switch (mUnit) { michael@0: case eUnit_Null: michael@0: case eUnit_Normal: michael@0: case eUnit_Auto: michael@0: case eUnit_None: michael@0: return true; michael@0: case eUnit_Enumerated: michael@0: case eUnit_Visibility: michael@0: case eUnit_Integer: michael@0: return mValue.mInt == aOther.mValue.mInt; michael@0: case eUnit_Coord: michael@0: return mValue.mCoord == aOther.mValue.mCoord; michael@0: case eUnit_Percent: michael@0: case eUnit_Float: michael@0: return mValue.mFloat == aOther.mValue.mFloat; michael@0: case eUnit_Color: michael@0: return mValue.mColor == aOther.mValue.mColor; michael@0: case eUnit_Calc: michael@0: return *mValue.mCSSValue == *aOther.mValue.mCSSValue; michael@0: case eUnit_CSSValuePair: michael@0: return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair; michael@0: case eUnit_CSSValueTriplet: michael@0: return *mValue.mCSSValueTriplet == *aOther.mValue.mCSSValueTriplet; michael@0: case eUnit_CSSRect: michael@0: return *mValue.mCSSRect == *aOther.mValue.mCSSRect; michael@0: case eUnit_Dasharray: michael@0: case eUnit_Filter: michael@0: case eUnit_Shadow: michael@0: case eUnit_BackgroundPosition: michael@0: return *mValue.mCSSValueList == *aOther.mValue.mCSSValueList; michael@0: case eUnit_Transform: michael@0: return *mValue.mCSSValueSharedList == *aOther.mValue.mCSSValueSharedList; michael@0: case eUnit_CSSValuePairList: michael@0: return *mValue.mCSSValuePairList == *aOther.mValue.mCSSValuePairList; michael@0: case eUnit_UnparsedString: michael@0: return (NS_strcmp(GetStringBufferValue(), michael@0: aOther.GetStringBufferValue()) == 0); michael@0: } michael@0: michael@0: NS_NOTREACHED("incomplete case"); michael@0: return false; michael@0: } michael@0: