Convert calc() serialization code to a template so it can also be used for nsStyleCoord. (Bug 363249) r=bzbarsky

This commit is contained in:
L. David Baron 2010-07-02 21:18:55 -07:00
parent dd9be1dd41
commit 60dfb4ddf1
2 changed files with 198 additions and 98 deletions

View File

@ -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 <class CalcOps>
static void
SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps);
// Serialize the toplevel value in a calc() tree. See big comment
// above.
template <class CalcOps>
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 <class CalcOps>
/* 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);
}
}
}
}

View File

@ -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;