Bug 929011 - Simplify path and transform parsing. r=dholbert

This commit is contained in:
Robert Longson 2013-10-29 13:29:28 +00:00
parent b6add35df3
commit 04fbfdf00a
17 changed files with 606 additions and 1545 deletions

View File

@ -10,7 +10,6 @@
// Keep others in (case-insensitive) order:
#include "gfxMatrix.h"
#include "mozilla/dom/SVGSVGElement.h"
#include "mozilla/RangedPtr.h"
#include "nsComputedDOMStyle.h"
#include "nsFontMetrics.h"
#include "nsIFrame.h"
@ -390,24 +389,22 @@ DecimalDigitValue(PRUnichar aCh)
template<class floatType>
bool
SVGContentUtils::ParseNumber(const nsAString& aString,
floatType& aValue,
nsAString& aLeftOver)
SVGContentUtils::ParseNumber(RangedPtr<const PRUnichar>& aIter,
const RangedPtr<const PRUnichar>& aEnd,
floatType& aValue)
{
mozilla::RangedPtr<const PRUnichar> iter(aString.Data(), aString.Length());
const mozilla::RangedPtr<const PRUnichar> end(aString.Data() + aString.Length(),
aString.Data(), aString.Length());
if (iter == end) {
if (aIter == aEnd) {
return false;
}
RangedPtr<const PRUnichar> iter(aIter);
// Sign of the mantissa (-1 or 1).
int32_t sign = *iter == '-' ? -1 : 1;
if (*iter == '-' || *iter == '+') {
++iter;
if (iter == end) {
if (iter == aEnd) {
return false;
}
}
@ -424,9 +421,9 @@ SVGContentUtils::ParseNumber(const nsAString& aString,
do {
intPart = floatType(10) * intPart + DecimalDigitValue(*iter);
++iter;
} while (iter != end && IsDigit(*iter));
} while (iter != aEnd && IsDigit(*iter));
if (iter != end) {
if (iter != aEnd) {
gotDot = *iter == '.';
}
}
@ -436,7 +433,7 @@ SVGContentUtils::ParseNumber(const nsAString& aString,
if (gotDot) {
++iter;
if (iter == end || !IsDigit(*iter)) {
if (iter == aEnd || !IsDigit(*iter)) {
return false;
}
// Power of ten by which we need to divide our next digit
@ -445,23 +442,23 @@ SVGContentUtils::ParseNumber(const nsAString& aString,
fracPart += DecimalDigitValue(*iter) / divisor;
divisor *= 10;
++iter;
} while (iter != end && IsDigit(*iter));
} while (iter != aEnd && IsDigit(*iter));
}
bool gotE = false;
int32_t exponent = 0;
int32_t expSign;
if (iter != end && (*iter == 'e' || *iter == 'E')) {
if (iter != aEnd && (*iter == 'e' || *iter == 'E')) {
mozilla::RangedPtr<const PRUnichar> expIter(iter);
RangedPtr<const PRUnichar> expIter(iter);
++expIter;
if (expIter != end) {
if (expIter != aEnd) {
expSign = *expIter == '-' ? -1 : 1;
if (*expIter == '-' || *expIter == '+') {
++expIter;
if (expIter != end && IsDigit(*expIter)) {
if (expIter != aEnd && IsDigit(*expIter)) {
// At this point we're sure this is an exponent
// and not the start of a unit such as em or ex.
gotE = true;
@ -474,41 +471,45 @@ SVGContentUtils::ParseNumber(const nsAString& aString,
do {
exponent = 10 * exponent + DecimalDigitValue(*iter);
++iter;
} while (iter != end && IsDigit(*iter));
} while (iter != aEnd && IsDigit(*iter));
}
}
// Assemble the number
aValue = sign * (intPart + fracPart);
floatType value = sign * (intPart + fracPart);
if (gotE) {
aValue *= pow(floatType(10), floatType(expSign * exponent));
value *= pow(floatType(10), floatType(expSign * exponent));
}
aLeftOver = Substring(iter.get(), end.get());
return NS_finite(aValue);
if (!NS_finite(value)) {
return false;
}
aIter = iter;
aValue = value;
return true;
}
template bool
SVGContentUtils::ParseNumber<float>(const nsAString& aString,
float& aValue,
nsAString& aLeftOver);
template bool
SVGContentUtils::ParseNumber<double>(const nsAString& aString,
double& aValue,
nsAString& aLeftOver);
RangedPtr<const PRUnichar>
SVGContentUtils::GetStartRangedPtr(const nsAString& aString)
{
return RangedPtr<const PRUnichar>(aString.Data(), aString.Length());
}
RangedPtr<const PRUnichar>
SVGContentUtils::GetEndRangedPtr(const nsAString& aString)
{
return RangedPtr<const PRUnichar>(aString.Data() + aString.Length(),
aString.Data(), aString.Length());
}
template<class floatType>
bool
SVGContentUtils::ParseNumber(const nsAString& aString,
floatType& aValue)
{
nsAutoString leftOver;
RangedPtr<const PRUnichar> iter = GetStartRangedPtr(aString);
const RangedPtr<const PRUnichar> end = GetEndRangedPtr(aString);
if (!ParseNumber(aString, aValue, leftOver)) {
return false;
}
return leftOver.IsEmpty();
return ParseNumber(iter, end, aValue) && iter == end;
}
template bool
@ -522,9 +523,8 @@ bool
SVGContentUtils::ParseInteger(const nsAString& aString,
int32_t& aValue)
{
mozilla::RangedPtr<const PRUnichar> iter(aString.Data(), aString.Length());
const mozilla::RangedPtr<const PRUnichar> end(aString.Data() + aString.Length(),
aString.Data(), aString.Length());
RangedPtr<const PRUnichar> iter = GetStartRangedPtr(aString);
const RangedPtr<const PRUnichar> end = GetEndRangedPtr(aString);
if (iter == end) {
return false;

View File

@ -11,6 +11,7 @@
#include <math.h>
#include "gfxMatrix.h"
#include "mozilla/RangedPtr.h"
class nsIContent;
class nsIDocument;
@ -132,16 +133,24 @@ public:
float aViewboxWidth, float aViewboxHeight,
const SVGPreserveAspectRatio &aPreserveAspectRatio);
static mozilla::RangedPtr<const PRUnichar>
GetStartRangedPtr(const nsAString& aString);
static mozilla::RangedPtr<const PRUnichar>
GetEndRangedPtr(const nsAString& aString);
/**
* Parse a number of the form:
* number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)?
* Parsing fails if the number cannot be represented by a floatType.
* Anything after the number is returned in aLeftOver.
* If parsing succeeds, aIter is updated so that it points to the character
* after the end of the number, otherwise it is left unchanged
*/
template<class floatType>
static bool
ParseNumber(const nsAString& aString, floatType& aValue,
nsAString& aLeftOver);
ParseNumber(mozilla::RangedPtr<const PRUnichar>& aIter,
const mozilla::RangedPtr<const PRUnichar>& aEnd,
floatType& aValue);
/**
* Parse a number of the form:

View File

@ -34,15 +34,20 @@ SVGLength::GetValueAsString(nsAString &aValue) const
}
bool
SVGLength::SetValueFromString(const nsAString &aValueAsString)
SVGLength::SetValueFromString(const nsAString &aString)
{
nsAutoString units;
RangedPtr<const PRUnichar> iter =
SVGContentUtils::GetStartRangedPtr(aString);
const RangedPtr<const PRUnichar> end =
SVGContentUtils::GetEndRangedPtr(aString);
float value;
if (!SVGContentUtils::ParseNumber(aValueAsString, value, units)) {
if (!SVGContentUtils::ParseNumber(iter, end, value)) {
return false;
}
const nsAString& units = Substring(iter.get(), end.get());
uint16_t unitType = GetUnitTypeForString(units);
if (!IsValidUnitType(unitType)) {
return false;

View File

@ -241,13 +241,13 @@ SVGMotionSMILAnimationFunction::RebuildPathAndVerticesFromPathAttr()
// Generate gfxPath from |path| attr
SVGPathData path;
nsSVGPathDataParserToInternal pathParser(&path);
nsSVGPathDataParser pathParser(pathSpec, &path);
// We ignore any failure returned from Parse() since the SVG spec says to
// accept all segments up to the first invalid token. Instead we must
// explicitly check that the parse produces at least one path segment (if
// the path data doesn't begin with a valid "M", then it's invalid).
pathParser.Parse(pathSpec);
pathParser.Parse();
if (!path.Length()) {
return;
}

View File

@ -71,8 +71,8 @@ SVGPathData::SetValueFromString(const nsAString& aValue)
// the first error. We still return any error though so that callers know if
// there's a problem.
nsSVGPathDataParserToInternal pathParser(this);
return pathParser.Parse(aValue);
nsSVGPathDataParser pathParser(aValue, this);
return pathParser.Parse() ? NS_OK : NS_ERROR_DOM_SYNTAX_ERR;
}
nsresult

View File

@ -20,7 +20,7 @@
class gfxContext;
class gfxPath;
class nsSVGPathDataParserToInternal; // IWYU pragma: keep
class nsSVGPathDataParser; // IWYU pragma: keep
struct gfxMatrix;
struct nsSVGMark;
@ -78,8 +78,8 @@ class SVGPathData
friend class SVGAnimatedPathSegList;
friend class DOMSVGPathSegList;
friend class DOMSVGPathSeg;
friend class ::nsSVGPathDataParserToInternal;
// nsSVGPathDataParserToInternal will not keep wrappers in sync, so consumers
friend class ::nsSVGPathDataParser;
// nsSVGPathDataParser will not keep wrappers in sync, so consumers
// are responsible for that!
typedef gfx::DrawTarget DrawTarget;

View File

@ -60,15 +60,21 @@ SVGPointList::SetValueFromString(const nsAString& aValue)
while (tokenizer.hasMoreTokens()) {
const nsAString& token = tokenizer.nextToken();
RangedPtr<const PRUnichar> iter =
SVGContentUtils::GetStartRangedPtr(token);
const RangedPtr<const PRUnichar> end =
SVGContentUtils::GetEndRangedPtr(token);
float x;
nsAutoString leftOver;
if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), x, leftOver)) {
if (!SVGContentUtils::ParseNumber(iter, end, x)) {
rv = NS_ERROR_DOM_SYNTAX_ERR;
break;
}
float y;
if (leftOver.IsEmpty()) {
if (iter == end) {
if (!tokenizer.hasMoreTokens() ||
!SVGContentUtils::ParseNumber(tokenizer.nextToken(), y)) {
rv = NS_ERROR_DOM_SYNTAX_ERR;
@ -77,6 +83,7 @@ SVGPointList::SetValueFromString(const nsAString& aValue)
} else {
// It's possible for the token to be 10-30 which has
// no separator but needs to be parsed as 10, -30
const nsAString& leftOver = Substring(iter.get(), end.get());
if (leftOver[0] != '-' || !SVGContentUtils::ParseNumber(leftOver, y)) {
rv = NS_ERROR_DOM_SYNTAX_ERR;
break;

View File

@ -70,10 +70,8 @@ SVGTransformList::GetValueAsString(nsAString& aValue) const
nsresult
SVGTransformList::SetValueFromString(const nsAString& aValue)
{
SVGTransformListParser parser;
nsresult rv = parser.Parse(aValue);
if (NS_FAILED(rv)) {
SVGTransformListParser parser(aValue);
if (!parser.Parse()) {
// there was a parse error.
return NS_ERROR_DOM_SYNTAX_ERR;
}

View File

@ -7,201 +7,138 @@
#include "mozilla/Util.h"
#include "SVGTransformListParser.h"
#include "SVGContentUtils.h"
#include "nsSVGTransform.h"
#include "nsError.h"
#include "nsGkAtoms.h"
#include "nsIAtom.h"
#include "plstr.h"
using namespace mozilla;
//----------------------------------------------------------------------
// private methods
nsresult
SVGTransformListParser::Match()
bool
SVGTransformListParser::Parse()
{
mTransforms.Clear();
return MatchTransformList();
return ParseTransforms();
}
nsresult
SVGTransformListParser::MatchTransformList()
{
MatchWsp();
if (IsTokenTransformStarter()) {
ENSURE_MATCHED(MatchTransforms());
}
MatchWsp();
return NS_OK;
}
nsresult
SVGTransformListParser::MatchTransforms()
{
ENSURE_MATCHED(MatchTransform());
while (mTokenType != END) {
const char* pos = mTokenPos;
/* Curiously the SVG BNF allows multiple comma-wsp between transforms */
while (IsTokenCommaWspStarter()) {
ENSURE_MATCHED(MatchCommaWsp());
}
if (IsTokenTransformStarter()) {
ENSURE_MATCHED(MatchTransform());
} else {
if (pos != mTokenPos) RewindTo(pos);
break;
}
}
return NS_OK;
}
nsresult
SVGTransformListParser::GetTransformToken(nsIAtom** aKeyAtom,
bool aAdvancePos)
{
if (mTokenType != OTHER || *mTokenPos == '\0') {
return NS_ERROR_FAILURE;
}
nsresult rv = NS_OK;
const char* delimiters = "\x20\x9\xD\xA,(";
char* delimiterStart = PL_strnpbrk(mTokenPos, delimiters, 11);
if (delimiterStart != 0) {
/* save this character and null it out */
char holdingChar = *delimiterStart;
*delimiterStart = '\0';
uint32_t len;
if ((len = strlen(mTokenPos)) > 0) {
*aKeyAtom = NS_NewAtom(Substring(mTokenPos, mTokenPos + len)).get();
if (aAdvancePos) {
mInputPos = mTokenPos + len;
mTokenPos = mInputPos;
}
} else {
rv = NS_ERROR_FAILURE;
}
/* reset character back to original */
*delimiterStart = holdingChar;
} else {
rv = NS_ERROR_FAILURE;
}
return rv;
}
nsresult
SVGTransformListParser::MatchTransform()
{
nsCOMPtr<nsIAtom> keyatom;
nsresult rv = GetTransformToken(getter_AddRefs(keyatom), true);
if (NS_FAILED(rv)) {
return rv;
}
if (keyatom == nsGkAtoms::translate) {
ENSURE_MATCHED(MatchTranslate());
} else if (keyatom == nsGkAtoms::scale) {
ENSURE_MATCHED(MatchScale());
} else if (keyatom == nsGkAtoms::rotate) {
ENSURE_MATCHED(MatchRotate());
} else if (keyatom == nsGkAtoms::skewX) {
ENSURE_MATCHED(MatchSkewX());
} else if (keyatom == nsGkAtoms::skewY) {
ENSURE_MATCHED(MatchSkewY());
} else if (keyatom == nsGkAtoms::matrix) {
ENSURE_MATCHED(MatchMatrix());
} else {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool
SVGTransformListParser::IsTokenTransformStarter()
SVGTransformListParser::ParseTransforms()
{
nsCOMPtr<nsIAtom> keyatom;
nsresult rv = GetTransformToken(getter_AddRefs(keyatom), false);
if (NS_FAILED(rv)) {
return false;
}
if (keyatom == nsGkAtoms::translate ||
keyatom == nsGkAtoms::scale ||
keyatom == nsGkAtoms::rotate ||
keyatom == nsGkAtoms::skewX ||
keyatom == nsGkAtoms::skewY ||
keyatom == nsGkAtoms::matrix) {
if (!SkipWsp()) {
return true;
}
if (!ParseTransform()) {
return false;
}
while (SkipWsp()) {
// The SVG BNF allows multiple comma-wsp between transforms
while (*mIter == ',') {
++mIter;
if (!SkipWsp()) {
return false;
}
}
if (!ParseTransform()) {
return false;
}
}
return true;
}
bool
SVGTransformListParser::ParseTransform()
{
RangedPtr<const PRUnichar> start(mIter);
while (IsAlpha(*mIter)) {
++mIter;
if (mIter == mEnd) {
return false;
}
}
if (start == mIter) {
// Didn't read anything
return false;
}
const nsAString& transform = Substring(start.get(), mIter.get());
nsIAtom* keyAtom = NS_GetStaticAtom(transform);
if (!keyAtom || !SkipWsp()) {
return false;
}
if (keyAtom == nsGkAtoms::translate) {
return ParseTranslate();
}
if (keyAtom == nsGkAtoms::scale) {
return ParseScale();
}
if (keyAtom == nsGkAtoms::rotate) {
return ParseRotate();
}
if (keyAtom == nsGkAtoms::skewX) {
return ParseSkewX();
}
if (keyAtom == nsGkAtoms::skewY) {
return ParseSkewY();
}
if (keyAtom == nsGkAtoms::matrix) {
return ParseMatrix();
}
return false;
}
nsresult
SVGTransformListParser::MatchNumberArguments(float *aResult,
uint32_t aMaxNum,
uint32_t *aParsedNum)
bool
SVGTransformListParser::ParseArguments(float* aResult,
uint32_t aMaxCount,
uint32_t* aParsedCount)
{
*aParsedNum = 0;
if (*mIter != '(') {
return false;
}
++mIter;
MatchWsp();
ENSURE_MATCHED(MatchLeftParen());
MatchWsp();
ENSURE_MATCHED(MatchNumber(&aResult[0]));
*aParsedNum = 1;
while (IsTokenCommaWspStarter()) {
MatchWsp();
if (mTokenType == RIGHT_PAREN) {
break;
}
if (*aParsedNum == aMaxNum) {
return NS_ERROR_FAILURE;
}
if (IsTokenCommaWspStarter()) {
MatchCommaWsp();
}
ENSURE_MATCHED(MatchNumber(&aResult[(*aParsedNum)++]));
if (!SkipWsp()) {
return false;
}
MatchWsp();
if (!SVGContentUtils::ParseNumber(mIter, mEnd, aResult[0])) {
return false;
}
*aParsedCount = 1;
ENSURE_MATCHED(MatchRightParen());
return NS_OK;
while (SkipWsp()) {
if (*mIter == ')') {
++mIter;
return true;
}
if (*aParsedCount == aMaxCount) {
return false;
}
SkipCommaWsp();
if (!SVGContentUtils::ParseNumber(mIter, mEnd, aResult[(*aParsedCount)++])) {
return false;
}
}
return false;
}
nsresult
SVGTransformListParser::MatchTranslate()
bool
SVGTransformListParser::ParseTranslate()
{
GetNextToken();
float t[2];
uint32_t count;
ENSURE_MATCHED(MatchNumberArguments(t, ArrayLength(t), &count));
if (!ParseArguments(t, ArrayLength(t), &count)) {
return false;
}
switch (count) {
case 1:
@ -210,27 +147,26 @@ SVGTransformListParser::MatchTranslate()
case 2:
{
nsSVGTransform* transform = mTransforms.AppendElement();
NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
if (!transform) {
return false;
}
transform->SetTranslate(t[0], t[1]);
break;
return true;
}
default:
return NS_ERROR_FAILURE;
}
return NS_OK;
return false;
}
nsresult
SVGTransformListParser::MatchScale()
bool
SVGTransformListParser::ParseScale()
{
GetNextToken();
float s[2];
uint32_t count;
ENSURE_MATCHED(MatchNumberArguments(s, ArrayLength(s), &count));
if (!ParseArguments(s, ArrayLength(s), &count)) {
return false;
}
switch (count) {
case 1:
@ -239,27 +175,27 @@ SVGTransformListParser::MatchScale()
case 2:
{
nsSVGTransform* transform = mTransforms.AppendElement();
NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
if (!transform) {
return false;
}
transform->SetScale(s[0], s[1]);
break;
return true;
}
default:
return NS_ERROR_FAILURE;
}
return NS_OK;
return false;
}
nsresult
SVGTransformListParser::MatchRotate()
bool
SVGTransformListParser::ParseRotate()
{
GetNextToken();
float r[3];
uint32_t count;
ENSURE_MATCHED(MatchNumberArguments(r, ArrayLength(r), &count));
if (!ParseArguments(r, ArrayLength(r), &count)) {
return false;
}
switch (count) {
case 1:
@ -268,79 +204,70 @@ SVGTransformListParser::MatchRotate()
case 3:
{
nsSVGTransform* transform = mTransforms.AppendElement();
NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
if (!transform) {
return false;
}
transform->SetRotate(r[0], r[1], r[2]);
break;
return true;
}
default:
return NS_ERROR_FAILURE;
}
return NS_OK;
return false;
}
nsresult
SVGTransformListParser::MatchSkewX()
bool
SVGTransformListParser::ParseSkewX()
{
GetNextToken();
float skew;
uint32_t count;
ENSURE_MATCHED(MatchNumberArguments(&skew, 1, &count));
if (count != 1) {
return NS_ERROR_FAILURE;
if (!ParseArguments(&skew, 1, &count) || count != 1) {
return false;
}
nsSVGTransform* transform = mTransforms.AppendElement();
NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
if (!transform) {
return false;
}
transform->SetSkewX(skew);
return NS_OK;
return true;
}
nsresult
SVGTransformListParser::MatchSkewY()
bool
SVGTransformListParser::ParseSkewY()
{
GetNextToken();
float skew;
uint32_t count;
ENSURE_MATCHED(MatchNumberArguments(&skew, 1, &count));
if (count != 1) {
return NS_ERROR_FAILURE;
if (!ParseArguments(&skew, 1, &count) || count != 1) {
return false;
}
nsSVGTransform* transform = mTransforms.AppendElement();
NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
if (!transform) {
return false;
}
transform->SetSkewY(skew);
return NS_OK;
return true;
}
nsresult
SVGTransformListParser::MatchMatrix()
bool
SVGTransformListParser::ParseMatrix()
{
GetNextToken();
float m[6];
uint32_t count;
ENSURE_MATCHED(MatchNumberArguments(m, ArrayLength(m), &count));
if (count != 6) {
return NS_ERROR_FAILURE;
if (!ParseArguments(m, ArrayLength(m), &count) || count != 6) {
return false;
}
nsSVGTransform* transform = mTransforms.AppendElement();
NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
if (!transform) {
return false;
}
transform->SetMatrix(gfxMatrix(m[0], m[1], m[2], m[3], m[4], m[5]));
return NS_OK;
return true;
}

View File

@ -12,14 +12,11 @@
#include "nsTArray.h"
////////////////////////////////////////////////////////////////////////
// SVGTransformListParser: taken from nsSVGPathDataParser, a simple
// recursive descent parser that builds the transform lists from the
// transform attributes. The grammar for path data
// SVGTransformListParser: A simple recursive descent parser that builds
// transform lists from transform attributes. The grammar for path data
// can be found in SVG 1.1, chapter 7.
// http://www.w3.org/TR/SVG11/coords.html#TransformAttribute
class nsIAtom;
namespace mozilla {
class nsSVGTransform;
@ -27,36 +24,33 @@ class nsSVGTransform;
class SVGTransformListParser : public nsSVGDataParser
{
public:
SVGTransformListParser(const nsAString& aValue)
: nsSVGDataParser(aValue) {}
bool Parse();
const nsTArray<nsSVGTransform>& GetTransformList() const {
return mTransforms;
}
private:
nsTArray<nsSVGTransform> mTransforms;
// helpers
virtual nsresult Match() MOZ_OVERRIDE;
bool ParseArguments(float *aResult,
uint32_t aMaxCount,
uint32_t *aParsedCount);
nsresult MatchNumberArguments(float *aResult,
uint32_t aMaxNum,
uint32_t *aParsedNum);
bool ParseTransforms();
nsresult MatchTransformList();
bool ParseTransform();
nsresult GetTransformToken(nsIAtom** aKeyatom, bool aAdvancePos);
nsresult MatchTransforms();
bool ParseTranslate();
bool ParseScale();
bool ParseRotate();
bool ParseSkewX();
bool ParseSkewY();
bool ParseMatrix();
nsresult MatchTransform();
bool IsTokenTransformStarter();
nsresult MatchTranslate();
nsresult MatchScale();
nsresult MatchRotate();
nsresult MatchSkewX();
nsresult MatchSkewY();
nsresult MatchMatrix();
FallibleTArray<nsSVGTransform> mTransforms;
};
} // namespace mozilla

View File

@ -94,16 +94,20 @@ GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
}
static bool
GetValueFromString(const nsAString& aValueAsString,
GetValueFromString(const nsAString& aString,
float& aValue,
uint16_t* aUnitType)
{
nsAutoString units;
RangedPtr<const PRUnichar> iter =
SVGContentUtils::GetStartRangedPtr(aString);
const RangedPtr<const PRUnichar> end =
SVGContentUtils::GetEndRangedPtr(aString);
if (!SVGContentUtils::ParseNumber(aValueAsString, aValue, units)) {
if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
return false;
}
const nsAString& units = Substring(iter.get(), end.get());
*aUnitType = GetUnitTypeForString(units);
return IsValidUnitType(*aUnitType);
}

View File

@ -3,298 +3,37 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* NOTE:
*
* How should subclasses use this class?
* This class was separated from the nsSVGPathDataParser class to share
* functionality found to be common in other descent parsers in this component.
*
* A subclass should implement a Match method which gets invoked from the
* Parse method. The Parse method can be overridden, as required.
*
*/
#include "nsSVGDataParser.h"
#include "prdtoa.h"
#include "nsMathUtils.h"
#include "nsMemory.h"
#include "nsReadableUtils.h"
#include <stdlib.h>
#include <math.h>
#include "SVGContentUtils.h"
//----------------------------------------------------------------------
// public interface
nsresult
nsSVGDataParser::Parse(const nsAString &aValue)
nsSVGDataParser::nsSVGDataParser(const nsAString& aValue)
: mIter(SVGContentUtils::GetStartRangedPtr(aValue)),
mEnd(SVGContentUtils::GetEndRangedPtr(aValue))
{
char *str = ToNewUTF8String(aValue);
if (!str)
return NS_ERROR_OUT_OF_MEMORY;
mInputPos = str;
GetNextToken();
nsresult rv = Match();
if (mTokenType != END)
rv = NS_ERROR_FAILURE; // not all tokens were consumed
mInputPos = nullptr;
nsMemory::Free(str);
return rv;
}
//----------------------------------------------------------------------
// helpers
void nsSVGDataParser::GetNextToken()
bool
nsSVGDataParser::SkipCommaWsp()
{
mTokenPos = mInputPos;
mTokenVal = *mInputPos;
switch (mTokenVal) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
mTokenType = DIGIT;
break;
case '\x20': case '\x9': case '\xd': case '\xa':
mTokenType = WSP;
break;
case ',':
mTokenType = COMMA;
break;
case '+': case '-':
mTokenType = SIGN;
break;
case '.':
mTokenType = POINT;
break;
case '(':
mTokenType = LEFT_PAREN;
break;
case ')':
mTokenType = RIGHT_PAREN;
break;
case '\0':
mTokenType = END;
break;
default:
mTokenType = OTHER;
if (!SkipWsp()) {
// end of string
return false;
}
if (*mInputPos != '\0') {
++mInputPos;
if (*mIter != ',') {
return true;
}
++mIter;
return SkipWsp();
}
void nsSVGDataParser::RewindTo(const char* aPos)
bool
nsSVGDataParser::SkipWsp()
{
mInputPos = aPos;
GetNextToken();
}
//----------------------------------------------------------------------
nsresult nsSVGDataParser::MatchNumber(float* aX)
{
const char* pos = mTokenPos;
if (mTokenType == SIGN)
GetNextToken();
const char* pos2 = mTokenPos;
nsresult rv = MatchFloatingPointConst();
if (NS_FAILED(rv)) {
RewindTo(pos2);
ENSURE_MATCHED(MatchIntegerConst());
}
char* end;
/* PR_strtod is not particularily fast. We only need a float and not a double so
* we could probably use something faster here if needed. The CSS parser uses
* nsCSSScanner::ParseNumber() instead of PR_strtod. See bug 516396 for some
* additional info. */
*aX = float(PR_strtod(pos, &end));
if (pos != end && NS_finite(*aX)) {
return NS_OK;
}
return NS_ERROR_FAILURE;
}
bool nsSVGDataParser::IsTokenNumberStarter()
{
return (mTokenType == DIGIT || mTokenType == POINT || mTokenType == SIGN);
}
//----------------------------------------------------------------------
nsresult nsSVGDataParser::MatchCommaWsp()
{
switch (mTokenType) {
case WSP:
ENSURE_MATCHED(MatchWsp());
if (mTokenType == COMMA)
GetNextToken();
break;
case COMMA:
GetNextToken();
break;
default:
return NS_ERROR_FAILURE;
}
while (IsTokenWspStarter()) {
ENSURE_MATCHED(MatchWsp());
}
return NS_OK;
}
bool nsSVGDataParser::IsTokenCommaWspStarter()
{
return (IsTokenWspStarter() || mTokenType == COMMA);
}
//----------------------------------------------------------------------
nsresult nsSVGDataParser::MatchIntegerConst()
{
ENSURE_MATCHED(MatchDigitSeq());
return NS_OK;
}
//----------------------------------------------------------------------
nsresult nsSVGDataParser::MatchFloatingPointConst()
{
// XXX inefficient implementation. It would be nice if we could make
// this predictive and wouldn't have to backtrack...
const char* pos = mTokenPos;
nsresult rv = MatchFractConst();
if (NS_SUCCEEDED(rv)) {
if (IsTokenExponentStarter())
ENSURE_MATCHED(MatchExponent());
}
else {
RewindTo(pos);
ENSURE_MATCHED(MatchDigitSeq());
ENSURE_MATCHED(MatchExponent());
}
return NS_OK;
}
//----------------------------------------------------------------------
nsresult nsSVGDataParser::MatchFractConst()
{
if (mTokenType == POINT) {
GetNextToken();
ENSURE_MATCHED(MatchDigitSeq());
}
else {
ENSURE_MATCHED(MatchDigitSeq());
if (mTokenType == POINT) {
GetNextToken();
if (IsTokenDigitSeqStarter()) {
ENSURE_MATCHED(MatchDigitSeq());
}
while (mIter != mEnd) {
if (!IsSVGWhitespace(*mIter)) {
return true;
}
++mIter;
}
return NS_OK;
return false;
}
//----------------------------------------------------------------------
nsresult nsSVGDataParser::MatchExponent()
{
if (!(tolower(mTokenVal) == 'e')) return NS_ERROR_FAILURE;
GetNextToken();
if (mTokenType == SIGN)
GetNextToken();
ENSURE_MATCHED(MatchDigitSeq());
return NS_OK;
}
bool nsSVGDataParser::IsTokenExponentStarter()
{
return (tolower(mTokenVal) == 'e');
}
//----------------------------------------------------------------------
nsresult nsSVGDataParser::MatchDigitSeq()
{
if (!(mTokenType == DIGIT)) return NS_ERROR_FAILURE;
do {
GetNextToken();
} while (mTokenType == DIGIT);
return NS_OK;
}
bool nsSVGDataParser::IsTokenDigitSeqStarter()
{
return (mTokenType == DIGIT);
}
//----------------------------------------------------------------------
nsresult nsSVGDataParser::MatchWsp()
{
if (!(mTokenType == WSP)) return NS_ERROR_FAILURE;
do {
GetNextToken();
} while (mTokenType == WSP);
return NS_OK;
}
bool nsSVGDataParser::IsTokenWspStarter()
{
return (mTokenType == WSP);
}
//----------------------------------------------------------------------
nsresult nsSVGDataParser::MatchLeftParen()
{
switch (mTokenType) {
case LEFT_PAREN:
GetNextToken();
break;
default:
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult nsSVGDataParser::MatchRightParen()
{
switch (mTokenType) {
case RIGHT_PAREN:
GetNextToken();
break;
default:
return NS_ERROR_FAILURE;
}
return NS_OK;
}

View File

@ -6,57 +6,32 @@
#ifndef __NS_SVGDATAPARSER_H__
#define __NS_SVGDATAPARSER_H__
#include "nsError.h"
#include "mozilla/RangedPtr.h"
#include "nsString.h"
//----------------------------------------------------------------------
// helper macros
#define ENSURE_MATCHED(exp) { nsresult rv = exp; if (NS_FAILED(rv)) return rv; }
////////////////////////////////////////////////////////////////////////
// nsSVGDataParser: a simple abstract class for parsing values
// nsSVGDataParser: a simple base class for parsing values
// for path and transform values.
//
class nsSVGDataParser
{
public:
nsresult Parse(const nsAString &aValue);
nsSVGDataParser(const nsAString& aValue);
protected:
const char* mInputPos;
const char* mTokenPos;
enum { DIGIT, WSP, COMMA, POINT, SIGN, LEFT_PAREN, RIGHT_PAREN, OTHER, END } mTokenType;
char mTokenVal;
static bool IsAlpha(PRUnichar aCh) {
// Exclude non-ascii characters before calling isalpha
return (aCh & 0x7f) == aCh && isalpha(aCh);
}
// helpers
void GetNextToken();
void RewindTo(const char* aPos);
virtual nsresult Match()=0;
// Returns true if there are more characters to read, false otherwise.
bool SkipCommaWsp();
nsresult MatchNumber(float* x);
bool IsTokenNumberStarter();
nsresult MatchCommaWsp();
bool IsTokenCommaWspStarter();
nsresult MatchIntegerConst();
nsresult MatchFloatingPointConst();
nsresult MatchFractConst();
nsresult MatchExponent();
bool IsTokenExponentStarter();
nsresult MatchDigitSeq();
bool IsTokenDigitSeqStarter();
nsresult MatchWsp();
bool IsTokenWspStarter();
// Returns true if there are more characters to read, false otherwise.
bool SkipWsp();
nsresult MatchLeftParen();
nsresult MatchRightParen();
mozilla::RangedPtr<const PRUnichar> mIter;
const mozilla::RangedPtr<const PRUnichar> mEnd;
};

View File

@ -122,14 +122,19 @@ GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
}
static bool
GetValueFromString(const nsAString& aValueAsString,
GetValueFromString(const nsAString& aString,
float& aValue,
uint16_t* aUnitType)
{
nsAutoString units;
if (!SVGContentUtils::ParseNumber(aValueAsString, aValue, units)) {
RangedPtr<const PRUnichar> iter =
SVGContentUtils::GetStartRangedPtr(aString);
const RangedPtr<const PRUnichar> end =
SVGContentUtils::GetEndRangedPtr(aString);
if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
return false;
}
const nsAString& units = Substring(iter.get(), end.get());
*aUnitType = GetUnitTypeForString(units);
return IsValidUnitType(*aUnitType);
}

View File

@ -49,22 +49,28 @@ static nsSVGAttrTearoffTable<nsSVGNumber2, nsSVGNumber2::DOMAnimatedNumber>
sSVGAnimatedNumberTearoffTable;
static bool
GetValueFromString(const nsAString& aValueAsString,
GetValueFromString(const nsAString& aString,
bool aPercentagesAllowed,
float& aValue)
{
nsAutoString units;
RangedPtr<const PRUnichar> iter =
SVGContentUtils::GetStartRangedPtr(aString);
const RangedPtr<const PRUnichar> end =
SVGContentUtils::GetEndRangedPtr(aString);
if (!SVGContentUtils::ParseNumber(aValueAsString, aValue, units)) {
if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
return false;
}
if (aPercentagesAllowed && units.EqualsLiteral("%")) {
aValue /= 100;
return true;
if (aPercentagesAllowed) {
const nsAString& units = Substring(iter.get(), end.get());
if (units.EqualsLiteral("%")) {
aValue /= 100;
return true;
}
}
return units.IsEmpty();
return iter == end;
}
nsresult

File diff suppressed because it is too large Load Diff

View File

@ -22,91 +22,43 @@ class SVGPathData;
class nsSVGPathDataParser : public nsSVGDataParser
{
protected:
// Path data storage
virtual nsresult StoreMoveTo(bool absCoords, float x, float y) = 0;
virtual nsresult StoreClosePath() = 0;
virtual nsresult StoreLineTo(bool absCoords, float x, float y) = 0;
virtual nsresult StoreHLineTo(bool absCoords, float x) = 0;
virtual nsresult StoreVLineTo(bool absCoords, float y) = 0;
virtual nsresult StoreCurveTo(bool absCoords, float x, float y,
float x1, float y1, float x2, float y2) = 0;
virtual nsresult StoreSmoothCurveTo(bool absCoords, float x, float y,
float x2, float y2) = 0;
virtual nsresult StoreQuadCurveTo(bool absCoords, float x, float y,
float x1, float y1) = 0;
virtual nsresult StoreSmoothQuadCurveTo(bool absCoords,
float x, float y) = 0;
virtual nsresult StoreEllipticalArc(bool absCoords, float x, float y,
float r1, float r2, float angle,
bool largeArcFlag, bool sweepFlag) = 0;
virtual nsresult Match() MOZ_OVERRIDE;
nsresult MatchCoordPair(float* aX, float* aY);
bool IsTokenCoordPairStarter();
public:
nsSVGPathDataParser(const nsAString& aValue,
mozilla::SVGPathData* aList)
: nsSVGDataParser(aValue),
mPathSegList(aList)
{
MOZ_ASSERT(aList, "null path data");
}
nsresult MatchCoord(float* aX);
bool IsTokenCoordStarter();
bool Parse();
nsresult MatchFlag(bool* f);
private:
nsresult MatchSvgPath();
nsresult MatchSubPaths();
bool IsTokenSubPathsStarter();
nsresult MatchSubPath();
bool IsTokenSubPathStarter();
nsresult MatchSubPathElements();
bool IsTokenSubPathElementsStarter();
bool ParseCoordPair(float& aX, float& aY);
bool ParseFlag(bool& aFlag);
nsresult MatchSubPathElement();
bool IsTokenSubPathElementStarter();
bool ParsePath();
bool IsStartOfSubPath() const;
bool ParseSubPath();
bool ParseSubPathElements();
bool ParseSubPathElement(PRUnichar aCommandType,
bool aAbsCoords);
nsresult MatchMoveto();
nsresult MatchMovetoArgSeq(bool absCoords);
nsresult MatchClosePath();
nsresult MatchLineto();
nsresult MatchLinetoArgSeq(bool absCoords);
bool IsTokenLinetoArgSeqStarter();
nsresult MatchHorizontalLineto();
nsresult MatchHorizontalLinetoArgSeq(bool absCoords);
nsresult MatchVerticalLineto();
nsresult MatchVerticalLinetoArgSeq(bool absCoords);
nsresult MatchCurveto();
nsresult MatchCurvetoArgSeq(bool absCoords);
nsresult MatchCurvetoArg(float* x, float* y, float* x1,
float* y1, float* x2, float* y2);
bool IsTokenCurvetoArgStarter();
nsresult MatchSmoothCurveto();
nsresult MatchSmoothCurvetoArgSeq(bool absCoords);
nsresult MatchSmoothCurvetoArg(float* x, float* y, float* x2, float* y2);
bool IsTokenSmoothCurvetoArgStarter();
nsresult MatchQuadBezierCurveto();
nsresult MatchQuadBezierCurvetoArgSeq(bool absCoords);
nsresult MatchQuadBezierCurvetoArg(float* x, float* y, float* x1, float* y1);
bool IsTokenQuadBezierCurvetoArgStarter();
nsresult MatchSmoothQuadBezierCurveto();
nsresult MatchSmoothQuadBezierCurvetoArgSeq(bool absCoords);
nsresult MatchEllipticalArc();
nsresult MatchEllipticalArcArgSeq(bool absCoords);
nsresult MatchEllipticalArcArg(float* x, float* y,
float* r1, float* r2, float* angle,
bool* largeArcFlag, bool* sweepFlag);
bool IsTokenEllipticalArcArgStarter();
};
bool ParseMoveto();
bool ParseClosePath();
bool ParseLineto(bool aAbsCoords);
bool ParseHorizontalLineto(bool aAbsCoords);
bool ParseVerticalLineto(bool aAbsCoords);
bool ParseCurveto(bool aAbsCoords);
bool ParseSmoothCurveto(bool aAbsCoords);
bool ParseQuadBezierCurveto(bool aAbsCoords);
bool ParseSmoothQuadBezierCurveto(bool aAbsCoords);
bool ParseEllipticalArc(bool aAbsCoords);
mozilla::SVGPathData * const mPathSegList;
};
class nsSVGArcConverter
{
@ -128,34 +80,4 @@ protected:
Point mFrom, mC;
};
class nsSVGPathDataParserToInternal : public nsSVGPathDataParser
{
public:
nsSVGPathDataParserToInternal(mozilla::SVGPathData *aList)
: mPathSegList(aList)
{}
nsresult Parse(const nsAString &aValue);
protected:
virtual nsresult StoreMoveTo(bool absCoords, float x, float y) MOZ_OVERRIDE;
virtual nsresult StoreClosePath() MOZ_OVERRIDE;
virtual nsresult StoreLineTo(bool absCoords, float x, float y) MOZ_OVERRIDE;
virtual nsresult StoreHLineTo(bool absCoords, float x) MOZ_OVERRIDE;
virtual nsresult StoreVLineTo(bool absCoords, float y) MOZ_OVERRIDE;
virtual nsresult StoreCurveTo(bool absCoords, float x, float y,
float x1, float y1, float x2, float y2) MOZ_OVERRIDE;
virtual nsresult StoreSmoothCurveTo(bool absCoords, float x, float y,
float x2, float y2) MOZ_OVERRIDE;
virtual nsresult StoreQuadCurveTo(bool absCoords, float x, float y,
float x1, float y1) MOZ_OVERRIDE;
virtual nsresult StoreSmoothQuadCurveTo(bool absCoords,
float x, float y) MOZ_OVERRIDE;
virtual nsresult StoreEllipticalArc(bool absCoords, float x, float y,
float r1, float r2, float angle,
bool largeArcFlag, bool sweepFlag) MOZ_OVERRIDE;
private:
mozilla::SVGPathData *mPathSegList;
};
#endif // __NS_SVGPATHDATAPARSER_H__