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: #ifndef nsStyleAnimation_h_ michael@0: #define nsStyleAnimation_h_ michael@0: michael@0: #include "nsStringFwd.h" michael@0: #include "nsCRTGlue.h" michael@0: #include "nsStringBuffer.h" michael@0: #include "nsCSSProperty.h" michael@0: #include "nsCoord.h" michael@0: #include "nsColor.h" michael@0: #include "nsCSSValue.h" michael@0: michael@0: class nsStyleContext; michael@0: class gfx3DMatrix; michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class Element; michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: /** michael@0: * Utility class to handle animated style values michael@0: */ michael@0: class nsStyleAnimation { michael@0: public: michael@0: class Value; michael@0: michael@0: // Mathematical methods michael@0: // -------------------- michael@0: /** michael@0: * Adds |aCount| copies of |aValueToAdd| to |aDest|. The result of this michael@0: * addition is stored in aDest. michael@0: * michael@0: * Note that if |aCount| is 0, then |aDest| will be unchanged. Also, if michael@0: * this method fails, then |aDest| will be unchanged. michael@0: * michael@0: * @param aDest The value to add to. michael@0: * @param aValueToAdd The value to add. michael@0: * @param aCount The number of times to add aValueToAdd. michael@0: * @return true on success, false on failure. michael@0: */ michael@0: static bool Add(nsCSSProperty aProperty, Value& aDest, michael@0: const Value& aValueToAdd, uint32_t aCount) { michael@0: return AddWeighted(aProperty, 1.0, aDest, aCount, aValueToAdd, aDest); michael@0: } michael@0: michael@0: /** michael@0: * Calculates a measure of 'distance' between two values. michael@0: * michael@0: * This measure of Distance is guaranteed to be proportional to michael@0: * portions passed to Interpolate, Add, or AddWeighted. However, for michael@0: * some types of Value it may not produce sensible results for paced michael@0: * animation. michael@0: * michael@0: * If this method succeeds, the returned distance value is guaranteed to be michael@0: * non-negative. michael@0: * michael@0: * @param aStartValue The start of the interval for which the distance michael@0: * should be calculated. michael@0: * @param aEndValue The end of the interval for which the distance michael@0: * should be calculated. michael@0: * @param aDistance The result of the calculation. michael@0: * @return true on success, false on failure. michael@0: */ michael@0: static bool ComputeDistance(nsCSSProperty aProperty, michael@0: const Value& aStartValue, michael@0: const Value& aEndValue, michael@0: double& aDistance); michael@0: michael@0: /** michael@0: * Calculates an interpolated value that is the specified |aPortion| between michael@0: * the two given values. michael@0: * michael@0: * This really just does the following calculation: michael@0: * aResultValue = (1.0 - aPortion) * aStartValue + aPortion * aEndValue michael@0: * michael@0: * @param aStartValue The value defining the start of the interval of michael@0: * interpolation. michael@0: * @param aEndValue The value defining the end of the interval of michael@0: * interpolation. michael@0: * @param aPortion A number in the range [0.0, 1.0] defining the michael@0: * distance of the interpolated value in the interval. michael@0: * @param [out] aResultValue The resulting interpolated value. michael@0: * @return true on success, false on failure. michael@0: */ michael@0: static bool Interpolate(nsCSSProperty aProperty, michael@0: const Value& aStartValue, michael@0: const Value& aEndValue, michael@0: double aPortion, michael@0: Value& aResultValue) { michael@0: return AddWeighted(aProperty, 1.0 - aPortion, aStartValue, michael@0: aPortion, aEndValue, aResultValue); michael@0: } michael@0: michael@0: /** michael@0: * Does the calculation: michael@0: * aResultValue = aCoeff1 * aValue1 + aCoeff2 * aValue2 michael@0: * michael@0: * @param [out] aResultValue The resulting interpolated value. May be michael@0: * the same as aValue1 or aValue2. michael@0: * @return true on success, false on failure. michael@0: * michael@0: * NOTE: Current callers always pass aCoeff1 and aCoeff2 >= 0. They michael@0: * are currently permitted to be negative; however, if, as we add michael@0: * support more value types types, we find that this causes michael@0: * difficulty, we might change this to restrict them to being michael@0: * positive. michael@0: */ michael@0: static bool 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: // Type-conversion methods michael@0: // ----------------------- michael@0: /** michael@0: * Creates a computed value for the given specified value michael@0: * (property ID + string). A style context is needed in case the michael@0: * specified value depends on inherited style or on the values of other michael@0: * properties. michael@0: * michael@0: * @param aProperty The property whose value we're computing. michael@0: * @param aTargetElement The content node to which our computed value is michael@0: * applicable. michael@0: * @param aSpecifiedValue The specified value, from which we'll build our michael@0: * computed value. michael@0: * @param aUseSVGMode A flag to indicate whether we should parse michael@0: * |aSpecifiedValue| in SVG mode. michael@0: * @param [out] aComputedValue The resulting computed value. michael@0: * @param [out] aIsContextSensitive michael@0: * Set to true if |aSpecifiedValue| may produce michael@0: * a different |aComputedValue| depending on other CSS michael@0: * properties on |aTargetElement| or its ancestors. michael@0: * false otherwise. michael@0: * Note that the operation of this method is michael@0: * significantly faster when |aIsContextSensitive| is michael@0: * nullptr. michael@0: * @return true on success, false on failure. michael@0: */ michael@0: static bool ComputeValue(nsCSSProperty aProperty, michael@0: mozilla::dom::Element* aTargetElement, michael@0: const nsAString& aSpecifiedValue, michael@0: bool aUseSVGMode, michael@0: Value& aComputedValue, michael@0: bool* aIsContextSensitive = nullptr); michael@0: michael@0: /** michael@0: * Creates a specified value for the given computed value. michael@0: * michael@0: * The first overload fills in an nsCSSValue object; the second michael@0: * produces a string. The nsCSSValue result may depend on objects michael@0: * owned by the |aComputedValue| object, so users of that variant michael@0: * must keep |aComputedValue| alive longer than |aSpecifiedValue|. michael@0: * michael@0: * @param aProperty The property whose value we're uncomputing. michael@0: * @param aComputedValue The computed value to be converted. michael@0: * @param [out] aSpecifiedValue The resulting specified value. michael@0: * @return true on success, false on failure. michael@0: */ michael@0: static bool UncomputeValue(nsCSSProperty aProperty, michael@0: const Value& aComputedValue, michael@0: nsCSSValue& aSpecifiedValue); michael@0: static bool UncomputeValue(nsCSSProperty aProperty, michael@0: const Value& aComputedValue, michael@0: nsAString& aSpecifiedValue); michael@0: michael@0: /** michael@0: * Gets the computed value for the given property from the given style michael@0: * context. michael@0: * michael@0: * @param aProperty The property whose value we're looking up. michael@0: * @param aStyleContext The style context to check for the computed value. michael@0: * @param [out] aComputedValue The resulting computed value. michael@0: * @return true on success, false on failure. michael@0: */ michael@0: static bool ExtractComputedValue(nsCSSProperty aProperty, michael@0: nsStyleContext* aStyleContext, michael@0: Value& aComputedValue); michael@0: michael@0: /** michael@0: * Interpolates between 2 matrices by decomposing them. michael@0: * michael@0: * @param aMatrix1 First matrix, using CSS pixel units. michael@0: * @param aMatrix2 Second matrix, using CSS pixel units. michael@0: * @param aProgress Interpolation value in the range [0.0, 1.0] michael@0: */ michael@0: static gfx3DMatrix InterpolateTransformMatrix(const gfx3DMatrix &aMatrix1, michael@0: const gfx3DMatrix &aMatrix2, michael@0: double aProgress); michael@0: michael@0: static already_AddRefed michael@0: AppendTransformFunction(nsCSSKeyword aTransformFunction, michael@0: nsCSSValueList**& aListTail); michael@0: michael@0: /** michael@0: * The types and values for the values that we extract and animate. michael@0: */ michael@0: enum Unit { michael@0: eUnit_Null, // not initialized michael@0: eUnit_Normal, michael@0: eUnit_Auto, michael@0: eUnit_None, michael@0: eUnit_Enumerated, michael@0: eUnit_Visibility, // special case for transitions (which converts michael@0: // Enumerated to Visibility as needed) michael@0: eUnit_Integer, michael@0: eUnit_Coord, michael@0: eUnit_Percent, michael@0: eUnit_Float, michael@0: eUnit_Color, michael@0: eUnit_Calc, // nsCSSValue* (never null), always with a single michael@0: // calc() expression that's either length or length+percent michael@0: eUnit_CSSValuePair, // nsCSSValuePair* (never null) michael@0: eUnit_CSSValueTriplet, // nsCSSValueTriplet* (never null) michael@0: eUnit_CSSRect, // nsCSSRect* (never null) michael@0: eUnit_Dasharray, // nsCSSValueList* (never null) michael@0: eUnit_Filter, // nsCSSValueList* (may be null) michael@0: eUnit_Shadow, // nsCSSValueList* (may be null) michael@0: eUnit_Transform, // nsCSSValueList* (never null) michael@0: eUnit_BackgroundPosition, // nsCSSValueList* (never null) michael@0: eUnit_CSSValuePairList, // nsCSSValuePairList* (never null) michael@0: eUnit_UnparsedString // nsStringBuffer* (never null) michael@0: }; michael@0: michael@0: class Value { michael@0: private: michael@0: Unit mUnit; michael@0: union { michael@0: int32_t mInt; michael@0: nscoord mCoord; michael@0: float mFloat; michael@0: nscolor mColor; michael@0: nsCSSValue* mCSSValue; michael@0: nsCSSValuePair* mCSSValuePair; michael@0: nsCSSValueTriplet* mCSSValueTriplet; michael@0: nsCSSRect* mCSSRect; michael@0: nsCSSValueList* mCSSValueList; michael@0: nsCSSValueSharedList* mCSSValueSharedList; michael@0: nsCSSValuePairList* mCSSValuePairList; michael@0: nsStringBuffer* mString; michael@0: } mValue; michael@0: public: michael@0: Unit GetUnit() const { michael@0: NS_ASSERTION(mUnit != eUnit_Null, "uninitialized"); michael@0: return mUnit; michael@0: } michael@0: michael@0: // Accessor to let us verify assumptions about presence of null unit, michael@0: // without tripping the assertion in GetUnit(). michael@0: bool IsNull() const { michael@0: return mUnit == eUnit_Null; michael@0: } michael@0: michael@0: int32_t GetIntValue() const { michael@0: NS_ASSERTION(IsIntUnit(mUnit), "unit mismatch"); michael@0: return mValue.mInt; michael@0: } michael@0: nscoord GetCoordValue() const { michael@0: NS_ASSERTION(mUnit == eUnit_Coord, "unit mismatch"); michael@0: return mValue.mCoord; michael@0: } michael@0: float GetPercentValue() const { michael@0: NS_ASSERTION(mUnit == eUnit_Percent, "unit mismatch"); michael@0: return mValue.mFloat; michael@0: } michael@0: float GetFloatValue() const { michael@0: NS_ASSERTION(mUnit == eUnit_Float, "unit mismatch"); michael@0: return mValue.mFloat; michael@0: } michael@0: nscolor GetColorValue() const { michael@0: NS_ASSERTION(mUnit == eUnit_Color, "unit mismatch"); michael@0: return mValue.mColor; michael@0: } michael@0: nsCSSValue* GetCSSValueValue() const { michael@0: NS_ASSERTION(IsCSSValueUnit(mUnit), "unit mismatch"); michael@0: return mValue.mCSSValue; michael@0: } michael@0: nsCSSValuePair* GetCSSValuePairValue() const { michael@0: NS_ASSERTION(IsCSSValuePairUnit(mUnit), "unit mismatch"); michael@0: return mValue.mCSSValuePair; michael@0: } michael@0: nsCSSValueTriplet* GetCSSValueTripletValue() const { michael@0: NS_ASSERTION(IsCSSValueTripletUnit(mUnit), "unit mismatch"); michael@0: return mValue.mCSSValueTriplet; michael@0: } michael@0: nsCSSRect* GetCSSRectValue() const { michael@0: NS_ASSERTION(IsCSSRectUnit(mUnit), "unit mismatch"); michael@0: return mValue.mCSSRect; michael@0: } michael@0: nsCSSValueList* GetCSSValueListValue() const { michael@0: NS_ASSERTION(IsCSSValueListUnit(mUnit), "unit mismatch"); michael@0: return mValue.mCSSValueList; michael@0: } michael@0: nsCSSValueSharedList* GetCSSValueSharedListValue() const { michael@0: NS_ASSERTION(IsCSSValueSharedListValue(mUnit), "unit mismatch"); michael@0: return mValue.mCSSValueSharedList; michael@0: } michael@0: nsCSSValuePairList* GetCSSValuePairListValue() const { michael@0: NS_ASSERTION(IsCSSValuePairListUnit(mUnit), "unit mismatch"); michael@0: return mValue.mCSSValuePairList; michael@0: } michael@0: const char16_t* GetStringBufferValue() const { michael@0: NS_ASSERTION(IsStringUnit(mUnit), "unit mismatch"); michael@0: return GetBufferValue(mValue.mString); michael@0: } michael@0: michael@0: void GetStringValue(nsAString& aBuffer) const { michael@0: NS_ASSERTION(IsStringUnit(mUnit), "unit mismatch"); michael@0: aBuffer.Truncate(); michael@0: uint32_t len = NS_strlen(GetBufferValue(mValue.mString)); michael@0: mValue.mString->ToString(len, aBuffer); michael@0: } michael@0: michael@0: explicit Value(Unit aUnit = eUnit_Null) : mUnit(aUnit) { michael@0: NS_ASSERTION(aUnit == eUnit_Null || aUnit == eUnit_Normal || michael@0: aUnit == eUnit_Auto || aUnit == eUnit_None, michael@0: "must be valueless unit"); michael@0: } michael@0: Value(const Value& aOther) : mUnit(eUnit_Null) { *this = aOther; } michael@0: enum IntegerConstructorType { IntegerConstructor }; michael@0: Value(int32_t aInt, Unit aUnit, IntegerConstructorType); michael@0: enum CoordConstructorType { CoordConstructor }; michael@0: Value(nscoord aLength, CoordConstructorType); michael@0: enum PercentConstructorType { PercentConstructor }; michael@0: Value(float aPercent, PercentConstructorType); michael@0: enum FloatConstructorType { FloatConstructor }; michael@0: Value(float aFloat, FloatConstructorType); michael@0: enum ColorConstructorType { ColorConstructor }; michael@0: Value(nscolor aColor, ColorConstructorType); michael@0: michael@0: ~Value() { FreeValue(); } michael@0: michael@0: void SetNormalValue(); michael@0: void SetAutoValue(); michael@0: void SetNoneValue(); michael@0: void SetIntValue(int32_t aInt, Unit aUnit); michael@0: void SetCoordValue(nscoord aCoord); michael@0: void SetPercentValue(float aPercent); michael@0: void SetFloatValue(float aFloat); michael@0: void SetColorValue(nscolor aColor); michael@0: void SetUnparsedStringValue(const nsString& aString); michael@0: michael@0: // These setters take ownership of |aValue|, and are therefore named michael@0: // "SetAndAdopt*". michael@0: void SetAndAdoptCSSValueValue(nsCSSValue *aValue, Unit aUnit); michael@0: void SetAndAdoptCSSValuePairValue(nsCSSValuePair *aValue, Unit aUnit); michael@0: void SetAndAdoptCSSValueTripletValue(nsCSSValueTriplet *aValue, Unit aUnit); michael@0: void SetAndAdoptCSSRectValue(nsCSSRect *aValue, Unit aUnit); michael@0: void SetAndAdoptCSSValueListValue(nsCSSValueList *aValue, Unit aUnit); michael@0: void SetAndAdoptCSSValuePairListValue(nsCSSValuePairList *aValue); michael@0: michael@0: void SetTransformValue(nsCSSValueSharedList* aList); michael@0: michael@0: Value& operator=(const Value& aOther); michael@0: michael@0: bool operator==(const Value& aOther) const; michael@0: bool operator!=(const Value& aOther) const michael@0: { return !(*this == aOther); } michael@0: michael@0: private: michael@0: void FreeValue(); michael@0: michael@0: static const char16_t* GetBufferValue(nsStringBuffer* aBuffer) { michael@0: return static_cast(aBuffer->Data()); michael@0: } michael@0: michael@0: static bool IsIntUnit(Unit aUnit) { michael@0: return aUnit == eUnit_Enumerated || aUnit == eUnit_Visibility || michael@0: aUnit == eUnit_Integer; michael@0: } michael@0: static bool IsCSSValueUnit(Unit aUnit) { michael@0: return aUnit == eUnit_Calc; michael@0: } michael@0: static bool IsCSSValuePairUnit(Unit aUnit) { michael@0: return aUnit == eUnit_CSSValuePair; michael@0: } michael@0: static bool IsCSSValueTripletUnit(Unit aUnit) { michael@0: return aUnit == eUnit_CSSValueTriplet; michael@0: } michael@0: static bool IsCSSRectUnit(Unit aUnit) { michael@0: return aUnit == eUnit_CSSRect; michael@0: } michael@0: static bool IsCSSValueListUnit(Unit aUnit) { michael@0: return aUnit == eUnit_Dasharray || aUnit == eUnit_Filter || michael@0: aUnit == eUnit_Shadow || michael@0: aUnit == eUnit_BackgroundPosition; michael@0: } michael@0: static bool IsCSSValueSharedListValue(Unit aUnit) { michael@0: return aUnit == eUnit_Transform; michael@0: } michael@0: static bool IsCSSValuePairListUnit(Unit aUnit) { michael@0: return aUnit == eUnit_CSSValuePairList; michael@0: } michael@0: static bool IsStringUnit(Unit aUnit) { michael@0: return aUnit == eUnit_UnparsedString; michael@0: } michael@0: }; michael@0: }; michael@0: michael@0: #endif