Bug 605231 - Move transform parsing code out of font property parsing code. r=dbaron

This commit is contained in:
John Daggett 2013-01-08 17:13:17 +09:00
parent ab2798ea67
commit 9c000701a5

View File

@ -8401,432 +8401,6 @@ CSSParserImpl::ParseOneFamily(nsAString& aFamily)
}
}
///////////////////////////////////////////////////////
// transform Parsing Implementation
/* Reads a function list of arguments. Do not call this function
* directly; it's mean to be caled from ParseFunction.
*/
bool
CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[],
uint16_t aMinElems,
uint16_t aMaxElems,
InfallibleTArray<nsCSSValue> &aOutput)
{
for (uint16_t index = 0; index < aMaxElems; ++index) {
nsCSSValue newValue;
if (!ParseVariant(newValue, aVariantMask[index], nullptr))
return false;
aOutput.AppendElement(newValue);
// See whether to continue or whether to look for end of function.
if (!ExpectSymbol(',', true)) {
// We need to read the closing parenthesis, and also must take care
// that we haven't read too few symbols.
return ExpectSymbol(')', true) && (index + 1) >= aMinElems;
}
}
// If we're here, we finished looping without hitting the end, so we read too
// many elements.
return false;
}
/* Parses a function [ input of the form (a [, b]*) ] and stores it
* as an nsCSSValue that holds a function of the form
* function-name arg1 arg2 ... argN
*
* On error, the return value is false.
*
* @param aFunction The name of the function that we're reading.
* @param aAllowedTypes An array of values corresponding to the legal
* types for each element in the function. The zeroth element in the
* array corresponds to the first function parameter, etc. The length
* of this array _must_ be greater than or equal to aMaxElems or the
* behavior is undefined.
* @param aMinElems Minimum number of elements to read. Reading fewer than
* this many elements will result in the function failing.
* @param aMaxElems Maximum number of elements to read. Reading more than
* this many elements will result in the function failing.
* @param aValue (out) The value that was parsed.
*/
bool
CSSParserImpl::ParseFunction(const nsString &aFunction,
const int32_t aAllowedTypes[],
uint16_t aMinElems, uint16_t aMaxElems,
nsCSSValue &aValue)
{
typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
/* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
* elements stored in the the nsCSSValue::Array.
*/
static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
/* Make a copy of the function name, since the reference is _probably_ to
* mToken.mIdent, which is going to get overwritten during the course of this
* function.
*/
nsString functionName(aFunction);
/* Read in a list of values as an array, failing if we can't or if
* it's out of bounds.
*/
InfallibleTArray<nsCSSValue> foundValues;
if (!ParseFunctionInternals(aAllowedTypes, aMinElems, aMaxElems,
foundValues))
return false;
/* Now, convert this array into an nsCSSValue::Array object.
* We'll need N + 1 spots, one for the function name and the rest for the
* arguments. In case the user has given us more than 2^16 - 2 arguments,
* we'll truncate them at 2^16 - 2 arguments.
*/
uint16_t numElements = (foundValues.Length() <= MAX_ALLOWED_ELEMS ?
foundValues.Length() + 1 : MAX_ALLOWED_ELEMS);
nsRefPtr<nsCSSValue::Array> convertedArray =
nsCSSValue::Array::Create(numElements);
/* Copy things over. */
convertedArray->Item(0).SetStringValue(functionName, eCSSUnit_Ident);
for (uint16_t index = 0; index + 1 < numElements; ++index)
convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
/* Fill in the outparam value with the array. */
aValue.SetArrayValue(convertedArray, eCSSUnit_Function);
/* Return it! */
return true;
}
/**
* Given a token, determines the minimum and maximum number of function
* parameters to read, along with the mask that should be used to read
* those function parameters. If the token isn't a transform function,
* returns an error.
*
* @param aToken The token identifying the function.
* @param aMinElems [out] The minimum number of elements to read.
* @param aMaxElems [out] The maximum number of elements to read
* @param aVariantMask [out] The variant mask to use during parsing
* @return Whether the information was loaded successfully.
*/
static bool GetFunctionParseInformation(nsCSSKeyword aToken,
bool aIsPrefixed,
uint16_t &aMinElems,
uint16_t &aMaxElems,
const int32_t *& aVariantMask,
bool &aIs3D)
{
/* These types represent the common variant masks that will be used to
* parse out the individual functions. The order in the enumeration
* must match the order in which the masks are declared.
*/
enum { eLengthPercentCalc,
eLengthCalc,
eTwoLengthPercentCalcs,
eTwoLengthPercentCalcsOneLengthCalc,
eAngle,
eTwoAngles,
eNumber,
ePositiveLength,
eTwoNumbers,
eThreeNumbers,
eThreeNumbersOneAngle,
eMatrix,
eMatrixPrefixed,
eMatrix3d,
eMatrix3dPrefixed,
eNumVariantMasks };
static const int32_t kMaxElemsPerFunction = 16;
static const int32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
{VARIANT_LPCALC},
{VARIANT_LENGTH|VARIANT_CALC},
{VARIANT_LPCALC, VARIANT_LPCALC},
{VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC},
{VARIANT_ANGLE_OR_ZERO},
{VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
{VARIANT_NUMBER},
{VARIANT_LENGTH|VARIANT_POSITIVE_DIMENSION},
{VARIANT_NUMBER, VARIANT_NUMBER},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_LPNCALC, VARIANT_LPNCALC},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
#ifdef DEBUG
static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
{1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16};
#endif
int32_t variantIndex = eNumVariantMasks;
aIs3D = false;
switch (aToken) {
case eCSSKeyword_translatex:
case eCSSKeyword_translatey:
/* Exactly one length or percent. */
variantIndex = eLengthPercentCalc;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_translatez:
/* Exactly one length */
variantIndex = eLengthCalc;
aMinElems = 1U;
aMaxElems = 1U;
aIs3D = true;
break;
case eCSSKeyword_translate3d:
/* Exactly two lengthds or percents and a number */
variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
aMinElems = 3U;
aMaxElems = 3U;
aIs3D = true;
break;
case eCSSKeyword_scalez:
aIs3D = true;
case eCSSKeyword_scalex:
case eCSSKeyword_scaley:
/* Exactly one scale factor. */
variantIndex = eNumber;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_scale3d:
/* Exactly three scale factors. */
variantIndex = eThreeNumbers;
aMinElems = 3U;
aMaxElems = 3U;
aIs3D = true;
break;
case eCSSKeyword_rotatex:
case eCSSKeyword_rotatey:
aIs3D = true;
case eCSSKeyword_rotate:
case eCSSKeyword_rotatez:
/* Exactly one angle. */
variantIndex = eAngle;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_rotate3d:
variantIndex = eThreeNumbersOneAngle;
aMinElems = 4U;
aMaxElems = 4U;
aIs3D = true;
break;
case eCSSKeyword_translate:
/* One or two lengths or percents. */
variantIndex = eTwoLengthPercentCalcs;
aMinElems = 1U;
aMaxElems = 2U;
break;
case eCSSKeyword_skew:
/* Exactly one or two angles. */
variantIndex = eTwoAngles;
aMinElems = 1U;
aMaxElems = 2U;
break;
case eCSSKeyword_scale:
/* One or two scale factors. */
variantIndex = eTwoNumbers;
aMinElems = 1U;
aMaxElems = 2U;
break;
case eCSSKeyword_skewx:
/* Exactly one angle. */
variantIndex = eAngle;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_skewy:
/* Exactly one angle. */
variantIndex = eAngle;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_matrix:
/* Six values, all numbers. */
variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
aMinElems = 6U;
aMaxElems = 6U;
break;
case eCSSKeyword_matrix3d:
/* 16 matrix values, all numbers */
variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
aMinElems = 16U;
aMaxElems = 16U;
aIs3D = true;
break;
case eCSSKeyword_perspective:
/* Exactly one scale number. */
variantIndex = ePositiveLength;
aMinElems = 1U;
aMaxElems = 1U;
aIs3D = true;
break;
default:
/* Oh dear, we didn't match. Report an error. */
return false;
}
NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
#ifdef DEBUG
NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
"Invalid aMaxElems for this variant mask.");
#endif
// Convert the index into a mask.
aVariantMask = kVariantMasks[variantIndex];
return true;
}
/* Reads a single transform function from the tokenizer stream, reporting an
* error if something goes wrong.
*/
bool
CSSParserImpl::ParseSingleTransform(bool aIsPrefixed,
nsCSSValue& aValue, bool& aIs3D)
{
if (!GetToken(true))
return false;
if (mToken.mType != eCSSToken_Function) {
UngetToken();
return false;
}
const int32_t* variantMask;
uint16_t minElems, maxElems;
nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
if (!GetFunctionParseInformation(keyword, aIsPrefixed,
minElems, maxElems, variantMask, aIs3D))
return false;
// Bug 721136: Normalize the identifier to lowercase, except that things
// like scaleX should have the last character capitalized. This matches
// what other browsers do.
nsContentUtils::ASCIIToLower(mToken.mIdent);
switch (keyword) {
case eCSSKeyword_rotatex:
case eCSSKeyword_scalex:
case eCSSKeyword_skewx:
case eCSSKeyword_translatex:
mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('X'));
break;
case eCSSKeyword_rotatey:
case eCSSKeyword_scaley:
case eCSSKeyword_skewy:
case eCSSKeyword_translatey:
mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Y'));
break;
case eCSSKeyword_rotatez:
case eCSSKeyword_scalez:
case eCSSKeyword_translatez:
mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Z'));
break;
default:
break;
}
return ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, aValue);
}
/* Parses a transform property list by continuously reading in properties
* and constructing a matrix from it.
*/
bool CSSParserImpl::ParseTransform(bool aIsPrefixed)
{
nsCSSValue value;
if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
// 'inherit', 'initial', and 'none' must be alone
if (!ExpectEndProperty()) {
return false;
}
} else {
nsCSSValueList* cur = value.SetListValue();
for (;;) {
bool is3D;
if (!ParseSingleTransform(aIsPrefixed, cur->mValue, is3D)) {
return false;
}
if (is3D && !nsLayoutUtils::Are3DTransformsEnabled()) {
return false;
}
if (CheckEndProperty()) {
break;
}
cur->mNext = new nsCSSValueList;
cur = cur->mNext;
}
}
AppendValue(eCSSProperty_transform, value);
return true;
}
bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
{
nsCSSValuePair position;
if (!ParseBoxPositionValues(position, true))
return false;
nsCSSProperty prop = eCSSProperty_transform_origin;
if (aPerspective) {
if (!ExpectEndProperty()) {
return false;
}
prop = eCSSProperty_perspective_origin;
}
// Unlike many other uses of pairs, this position should always be stored
// as a pair, even if the values are the same, so it always serializes as
// a pair, and to keep the computation code simple.
if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
position.mXValue.GetUnit() == eCSSUnit_Initial) {
NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
"inherit/initial only half?");
AppendValue(prop, position.mXValue);
} else {
nsCSSValue value;
if (aPerspective) {
value.SetPairValue(position.mXValue, position.mYValue);
} else {
nsCSSValue depth;
if (!nsLayoutUtils::Are3DTransformsEnabled() ||
// only try parsing if 3-D transforms are enabled
!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) {
depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
}
value.SetTripletValue(position.mXValue, position.mYValue, depth);
}
AppendValue(prop, value);
}
return true;
}
bool
CSSParserImpl::ParseFamily(nsCSSValue& aValue)
{
@ -9492,7 +9066,433 @@ CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
}
return true;
}
///////////////////////////////////////////////////////
// transform Parsing Implementation
/* Reads a function list of arguments. Do not call this function
* directly; it's mean to be caled from ParseFunction.
*/
bool
CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[],
uint16_t aMinElems,
uint16_t aMaxElems,
InfallibleTArray<nsCSSValue> &aOutput)
{
for (uint16_t index = 0; index < aMaxElems; ++index) {
nsCSSValue newValue;
if (!ParseVariant(newValue, aVariantMask[index], nullptr))
return false;
aOutput.AppendElement(newValue);
// See whether to continue or whether to look for end of function.
if (!ExpectSymbol(',', true)) {
// We need to read the closing parenthesis, and also must take care
// that we haven't read too few symbols.
return ExpectSymbol(')', true) && (index + 1) >= aMinElems;
}
}
// If we're here, we finished looping without hitting the end, so we read too
// many elements.
return false;
}
/* Parses a function [ input of the form (a [, b]*) ] and stores it
* as an nsCSSValue that holds a function of the form
* function-name arg1 arg2 ... argN
*
* On error, the return value is false.
*
* @param aFunction The name of the function that we're reading.
* @param aAllowedTypes An array of values corresponding to the legal
* types for each element in the function. The zeroth element in the
* array corresponds to the first function parameter, etc. The length
* of this array _must_ be greater than or equal to aMaxElems or the
* behavior is undefined.
* @param aMinElems Minimum number of elements to read. Reading fewer than
* this many elements will result in the function failing.
* @param aMaxElems Maximum number of elements to read. Reading more than
* this many elements will result in the function failing.
* @param aValue (out) The value that was parsed.
*/
bool
CSSParserImpl::ParseFunction(const nsString &aFunction,
const int32_t aAllowedTypes[],
uint16_t aMinElems, uint16_t aMaxElems,
nsCSSValue &aValue)
{
typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
/* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
* elements stored in the the nsCSSValue::Array.
*/
static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
/* Make a copy of the function name, since the reference is _probably_ to
* mToken.mIdent, which is going to get overwritten during the course of this
* function.
*/
nsString functionName(aFunction);
/* Read in a list of values as an array, failing if we can't or if
* it's out of bounds.
*/
InfallibleTArray<nsCSSValue> foundValues;
if (!ParseFunctionInternals(aAllowedTypes, aMinElems, aMaxElems,
foundValues))
return false;
/* Now, convert this array into an nsCSSValue::Array object.
* We'll need N + 1 spots, one for the function name and the rest for the
* arguments. In case the user has given us more than 2^16 - 2 arguments,
* we'll truncate them at 2^16 - 2 arguments.
*/
uint16_t numElements = (foundValues.Length() <= MAX_ALLOWED_ELEMS ?
foundValues.Length() + 1 : MAX_ALLOWED_ELEMS);
nsRefPtr<nsCSSValue::Array> convertedArray =
nsCSSValue::Array::Create(numElements);
/* Copy things over. */
convertedArray->Item(0).SetStringValue(functionName, eCSSUnit_Ident);
for (uint16_t index = 0; index + 1 < numElements; ++index)
convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
/* Fill in the outparam value with the array. */
aValue.SetArrayValue(convertedArray, eCSSUnit_Function);
/* Return it! */
return true;
}
/**
* Given a token, determines the minimum and maximum number of function
* parameters to read, along with the mask that should be used to read
* those function parameters. If the token isn't a transform function,
* returns an error.
*
* @param aToken The token identifying the function.
* @param aMinElems [out] The minimum number of elements to read.
* @param aMaxElems [out] The maximum number of elements to read
* @param aVariantMask [out] The variant mask to use during parsing
* @return Whether the information was loaded successfully.
*/
static bool GetFunctionParseInformation(nsCSSKeyword aToken,
bool aIsPrefixed,
uint16_t &aMinElems,
uint16_t &aMaxElems,
const int32_t *& aVariantMask,
bool &aIs3D)
{
/* These types represent the common variant masks that will be used to
* parse out the individual functions. The order in the enumeration
* must match the order in which the masks are declared.
*/
enum { eLengthPercentCalc,
eLengthCalc,
eTwoLengthPercentCalcs,
eTwoLengthPercentCalcsOneLengthCalc,
eAngle,
eTwoAngles,
eNumber,
ePositiveLength,
eTwoNumbers,
eThreeNumbers,
eThreeNumbersOneAngle,
eMatrix,
eMatrixPrefixed,
eMatrix3d,
eMatrix3dPrefixed,
eNumVariantMasks };
static const int32_t kMaxElemsPerFunction = 16;
static const int32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
{VARIANT_LPCALC},
{VARIANT_LENGTH|VARIANT_CALC},
{VARIANT_LPCALC, VARIANT_LPCALC},
{VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC},
{VARIANT_ANGLE_OR_ZERO},
{VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
{VARIANT_NUMBER},
{VARIANT_LENGTH|VARIANT_POSITIVE_DIMENSION},
{VARIANT_NUMBER, VARIANT_NUMBER},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_LPNCALC, VARIANT_LPNCALC},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
{VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
#ifdef DEBUG
static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
{1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16};
#endif
int32_t variantIndex = eNumVariantMasks;
aIs3D = false;
switch (aToken) {
case eCSSKeyword_translatex:
case eCSSKeyword_translatey:
/* Exactly one length or percent. */
variantIndex = eLengthPercentCalc;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_translatez:
/* Exactly one length */
variantIndex = eLengthCalc;
aMinElems = 1U;
aMaxElems = 1U;
aIs3D = true;
break;
case eCSSKeyword_translate3d:
/* Exactly two lengthds or percents and a number */
variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
aMinElems = 3U;
aMaxElems = 3U;
aIs3D = true;
break;
case eCSSKeyword_scalez:
aIs3D = true;
case eCSSKeyword_scalex:
case eCSSKeyword_scaley:
/* Exactly one scale factor. */
variantIndex = eNumber;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_scale3d:
/* Exactly three scale factors. */
variantIndex = eThreeNumbers;
aMinElems = 3U;
aMaxElems = 3U;
aIs3D = true;
break;
case eCSSKeyword_rotatex:
case eCSSKeyword_rotatey:
aIs3D = true;
case eCSSKeyword_rotate:
case eCSSKeyword_rotatez:
/* Exactly one angle. */
variantIndex = eAngle;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_rotate3d:
variantIndex = eThreeNumbersOneAngle;
aMinElems = 4U;
aMaxElems = 4U;
aIs3D = true;
break;
case eCSSKeyword_translate:
/* One or two lengths or percents. */
variantIndex = eTwoLengthPercentCalcs;
aMinElems = 1U;
aMaxElems = 2U;
break;
case eCSSKeyword_skew:
/* Exactly one or two angles. */
variantIndex = eTwoAngles;
aMinElems = 1U;
aMaxElems = 2U;
break;
case eCSSKeyword_scale:
/* One or two scale factors. */
variantIndex = eTwoNumbers;
aMinElems = 1U;
aMaxElems = 2U;
break;
case eCSSKeyword_skewx:
/* Exactly one angle. */
variantIndex = eAngle;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_skewy:
/* Exactly one angle. */
variantIndex = eAngle;
aMinElems = 1U;
aMaxElems = 1U;
break;
case eCSSKeyword_matrix:
/* Six values, all numbers. */
variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
aMinElems = 6U;
aMaxElems = 6U;
break;
case eCSSKeyword_matrix3d:
/* 16 matrix values, all numbers */
variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
aMinElems = 16U;
aMaxElems = 16U;
aIs3D = true;
break;
case eCSSKeyword_perspective:
/* Exactly one scale number. */
variantIndex = ePositiveLength;
aMinElems = 1U;
aMaxElems = 1U;
aIs3D = true;
break;
default:
/* Oh dear, we didn't match. Report an error. */
return false;
}
NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
#ifdef DEBUG
NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
"Invalid aMaxElems for this variant mask.");
#endif
// Convert the index into a mask.
aVariantMask = kVariantMasks[variantIndex];
return true;
}
/* Reads a single transform function from the tokenizer stream, reporting an
* error if something goes wrong.
*/
bool
CSSParserImpl::ParseSingleTransform(bool aIsPrefixed,
nsCSSValue& aValue, bool& aIs3D)
{
if (!GetToken(true))
return false;
if (mToken.mType != eCSSToken_Function) {
UngetToken();
return false;
}
const int32_t* variantMask;
uint16_t minElems, maxElems;
nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
if (!GetFunctionParseInformation(keyword, aIsPrefixed,
minElems, maxElems, variantMask, aIs3D))
return false;
// Bug 721136: Normalize the identifier to lowercase, except that things
// like scaleX should have the last character capitalized. This matches
// what other browsers do.
nsContentUtils::ASCIIToLower(mToken.mIdent);
switch (keyword) {
case eCSSKeyword_rotatex:
case eCSSKeyword_scalex:
case eCSSKeyword_skewx:
case eCSSKeyword_translatex:
mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('X'));
break;
case eCSSKeyword_rotatey:
case eCSSKeyword_scaley:
case eCSSKeyword_skewy:
case eCSSKeyword_translatey:
mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Y'));
break;
case eCSSKeyword_rotatez:
case eCSSKeyword_scalez:
case eCSSKeyword_translatez:
mToken.mIdent.Replace(mToken.mIdent.Length() - 1, 1, PRUnichar('Z'));
break;
default:
break;
}
return ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, aValue);
}
/* Parses a transform property list by continuously reading in properties
* and constructing a matrix from it.
*/
bool CSSParserImpl::ParseTransform(bool aIsPrefixed)
{
nsCSSValue value;
if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
// 'inherit', 'initial', and 'none' must be alone
if (!ExpectEndProperty()) {
return false;
}
} else {
nsCSSValueList* cur = value.SetListValue();
for (;;) {
bool is3D;
if (!ParseSingleTransform(aIsPrefixed, cur->mValue, is3D)) {
return false;
}
if (is3D && !nsLayoutUtils::Are3DTransformsEnabled()) {
return false;
}
if (CheckEndProperty()) {
break;
}
cur->mNext = new nsCSSValueList;
cur = cur->mNext;
}
}
AppendValue(eCSSProperty_transform, value);
return true;
}
bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
{
nsCSSValuePair position;
if (!ParseBoxPositionValues(position, true))
return false;
nsCSSProperty prop = eCSSProperty_transform_origin;
if (aPerspective) {
if (!ExpectEndProperty()) {
return false;
}
prop = eCSSProperty_perspective_origin;
}
// Unlike many other uses of pairs, this position should always be stored
// as a pair, even if the values are the same, so it always serializes as
// a pair, and to keep the computation code simple.
if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
position.mXValue.GetUnit() == eCSSUnit_Initial) {
NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
"inherit/initial only half?");
AppendValue(prop, position.mXValue);
} else {
nsCSSValue value;
if (aPerspective) {
value.SetPairValue(position.mXValue, position.mYValue);
} else {
nsCSSValue depth;
if (!nsLayoutUtils::Are3DTransformsEnabled() ||
// only try parsing if 3-D transforms are enabled
!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) {
depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
}
value.SetTripletValue(position.mXValue, position.mYValue, depth);
}
AppendValue(prop, value);
}
return true;
}
bool
CSSParserImpl::ParseTransitionProperty()
{