diff --git a/layout/style/CSSCalc.h b/layout/style/CSSCalc.h index 5f5879432ca..c6edabc2080 100644 --- a/layout/style/CSSCalc.h +++ b/layout/style/CSSCalc.h @@ -306,6 +306,159 @@ ConvertCalcUnit(nsStyleUnit aUnit) return nsCSSUnit(aUnit - 14); } +/** + * SerializeCalc appends the serialization of aValue to a string. + * + * It is templatized over a CalcOps class that is expected to provide: + * + * // input_type and input_array_type have a bunch of very specific + * // expectations (which happen to be met by two classes (nsCSSValue + * // and nsStyleCoord). There must be methods (roughly): + * // input_array_type* input_type::GetArrayValue(); + * // PRUint32 input_array_type::Count() const; + * // input_type& input_array_type::Item(PRUint32); + * typedef ... input_type; + * typedef ... input_array_type; + * + * static nsCSSUnit GetUnit(const input_type& aValue); + * + * void Append(const char* aString); + * void AppendLeafValue(const input_type& aValue); + * void AppendNumber(const input_type& aValue); + * + * Data structures given may or may not have a toplevel eCSSUnit_Calc + * node representing a calc whose toplevel is not min() or max(). + */ + +template +static void +SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps); + +// Serialize the toplevel value in a calc() tree. See big comment +// above. +template +static void +SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps) +{ + aOps.Append("-moz-"); + nsCSSUnit unit = CalcOps::GetUnit(aValue); + if (unit != eCSSUnit_Calc_Minimum && unit != eCSSUnit_Calc_Maximum) { + aOps.Append("calc("); + } + if (unit == eCSSUnit_Calc) { + const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); + NS_ABORT_IF_FALSE(array->Count() == 1, "unexpected length"); + SerializeCalcInternal(array->Item(0), aOps); + } else { + SerializeCalcInternal(aValue, aOps); + } + if (unit != eCSSUnit_Calc_Minimum && unit != eCSSUnit_Calc_Maximum) { + aOps.Append(")"); + } +} + +static inline PRBool +IsCalcAdditiveUnit(nsCSSUnit aUnit) +{ + return aUnit == eCSSUnit_Calc_Plus || + aUnit == eCSSUnit_Calc_Minus; +} + +static inline PRBool +IsCalcMultiplicativeUnit(nsCSSUnit aUnit) +{ + return aUnit == eCSSUnit_Calc_Times_L || + aUnit == eCSSUnit_Calc_Times_R || + aUnit == eCSSUnit_Calc_Divided; +} + +// Serialize a non-toplevel value in a calc() tree. See big comment +// above. +template +/* static */ void +SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps) +{ + nsCSSUnit unit = CalcOps::GetUnit(aValue); + if (eCSSUnit_Calc_Minimum == unit || eCSSUnit_Calc_Maximum == unit) { + const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); + if (eCSSUnit_Calc_Minimum == unit) { + aOps.Append("min("); + } else { + aOps.Append("max("); + } + + for (size_t i = 0, i_end = array->Count(); i < i_end; ++i) { + if (i != 0) { + aOps.Append(", "); + } + SerializeCalcInternal(array->Item(i), aOps); + } + + aOps.Append(")"); + } else if (IsCalcAdditiveUnit(unit)) { + const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); + NS_ABORT_IF_FALSE(array->Count() == 2, "unexpected length"); + + SerializeCalcInternal(array->Item(0), aOps); + + if (eCSSUnit_Calc_Plus == unit) { + aOps.Append(" + "); + } else { + NS_ABORT_IF_FALSE(eCSSUnit_Calc_Minus == unit, "unexpected unit"); + aOps.Append(" - "); + } + + PRBool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(1))); + if (needParens) { + aOps.Append("("); + } + SerializeCalcInternal(array->Item(1), aOps); + if (needParens) { + aOps.Append(")"); + } + } else if (IsCalcMultiplicativeUnit(unit)) { + const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); + NS_ABORT_IF_FALSE(array->Count() == 2, "unexpected length"); + + PRBool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(0))); + if (needParens) { + aOps.Append("("); + } + if (unit == eCSSUnit_Calc_Times_L) { + aOps.AppendNumber(array->Item(0)); + } else { + SerializeCalcInternal(array->Item(0), aOps); + } + if (needParens) { + aOps.Append(")"); + } + + if (eCSSUnit_Calc_Times_L == unit || eCSSUnit_Calc_Times_R == unit) { + aOps.Append(" * "); + } else { + NS_ABORT_IF_FALSE(eCSSUnit_Calc_Divided == unit, "unexpected unit"); + aOps.Append(" / "); + } + + nsCSSUnit subUnit = CalcOps::GetUnit(array->Item(1)); + needParens = IsCalcAdditiveUnit(subUnit) || + IsCalcMultiplicativeUnit(subUnit); + if (needParens) { + aOps.Append("("); + } + if (unit == eCSSUnit_Calc_Times_L) { + SerializeCalcInternal(array->Item(1), aOps); + } else { + aOps.AppendNumber(array->Item(1)); + } + if (needParens) { + aOps.Append(")"); + } + } else { + aOps.AppendLeafValue(aValue); + } +} + } } diff --git a/layout/style/nsCSSDeclaration.cpp b/layout/style/nsCSSDeclaration.cpp index 7037b70b61b..5c43cbe7795 100644 --- a/layout/style/nsCSSDeclaration.cpp +++ b/layout/style/nsCSSDeclaration.cpp @@ -56,10 +56,11 @@ #include "nsFont.h" #include "nsReadableUtils.h" #include "nsStyleUtil.h" - #include "nsStyleConsts.h" - #include "nsCOMPtr.h" +#include "CSSCalc.h" + +namespace css = mozilla::css; nsCSSDeclaration::nsCSSDeclaration() { @@ -226,20 +227,42 @@ nsCSSDeclaration::AppendStorageToString(nsCSSProperty aProperty, return aStorage != nsnull; } -static inline PRBool -IsCalcAdditiveUnit(nsCSSUnit aUnit) -{ - return aUnit == eCSSUnit_Calc_Plus || - aUnit == eCSSUnit_Calc_Minus; -} +struct CSSValueSerializeCalcOps { + CSSValueSerializeCalcOps(nsCSSProperty aProperty, nsAString& aResult) + : mProperty(aProperty), + mResult(aResult) + { + } -static inline PRBool -IsCalcMultiplicativeUnit(nsCSSUnit aUnit) -{ - return aUnit == eCSSUnit_Calc_Times_L || - aUnit == eCSSUnit_Calc_Times_R || - aUnit == eCSSUnit_Calc_Divided; -} + typedef nsCSSValue input_type; + typedef nsCSSValue::Array input_array_type; + + static nsCSSUnit GetUnit(const input_type& aValue) { + return aValue.GetUnit(); + } + + void Append(const char* aString) + { + mResult.AppendASCII(aString); + } + + void AppendLeafValue(const input_type& aValue) + { + NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Percent || + aValue.IsLengthUnit(), "unexpected unit"); + nsCSSDeclaration::AppendCSSValueToString(mProperty, aValue, mResult); + } + + void AppendNumber(const input_type& aValue) + { + NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit"); + nsCSSDeclaration::AppendCSSValueToString(mProperty, aValue, mResult); + } + +private: + nsCSSProperty mProperty; + nsAString &mResult; +}; /* static */ PRBool nsCSSDeclaration::AppendCSSValueToString(nsCSSProperty aProperty, @@ -338,89 +361,13 @@ nsCSSDeclaration::AppendCSSValueToString(nsCSSProperty aProperty, /* Finally, append the closing parenthesis. */ aResult.AppendLiteral(")"); } - else if (eCSSUnit_Calc <= unit && unit <= eCSSUnit_Calc_Maximum) { - const nsCSSValue::Array* array = aValue.GetArrayValue(); - if (eCSSUnit_Calc == unit) { - NS_ABORT_IF_FALSE(array->Count() == 1, "unexpected length"); - aResult.AppendLiteral("-moz-calc("); - // When we make recursive calls, we pass eCSSProperty_UNKNOWN as - // the property so we can distinguish min() and max() at toplevel - // (where we need to serialize with a -moz- prefix) from min() and - // max() within calc() (where we don't). - AppendCSSValueToString(eCSSProperty_UNKNOWN, array->Item(0), aResult); - aResult.AppendLiteral(")"); - } else if (eCSSUnit_Calc_Minimum == unit || - eCSSUnit_Calc_Maximum == unit) { - if (aProperty == eCSSProperty_UNKNOWN) { - // min() or max() inside calc() - if (eCSSUnit_Calc_Minimum == unit) { - aResult.AppendLiteral("min("); - } else { - aResult.AppendLiteral("max("); - } - } else { - // min() or max() at toplevel - if (eCSSUnit_Calc_Minimum == unit) { - aResult.AppendLiteral("-moz-min("); - } else { - aResult.AppendLiteral("-moz-max("); - } - } - - for (size_t i = 0, i_end = array->Count(); i < i_end; ++i) { - if (i != 0) { - aResult.AppendLiteral(", "); - } - // When we make recursive calls, we pass eCSSProperty_UNKNOWN as - // the property so we can distinguish min() and max() at toplevel - // (where we need to serialize with a -moz- prefix) from min() and - // max() within calc() (where we don't). - AppendCSSValueToString(eCSSProperty_UNKNOWN, array->Item(i), aResult); - } - - aResult.AppendLiteral(")"); - } else if (IsCalcAdditiveUnit(unit)) { - NS_ABORT_IF_FALSE(array->Count() == 2, "unexpected length"); - - AppendCSSValueToString(aProperty, array->Item(0), aResult); - - if (eCSSUnit_Calc_Plus == unit) { - aResult.AppendLiteral(" + "); - } else { - NS_ABORT_IF_FALSE(eCSSUnit_Calc_Minus == unit, "unexpected unit"); - aResult.AppendLiteral(" - "); - } - - PRBool needParens = IsCalcAdditiveUnit(array->Item(1).GetUnit()); - if (needParens) - aResult.AppendLiteral("("); - AppendCSSValueToString(aProperty, array->Item(1), aResult); - if (needParens) - aResult.AppendLiteral(")"); - } else if (IsCalcMultiplicativeUnit(unit)) { - PRBool needParens = IsCalcAdditiveUnit(array->Item(0).GetUnit()); - if (needParens) - aResult.AppendLiteral("("); - AppendCSSValueToString(aProperty, array->Item(0), aResult); - if (needParens) - aResult.AppendLiteral(")"); - - if (eCSSUnit_Calc_Times_L == unit || eCSSUnit_Calc_Times_R == unit) { - aResult.AppendLiteral(" * "); - } else { - NS_ABORT_IF_FALSE(eCSSUnit_Calc_Divided == unit, "unexpected unit"); - aResult.AppendLiteral(" / "); - } - - nsCSSUnit subUnit = array->Item(1).GetUnit(); - needParens = IsCalcAdditiveUnit(subUnit) || - IsCalcMultiplicativeUnit(subUnit); - if (needParens) - aResult.AppendLiteral("("); - AppendCSSValueToString(aProperty, array->Item(1), aResult); - if (needParens) - aResult.AppendLiteral(")"); - } + else if (aValue.IsCalcUnit()) { + NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Calc || + aValue.GetUnit() == eCSSUnit_Calc_Maximum || + aValue.GetUnit() == eCSSUnit_Calc_Minimum, + "unexpected unit"); + CSSValueSerializeCalcOps ops(aProperty, aResult); + css::SerializeCalc(aValue, ops); } else if (eCSSUnit_Integer == unit) { nsAutoString tmpStr;