author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Tue, 29 Oct 2013 15:59:50 +0100 | |
changeset 152886 | e6428d3d7313c9a25ba7bef9b77296a33895a71c |
parent 152885 | 5c3c2060a7237d66f94a6a982196d14d1821f0e6 |
child 152887 | f31dc2d87066888dbde9b9ae7848ec8519266ba8 |
push id | 25562 |
push user | kwierso@gmail.com |
push date | Fri, 01 Nov 2013 01:00:51 +0000 |
treeherder | mozilla-central@4813677c42bf [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 929011 |
milestone | 28.0a1 |
backs out | cb6165af87e7cbcacc305b03d62f6f0a000d289f |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/svg/content/src/SVGContentUtils.cpp +++ b/content/svg/content/src/SVGContentUtils.cpp @@ -5,16 +5,17 @@ // Main header first: // This is also necessary to ensure our definition of M_SQRT1_2 is picked up #include "SVGContentUtils.h" // 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" #include "nsIScriptError.h" #include "nsLayoutUtils.h" #include "SVGAnimationElement.h" #include "SVGAnimatedPreserveAspectRatio.h" #include "nsContentUtils.h" @@ -384,152 +385,151 @@ static inline uint32_t DecimalDigitValue(PRUnichar aCh) { MOZ_ASSERT(IsDigit(aCh), "Digit expected"); return aCh - '0'; } template<class floatType> bool -SVGContentUtils::ParseNumber(RangedPtr<const PRUnichar>& aIter, - const RangedPtr<const PRUnichar>& aEnd, - floatType& aValue) +SVGContentUtils::ParseNumber(const nsAString& aString, + floatType& aValue, + nsAString& aLeftOver) { - if (aIter == aEnd) { + 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) { 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 == aEnd) { + if (iter == end) { return false; } } // Absolute value of the integer part of the mantissa. floatType intPart = floatType(0); bool gotDot = *iter == '.'; if (!gotDot) { if (!IsDigit(*iter)) { return false; } do { intPart = floatType(10) * intPart + DecimalDigitValue(*iter); ++iter; - } while (iter != aEnd && IsDigit(*iter)); + } while (iter != end && IsDigit(*iter)); - if (iter != aEnd) { + if (iter != end) { gotDot = *iter == '.'; } } // Fractional part of the mantissa. floatType fracPart = floatType(0); if (gotDot) { ++iter; - if (iter == aEnd || !IsDigit(*iter)) { + if (iter == end || !IsDigit(*iter)) { return false; } // Power of ten by which we need to divide our next digit floatType divisor = floatType(10); do { fracPart += DecimalDigitValue(*iter) / divisor; divisor *= 10; ++iter; - } while (iter != aEnd && IsDigit(*iter)); + } while (iter != end && IsDigit(*iter)); } bool gotE = false; int32_t exponent = 0; int32_t expSign; - if (iter != aEnd && (*iter == 'e' || *iter == 'E')) { + if (iter != end && (*iter == 'e' || *iter == 'E')) { - RangedPtr<const PRUnichar> expIter(iter); + mozilla::RangedPtr<const PRUnichar> expIter(iter); ++expIter; - if (expIter != aEnd) { + if (expIter != end) { expSign = *expIter == '-' ? -1 : 1; if (*expIter == '-' || *expIter == '+') { ++expIter; - if (expIter != aEnd && IsDigit(*expIter)) { + if (expIter != end && 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; } } } if (gotE) { iter = expIter; do { exponent = 10 * exponent + DecimalDigitValue(*iter); ++iter; - } while (iter != aEnd && IsDigit(*iter)); + } while (iter != end && IsDigit(*iter)); } } // Assemble the number - floatType value = sign * (intPart + fracPart); + aValue = sign * (intPart + fracPart); if (gotE) { - value *= pow(floatType(10), floatType(expSign * exponent)); + aValue *= pow(floatType(10), floatType(expSign * exponent)); } - if (!NS_finite(value)) { - return false; - } - aIter = iter; - aValue = value; - return true; + + aLeftOver = Substring(iter.get(), end.get()); + return NS_finite(aValue); } -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 bool +SVGContentUtils::ParseNumber<float>(const nsAString& aString, + float& aValue, + nsAString& aLeftOver); +template bool +SVGContentUtils::ParseNumber<double>(const nsAString& aString, + double& aValue, + nsAString& aLeftOver); template<class floatType> bool SVGContentUtils::ParseNumber(const nsAString& aString, floatType& aValue) { - RangedPtr<const PRUnichar> iter = GetStartRangedPtr(aString); - const RangedPtr<const PRUnichar> end = GetEndRangedPtr(aString); + nsAutoString leftOver; - return ParseNumber(iter, end, aValue) && iter == end; + if (!ParseNumber(aString, aValue, leftOver)) { + return false; + } + + return leftOver.IsEmpty(); } template bool SVGContentUtils::ParseNumber<float>(const nsAString& aString, float& aValue); template bool SVGContentUtils::ParseNumber<double>(const nsAString& aString, double& aValue); bool SVGContentUtils::ParseInteger(const nsAString& aString, int32_t& aValue) { - RangedPtr<const PRUnichar> iter = GetStartRangedPtr(aString); - const RangedPtr<const PRUnichar> end = GetEndRangedPtr(aString); + 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) { return false; } int32_t sign = *iter == '-' ? -1 : 1; if (*iter == '-' || *iter == '+') {
--- a/content/svg/content/src/SVGContentUtils.h +++ b/content/svg/content/src/SVGContentUtils.h @@ -6,17 +6,16 @@ #ifndef MOZILLA_SVGCONTENTUTILS_H #define MOZILLA_SVGCONTENTUTILS_H // include math.h to pick up definition of M_ maths defines e.g. M_PI #define _USE_MATH_DEFINES #include <math.h> #include "gfxMatrix.h" -#include "mozilla/RangedPtr.h" class nsIContent; class nsIDocument; class nsIFrame; class nsStyleContext; class nsSVGElement; namespace mozilla { @@ -128,34 +127,26 @@ public: const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio); static gfxMatrix GetViewBoxTransform(float aViewportWidth, float aViewportHeight, float aViewboxX, float aViewboxY, 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. - * If parsing succeeds, aIter is updated so that it points to the character - * after the end of the number, otherwise it is left unchanged + * Anything after the number is returned in aLeftOver. */ template<class floatType> static bool - ParseNumber(mozilla::RangedPtr<const PRUnichar>& aIter, - const mozilla::RangedPtr<const PRUnichar>& aEnd, - floatType& aValue); + ParseNumber(const nsAString& aString, floatType& aValue, + nsAString& aLeftOver); /** * Parse a number of the form: * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)? * Parsing fails if there is anything left over after the number, * or the number cannot be represented by a floatType. */ template<class floatType>
--- a/content/svg/content/src/SVGLength.cpp +++ b/content/svg/content/src/SVGLength.cpp @@ -29,30 +29,25 @@ SVGLength::GetValueAsString(nsAString &a aValue.Assign(buf); nsAutoString unitString; GetUnitString(unitString, mUnit); aValue.Append(unitString); } bool -SVGLength::SetValueFromString(const nsAString &aString) +SVGLength::SetValueFromString(const nsAString &aValueAsString) { - RangedPtr<const PRUnichar> iter = - SVGContentUtils::GetStartRangedPtr(aString); - const RangedPtr<const PRUnichar> end = - SVGContentUtils::GetEndRangedPtr(aString); - + nsAutoString units; float value; - if (!SVGContentUtils::ParseNumber(iter, end, value)) { + if (!SVGContentUtils::ParseNumber(aValueAsString, value, units)) { return false; } - const nsAString& units = Substring(iter.get(), end.get()); uint16_t unitType = GetUnitTypeForString(units); if (!IsValidUnitType(unitType)) { return false; } mValue = value; mUnit = uint8_t(unitType); return true; }
--- a/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp +++ b/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp @@ -236,23 +236,23 @@ SVGMotionSMILAnimationFunction:: void SVGMotionSMILAnimationFunction::RebuildPathAndVerticesFromPathAttr() { const nsAString& pathSpec = GetAttr(nsGkAtoms::path)->GetStringValue(); mPathSourceType = ePathSourceType_PathAttr; // Generate gfxPath from |path| attr SVGPathData path; - nsSVGPathDataParser pathParser(pathSpec, &path); + nsSVGPathDataParserToInternal pathParser(&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(); + pathParser.Parse(pathSpec); if (!path.Length()) { return; } mPath = path.ToPath(gfxMatrix()); bool ok = path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices); if (!ok || !mPathVertices.Length()) { mPath = nullptr;
--- a/content/svg/content/src/SVGPathData.cpp +++ b/content/svg/content/src/SVGPathData.cpp @@ -66,18 +66,18 @@ SVGPathData::GetValueAsString(nsAString& nsresult SVGPathData::SetValueFromString(const nsAString& aValue) { // We don't use a temp variable since the spec says to parse everything up to // the first error. We still return any error though so that callers know if // there's a problem. - nsSVGPathDataParser pathParser(aValue, this); - return pathParser.Parse() ? NS_OK : NS_ERROR_DOM_SYNTAX_ERR; + nsSVGPathDataParserToInternal pathParser(this); + return pathParser.Parse(aValue); } nsresult SVGPathData::AppendSeg(uint32_t aType, ...) { uint32_t oldLength = mData.Length(); uint32_t newLength = oldLength + 1 + SVGPathSegUtils::ArgCountForType(aType); if (!mData.SetLength(newLength)) {
--- a/content/svg/content/src/SVGPathData.h +++ b/content/svg/content/src/SVGPathData.h @@ -15,17 +15,17 @@ #include "mozilla/RefPtr.h" #include "nsSVGElement.h" #include "nsTArray.h" #include <string.h> class gfxContext; class gfxPath; -class nsSVGPathDataParser; // IWYU pragma: keep +class nsSVGPathDataParserToInternal; // IWYU pragma: keep struct gfxMatrix; struct nsSVGMark; namespace mozilla { /** * ATTENTION! WARNING! WATCH OUT!! @@ -73,18 +73,18 @@ namespace mozilla { * there are (if any), and thus where the current encoded segment ends, and * where the next segment (if any) begins. */ class SVGPathData { friend class SVGAnimatedPathSegList; friend class DOMSVGPathSegList; friend class DOMSVGPathSeg; - friend class ::nsSVGPathDataParser; - // nsSVGPathDataParser will not keep wrappers in sync, so consumers + friend class ::nsSVGPathDataParserToInternal; + // nsSVGPathDataParserToInternal will not keep wrappers in sync, so consumers // are responsible for that! typedef gfx::DrawTarget DrawTarget; typedef gfx::Path Path; typedef gfx::FillRule FillRule; typedef gfx::CapStyle CapStyle; public:
--- a/content/svg/content/src/SVGPointList.cpp +++ b/content/svg/content/src/SVGPointList.cpp @@ -55,40 +55,33 @@ SVGPointList::SetValueFromString(const n SVGPointList temp; nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> tokenizer(aValue, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); 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; - if (!SVGContentUtils::ParseNumber(iter, end, x)) { + nsAutoString leftOver; + if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), x, leftOver)) { rv = NS_ERROR_DOM_SYNTAX_ERR; break; } float y; - if (iter == end) { + if (leftOver.IsEmpty()) { if (!tokenizer.hasMoreTokens() || !SVGContentUtils::ParseNumber(tokenizer.nextToken(), y)) { rv = NS_ERROR_DOM_SYNTAX_ERR; break; } } 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; } } temp.AppendItem(SVGPoint(x, y)); } if (tokenizer.separatorAfterCurrentToken()) {
--- a/content/svg/content/src/SVGTransformList.cpp +++ b/content/svg/content/src/SVGTransformList.cpp @@ -65,18 +65,20 @@ SVGTransformList::GetValueAsString(nsASt aValue.Append(' '); } } } nsresult SVGTransformList::SetValueFromString(const nsAString& aValue) { - SVGTransformListParser parser(aValue); - if (!parser.Parse()) { + SVGTransformListParser parser; + nsresult rv = parser.Parse(aValue); + + if (NS_FAILED(rv)) { // there was a parse error. return NS_ERROR_DOM_SYNTAX_ERR; } return CopyFrom(parser.GetTransformList()); } } // namespace mozilla
--- a/content/svg/content/src/SVGTransformListParser.cpp +++ b/content/svg/content/src/SVGTransformListParser.cpp @@ -2,272 +2,345 @@ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : * This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ #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 -bool -SVGTransformListParser::Parse() +nsresult +SVGTransformListParser::Match() { mTransforms.Clear(); - return ParseTransforms(); + return MatchTransformList(); } -bool -SVGTransformListParser::ParseTransforms() + +nsresult +SVGTransformListParser::MatchTransformList() { - if (!SkipWsp()) { - return true; - } + MatchWsp(); - if (!ParseTransform()) { - return false; + if (IsTokenTransformStarter()) { + ENSURE_MATCHED(MatchTransforms()); } - while (SkipWsp()) { - // The SVG BNF allows multiple comma-wsp between transforms - while (*mIter == ',') { - ++mIter; - if (!SkipWsp()) { - return false; - } + 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 (!ParseTransform()) { - return false; - } - } - return true; -} - -bool -SVGTransformListParser::ParseTransform() -{ - RangedPtr<const PRUnichar> start(mIter); - while (IsAlpha(*mIter)) { - ++mIter; - if (mIter == mEnd) { - return false; + if (IsTokenTransformStarter()) { + ENSURE_MATCHED(MatchTransform()); + } else { + if (pos != mTokenPos) RewindTo(pos); + break; } } - if (start == mIter) { - // Didn't read anything - return false; + 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; } - const nsAString& transform = Substring(start.get(), mIter.get()); - nsIAtom* keyAtom = NS_GetStaticAtom(transform); + return rv; +} + + +nsresult +SVGTransformListParser::MatchTransform() +{ + nsCOMPtr<nsIAtom> keyatom; + + nsresult rv = GetTransformToken(getter_AddRefs(keyatom), true); + if (NS_FAILED(rv)) { + return rv; + } - if (!keyAtom || !SkipWsp()) { + 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() +{ + nsCOMPtr<nsIAtom> keyatom; + + nsresult rv = GetTransformToken(getter_AddRefs(keyatom), false); + if (NS_FAILED(rv)) { return false; } - if (keyAtom == nsGkAtoms::translate) { - return ParseTranslate(); - } - if (keyAtom == nsGkAtoms::scale) { - return ParseScale(); - } - if (keyAtom == nsGkAtoms::rotate) { - return ParseRotate(); + if (keyatom == nsGkAtoms::translate || + keyatom == nsGkAtoms::scale || + keyatom == nsGkAtoms::rotate || + keyatom == nsGkAtoms::skewX || + keyatom == nsGkAtoms::skewY || + keyatom == nsGkAtoms::matrix) { + return true; } - if (keyAtom == nsGkAtoms::skewX) { - return ParseSkewX(); - } - if (keyAtom == nsGkAtoms::skewY) { - return ParseSkewY(); - } - if (keyAtom == nsGkAtoms::matrix) { - return ParseMatrix(); - } + return false; } -bool -SVGTransformListParser::ParseArguments(float* aResult, - uint32_t aMaxCount, - uint32_t* aParsedCount) +nsresult +SVGTransformListParser::MatchNumberArguments(float *aResult, + uint32_t aMaxNum, + uint32_t *aParsedNum) { - if (*mIter != '(') { - return false; - } - ++mIter; + *aParsedNum = 0; + + MatchWsp(); + + ENSURE_MATCHED(MatchLeftParen()); + + MatchWsp(); + + ENSURE_MATCHED(MatchNumber(&aResult[0])); + *aParsedNum = 1; - if (!SkipWsp()) { - return false; + while (IsTokenCommaWspStarter()) { + MatchWsp(); + if (mTokenType == RIGHT_PAREN) { + break; + } + if (*aParsedNum == aMaxNum) { + return NS_ERROR_FAILURE; + } + if (IsTokenCommaWspStarter()) { + MatchCommaWsp(); + } + ENSURE_MATCHED(MatchNumber(&aResult[(*aParsedNum)++])); } - if (!SVGContentUtils::ParseNumber(mIter, mEnd, aResult[0])) { - return false; - } - *aParsedCount = 1; + MatchWsp(); - while (SkipWsp()) { - if (*mIter == ')') { - ++mIter; - return true; - } - if (*aParsedCount == aMaxCount) { - return false; - } - SkipCommaWsp(); - if (!SVGContentUtils::ParseNumber(mIter, mEnd, aResult[(*aParsedCount)++])) { - return false; - } - } - return false; + ENSURE_MATCHED(MatchRightParen()); + + return NS_OK; } -bool -SVGTransformListParser::ParseTranslate() +nsresult +SVGTransformListParser::MatchTranslate() { + GetNextToken(); + float t[2]; uint32_t count; - if (!ParseArguments(t, ArrayLength(t), &count)) { - return false; - } + ENSURE_MATCHED(MatchNumberArguments(t, ArrayLength(t), &count)); switch (count) { case 1: t[1] = 0.f; // fall-through case 2: { nsSVGTransform* transform = mTransforms.AppendElement(); - if (!transform) { - return false; - } + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); transform->SetTranslate(t[0], t[1]); - return true; + break; } + default: + return NS_ERROR_FAILURE; } - return false; + return NS_OK; } -bool -SVGTransformListParser::ParseScale() + +nsresult +SVGTransformListParser::MatchScale() { + GetNextToken(); + float s[2]; uint32_t count; - if (!ParseArguments(s, ArrayLength(s), &count)) { - return false; - } + ENSURE_MATCHED(MatchNumberArguments(s, ArrayLength(s), &count)); switch (count) { case 1: s[1] = s[0]; // fall-through case 2: { nsSVGTransform* transform = mTransforms.AppendElement(); - if (!transform) { - return false; - } + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); transform->SetScale(s[0], s[1]); - return true; + break; } + default: + return NS_ERROR_FAILURE; } - return false; + return NS_OK; } -bool -SVGTransformListParser::ParseRotate() +nsresult +SVGTransformListParser::MatchRotate() { + GetNextToken(); + float r[3]; uint32_t count; - if (!ParseArguments(r, ArrayLength(r), &count)) { - return false; - } + ENSURE_MATCHED(MatchNumberArguments(r, ArrayLength(r), &count)); switch (count) { case 1: r[1] = r[2] = 0.f; // fall-through case 3: { nsSVGTransform* transform = mTransforms.AppendElement(); - if (!transform) { - return false; - } + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); transform->SetRotate(r[0], r[1], r[2]); - return true; + break; } + default: + return NS_ERROR_FAILURE; } - return false; + return NS_OK; } -bool -SVGTransformListParser::ParseSkewX() + +nsresult +SVGTransformListParser::MatchSkewX() { + GetNextToken(); + float skew; uint32_t count; - if (!ParseArguments(&skew, 1, &count) || count != 1) { - return false; + ENSURE_MATCHED(MatchNumberArguments(&skew, 1, &count)); + + if (count != 1) { + return NS_ERROR_FAILURE; } nsSVGTransform* transform = mTransforms.AppendElement(); - if (!transform) { - return false; - } + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); transform->SetSkewX(skew); - return true; + return NS_OK; } -bool -SVGTransformListParser::ParseSkewY() + +nsresult +SVGTransformListParser::MatchSkewY() { + GetNextToken(); + float skew; uint32_t count; - if (!ParseArguments(&skew, 1, &count) || count != 1) { - return false; + ENSURE_MATCHED(MatchNumberArguments(&skew, 1, &count)); + + if (count != 1) { + return NS_ERROR_FAILURE; } nsSVGTransform* transform = mTransforms.AppendElement(); - if (!transform) { - return false; - } + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); transform->SetSkewY(skew); - return true; + return NS_OK; } -bool -SVGTransformListParser::ParseMatrix() + +nsresult +SVGTransformListParser::MatchMatrix() { + GetNextToken(); + float m[6]; uint32_t count; - if (!ParseArguments(m, ArrayLength(m), &count) || count != 6) { - return false; + ENSURE_MATCHED(MatchNumberArguments(m, ArrayLength(m), &count)); + + if (count != 6) { + return NS_ERROR_FAILURE; } nsSVGTransform* transform = mTransforms.AppendElement(); - if (!transform) { - return false; - } + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); transform->SetMatrix(gfxMatrix(m[0], m[1], m[2], m[3], m[4], m[5])); - return true; + return NS_OK; }
--- a/content/svg/content/src/SVGTransformListParser.h +++ b/content/svg/content/src/SVGTransformListParser.h @@ -7,52 +7,58 @@ #ifndef MOZILLA_SVGTRANSFORMLISTPARSER_H__ #define MOZILLA_SVGTRANSFORMLISTPARSER_H__ #include "mozilla/Attributes.h" #include "nsSVGDataParser.h" #include "nsTArray.h" //////////////////////////////////////////////////////////////////////// -// SVGTransformListParser: A simple recursive descent parser that builds -// transform lists from transform attributes. The grammar for path data +// SVGTransformListParser: taken from nsSVGPathDataParser, a simple +// recursive descent parser that builds the transform lists from the +// 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; 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 - bool ParseArguments(float *aResult, - uint32_t aMaxCount, - uint32_t *aParsedCount); + virtual nsresult Match() MOZ_OVERRIDE; - bool ParseTransforms(); + nsresult MatchNumberArguments(float *aResult, + uint32_t aMaxNum, + uint32_t *aParsedNum); - bool ParseTransform(); + nsresult MatchTransformList(); - bool ParseTranslate(); - bool ParseScale(); - bool ParseRotate(); - bool ParseSkewX(); - bool ParseSkewY(); - bool ParseMatrix(); + nsresult GetTransformToken(nsIAtom** aKeyatom, bool aAdvancePos); + nsresult MatchTransforms(); + + nsresult MatchTransform(); + + bool IsTokenTransformStarter(); - FallibleTArray<nsSVGTransform> mTransforms; + nsresult MatchTranslate(); + + nsresult MatchScale(); + nsresult MatchRotate(); + nsresult MatchSkewX(); + nsresult MatchSkewY(); + nsresult MatchMatrix(); }; } // namespace mozilla #endif // MOZILLA_SVGTRANSFORMLISTPARSER_H__
--- a/content/svg/content/src/nsSVGAngle.cpp +++ b/content/svg/content/src/nsSVGAngle.cpp @@ -89,30 +89,26 @@ GetValueString(nsAString &aValueAsString aValueAsString.Assign(buf); nsAutoString unitString; GetUnitString(unitString, aUnitType); aValueAsString.Append(unitString); } static bool -GetValueFromString(const nsAString& aString, +GetValueFromString(const nsAString& aValueAsString, float& aValue, uint16_t* aUnitType) { - RangedPtr<const PRUnichar> iter = - SVGContentUtils::GetStartRangedPtr(aString); - const RangedPtr<const PRUnichar> end = - SVGContentUtils::GetEndRangedPtr(aString); + nsAutoString units; - if (!SVGContentUtils::ParseNumber(iter, end, aValue)) { + if (!SVGContentUtils::ParseNumber(aValueAsString, aValue, units)) { return false; } - const nsAString& units = Substring(iter.get(), end.get()); *aUnitType = GetUnitTypeForString(units); return IsValidUnitType(*aUnitType); } /* static */ float nsSVGAngle::GetDegreesPerUnit(uint8_t aUnit) { switch (aUnit) {
--- a/content/svg/content/src/nsSVGDataParser.cpp +++ b/content/svg/content/src/nsSVGDataParser.cpp @@ -1,39 +1,300 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * 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 "SVGContentUtils.h" +#include "prdtoa.h" +#include "nsMathUtils.h" +#include "nsMemory.h" +#include "nsReadableUtils.h" +#include <stdlib.h> +#include <math.h> + +//---------------------------------------------------------------------- +// public interface + +nsresult +nsSVGDataParser::Parse(const nsAString &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 -nsSVGDataParser::nsSVGDataParser(const nsAString& aValue) - : mIter(SVGContentUtils::GetStartRangedPtr(aValue)), - mEnd(SVGContentUtils::GetEndRangedPtr(aValue)) +void nsSVGDataParser::GetNextToken() { + 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 (*mInputPos != '\0') { + ++mInputPos; + } +} + +void nsSVGDataParser::RewindTo(const char* aPos) +{ + 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); } -bool -nsSVGDataParser::SkipCommaWsp() + +//---------------------------------------------------------------------- + +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() { - if (!SkipWsp()) { - // end of string - return false; + 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()); } - if (*mIter != ',') { - return true; + + return NS_OK; +} + +//---------------------------------------------------------------------- + +nsresult nsSVGDataParser::MatchFractConst() +{ + if (mTokenType == POINT) { + GetNextToken(); + ENSURE_MATCHED(MatchDigitSeq()); } - ++mIter; - return SkipWsp(); + else { + ENSURE_MATCHED(MatchDigitSeq()); + if (mTokenType == POINT) { + GetNextToken(); + if (IsTokenDigitSeqStarter()) { + ENSURE_MATCHED(MatchDigitSeq()); + } + } + } + return NS_OK; } -bool -nsSVGDataParser::SkipWsp() +//---------------------------------------------------------------------- + +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() { - while (mIter != mEnd) { - if (!IsSVGWhitespace(*mIter)) { - return true; - } - ++mIter; + 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 false; + + + return NS_OK; } + +nsresult nsSVGDataParser::MatchRightParen() +{ + switch (mTokenType) { + case RIGHT_PAREN: + GetNextToken(); + break; + default: + return NS_ERROR_FAILURE; + } + + return NS_OK; +} +
--- a/content/svg/content/src/nsSVGDataParser.h +++ b/content/svg/content/src/nsSVGDataParser.h @@ -1,38 +1,63 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ #ifndef __NS_SVGDATAPARSER_H__ #define __NS_SVGDATAPARSER_H__ -#include "mozilla/RangedPtr.h" +#include "nsError.h" #include "nsString.h" +//---------------------------------------------------------------------- +// helper macros +#define ENSURE_MATCHED(exp) { nsresult rv = exp; if (NS_FAILED(rv)) return rv; } + //////////////////////////////////////////////////////////////////////// -// nsSVGDataParser: a simple base class for parsing values +// nsSVGDataParser: a simple abstract class for parsing values // for path and transform values. // class nsSVGDataParser { public: - nsSVGDataParser(const nsAString& aValue); + nsresult Parse(const nsAString &aValue); protected: - static bool IsAlpha(PRUnichar aCh) { - // Exclude non-ascii characters before calling isalpha - return (aCh & 0x7f) == aCh && isalpha(aCh); - } + const char* mInputPos; + + const char* mTokenPos; + enum { DIGIT, WSP, COMMA, POINT, SIGN, LEFT_PAREN, RIGHT_PAREN, OTHER, END } mTokenType; + char mTokenVal; + + // 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(); - - mozilla::RangedPtr<const PRUnichar> mIter; - const mozilla::RangedPtr<const PRUnichar> mEnd; + nsresult MatchLeftParen(); + nsresult MatchRightParen(); }; #endif // __NS_SVGDATAPARSER_H__
--- a/content/svg/content/src/nsSVGLength2.cpp +++ b/content/svg/content/src/nsSVGLength2.cpp @@ -117,29 +117,24 @@ GetValueString(nsAString &aValueAsString aValueAsString.Assign(buf); nsAutoString unitString; GetUnitString(unitString, aUnitType); aValueAsString.Append(unitString); } static bool -GetValueFromString(const nsAString& aString, +GetValueFromString(const nsAString& aValueAsString, float& aValue, uint16_t* aUnitType) { - RangedPtr<const PRUnichar> iter = - SVGContentUtils::GetStartRangedPtr(aString); - const RangedPtr<const PRUnichar> end = - SVGContentUtils::GetEndRangedPtr(aString); - - if (!SVGContentUtils::ParseNumber(iter, end, aValue)) { + nsAutoString units; + if (!SVGContentUtils::ParseNumber(aValueAsString, aValue, units)) { return false; } - const nsAString& units = Substring(iter.get(), end.get()); *aUnitType = GetUnitTypeForString(units); return IsValidUnitType(*aUnitType); } static float FixAxisLength(float aLength) { if (aLength == 0.0f) {
--- a/content/svg/content/src/nsSVGNumber2.cpp +++ b/content/svg/content/src/nsSVGNumber2.cpp @@ -44,38 +44,32 @@ NS_INTERFACE_MAP_BEGIN(DOMSVGNumber) NS_INTERFACE_MAP_END /* Implementation */ static nsSVGAttrTearoffTable<nsSVGNumber2, nsSVGNumber2::DOMAnimatedNumber> sSVGAnimatedNumberTearoffTable; static bool -GetValueFromString(const nsAString& aString, +GetValueFromString(const nsAString& aValueAsString, bool aPercentagesAllowed, float& aValue) { - RangedPtr<const PRUnichar> iter = - SVGContentUtils::GetStartRangedPtr(aString); - const RangedPtr<const PRUnichar> end = - SVGContentUtils::GetEndRangedPtr(aString); + nsAutoString units; - if (!SVGContentUtils::ParseNumber(iter, end, aValue)) { + if (!SVGContentUtils::ParseNumber(aValueAsString, aValue, units)) { return false; } - if (aPercentagesAllowed) { - const nsAString& units = Substring(iter.get(), end.get()); - if (units.EqualsLiteral("%")) { - aValue /= 100; - return true; - } + if (aPercentagesAllowed && units.EqualsLiteral("%")) { + aValue /= 100; + return true; } - return iter == end; + return units.IsEmpty(); } nsresult nsSVGNumber2::SetBaseValueString(const nsAString &aValueAsString, nsSVGElement *aSVGElement) { float val;
--- a/content/svg/content/src/nsSVGPathDataParser.cpp +++ b/content/svg/content/src/nsSVGPathDataParser.cpp @@ -2,402 +2,838 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ #include "nsSVGPathDataParser.h" #include "mozilla/gfx/Point.h" #include "nsSVGDataParser.h" -#include "SVGContentUtils.h" #include "SVGPathData.h" #include "SVGPathSegUtils.h" +#include <stdlib.h> +#include <math.h> using namespace mozilla; using namespace mozilla::gfx; -static inline PRUnichar ToUpper(PRUnichar aCh) +nsresult nsSVGPathDataParser::Match() +{ + return MatchSvgPath(); +} + +//---------------------------------------------------------------------- + +nsresult nsSVGPathDataParser::MatchCoordPair(float* aX, float* aY) { - return aCh >= 'a' && aCh <= 'z' ? aCh - 'a' + 'A' : aCh; + ENSURE_MATCHED(MatchCoord(aX)); + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); + } + + ENSURE_MATCHED(MatchCoord(aY)); + + return NS_OK; +} + +bool nsSVGPathDataParser::IsTokenCoordPairStarter() +{ + return IsTokenCoordStarter(); } -bool -nsSVGPathDataParser::Parse() +//---------------------------------------------------------------------- + +nsresult nsSVGPathDataParser::MatchCoord(float* aX) +{ + ENSURE_MATCHED(MatchNumber(aX)); + + return NS_OK; +} + +bool nsSVGPathDataParser::IsTokenCoordStarter() { - mPathSegList->Clear(); - return ParsePath(); + return IsTokenNumberStarter(); +} + +//---------------------------------------------------------------------- + +nsresult nsSVGPathDataParser::MatchFlag(bool* f) +{ + switch (mTokenVal) { + case '0': + *f = false; + break; + case '1': + *f = true; + break; + default: + return NS_ERROR_FAILURE; + } + GetNextToken(); + return NS_OK; } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseCoordPair(float& aX, float& aY) +nsresult nsSVGPathDataParser::MatchSvgPath() { - return SVGContentUtils::ParseNumber(mIter, mEnd, aX) && - SkipCommaWsp() && - SVGContentUtils::ParseNumber(mIter, mEnd, aY); + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + if (IsTokenSubPathsStarter()) { + ENSURE_MATCHED(MatchSubPaths()); + } + + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + return NS_OK; } -bool -nsSVGPathDataParser::ParseFlag(bool& aFlag) +//---------------------------------------------------------------------- + +nsresult nsSVGPathDataParser::MatchSubPaths() { - if (mIter == mEnd || (*mIter != '0' && *mIter != '1')) { - return false; + ENSURE_MATCHED(MatchSubPath()); + + while (1) { + const char* pos = mTokenPos; + + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + if (IsTokenSubPathStarter()) { + ENSURE_MATCHED(MatchSubPath()); + } + else { + if (pos != mTokenPos) RewindTo(pos); + break; + } } - aFlag = (*mIter == '1'); + + return NS_OK; +} - ++mIter; - return true; +bool nsSVGPathDataParser::IsTokenSubPathsStarter() +{ + return IsTokenSubPathStarter(); } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParsePath() +nsresult nsSVGPathDataParser::MatchSubPath() +{ + ENSURE_MATCHED(MatchMoveto()); + + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + if (IsTokenSubPathElementsStarter()) + ENSURE_MATCHED(MatchSubPathElements()); + + return NS_OK; +} + +bool nsSVGPathDataParser::IsTokenSubPathStarter() { - while (SkipWsp()) { - if (!ParseSubPath()) { - return false; + return (tolower(mTokenVal) == 'm'); +} + +//---------------------------------------------------------------------- + +nsresult nsSVGPathDataParser::MatchSubPathElements() +{ + ENSURE_MATCHED(MatchSubPathElement()); + + while (1) { + const char* pos = mTokenPos; + + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + + if (IsTokenSubPathElementStarter()) { + ENSURE_MATCHED(MatchSubPathElement()); + } + else { + if (pos != mTokenPos) RewindTo(pos); + return NS_OK; } } + + return NS_OK; +} - return true; +bool nsSVGPathDataParser::IsTokenSubPathElementsStarter() +{ + return IsTokenSubPathElementStarter(); } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseSubPath() -{ - return ParseMoveto() && ParseSubPathElements(); -} - -bool -nsSVGPathDataParser::ParseSubPathElements() +nsresult nsSVGPathDataParser::MatchSubPathElement() { - while (SkipWsp() && !IsStartOfSubPath()) { - PRUnichar commandType = ToUpper(*mIter); - - // Upper case commands have absolute co-ordinates, - // lower case commands have relative co-ordinates. - bool absCoords = commandType == *mIter; - - ++mIter; - SkipWsp(); - - if (!ParseSubPathElement(commandType, absCoords)) { - return false; - } + switch (tolower(mTokenVal)) { + case 'z': + ENSURE_MATCHED(MatchClosePath()); + break; + case 'l': + ENSURE_MATCHED(MatchLineto()); + break; + case 'h': + ENSURE_MATCHED(MatchHorizontalLineto()); + break; + case 'v': + ENSURE_MATCHED(MatchVerticalLineto()); + break; + case 'c': + ENSURE_MATCHED(MatchCurveto()); + break; + case 's': + ENSURE_MATCHED(MatchSmoothCurveto()); + break; + case 'q': + ENSURE_MATCHED(MatchQuadBezierCurveto()); + break; + case 't': + ENSURE_MATCHED(MatchSmoothQuadBezierCurveto()); + break; + case 'a': + ENSURE_MATCHED(MatchEllipticalArc()); + break; + default: + return NS_ERROR_FAILURE; + break; } - return true; + return NS_OK; } -bool -nsSVGPathDataParser::ParseSubPathElement(PRUnichar aCommandType, - bool aAbsCoords) +bool nsSVGPathDataParser::IsTokenSubPathElementStarter() { - switch (aCommandType) { - case 'Z': - return ParseClosePath(); - case 'L': - return ParseLineto(aAbsCoords); - case 'H': - return ParseHorizontalLineto(aAbsCoords); - case 'V': - return ParseVerticalLineto(aAbsCoords); - case 'C': - return ParseCurveto(aAbsCoords); - case 'S': - return ParseSmoothCurveto(aAbsCoords); - case 'Q': - return ParseQuadBezierCurveto(aAbsCoords); - case 'T': - return ParseSmoothQuadBezierCurveto(aAbsCoords); - case 'A': - return ParseEllipticalArc(aAbsCoords); + switch (tolower(mTokenVal)) { + case 'z': case 'l': case 'h': case 'v': case 'c': + case 's': case 'q': case 't': case 'a': + return true; + break; + default: + return false; + break; } return false; +} + +//---------------------------------------------------------------------- + +nsresult nsSVGPathDataParser::MatchMoveto() +{ + bool absCoords; + + switch (mTokenVal) { + case 'M': + absCoords = true; + break; + case 'm': + absCoords = false; + break; + default: + return NS_ERROR_FAILURE; + } + + GetNextToken(); + + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + ENSURE_MATCHED(MatchMovetoArgSeq(absCoords)); + + return NS_OK; } -bool -nsSVGPathDataParser::IsStartOfSubPath() const +nsresult nsSVGPathDataParser::MatchMovetoArgSeq(bool absCoords) { - return *mIter == 'm' || *mIter == 'M'; + + float x, y; + ENSURE_MATCHED(MatchCoordPair(&x, &y)); + + nsresult rv = StoreMoveTo(absCoords, x, y); + NS_ENSURE_SUCCESS(rv, rv); + + const char* pos = mTokenPos; + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); + } + + if (IsTokenLinetoArgSeqStarter()) { + ENSURE_MATCHED(MatchLinetoArgSeq(absCoords)); + } + else { + if (pos != mTokenPos) RewindTo(pos); + } + + return NS_OK; } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseMoveto() +nsresult nsSVGPathDataParser::MatchClosePath() { - if (!IsStartOfSubPath()) { - return false; + switch (mTokenVal) { + case 'Z': + case 'z': + GetNextToken(); + break; + default: + return NS_ERROR_FAILURE; + } + + return StoreClosePath(); +} + +//---------------------------------------------------------------------- + +nsresult nsSVGPathDataParser::MatchLineto() +{ + bool absCoords; + + switch (mTokenVal) { + case 'L': + absCoords = true; + break; + case 'l': + absCoords = false; + break; + default: + return NS_ERROR_FAILURE; } - bool absCoords = (*mIter == 'M'); + GetNextToken(); - ++mIter; - SkipWsp(); - - float x, y; - if (!ParseCoordPair(x, y)) { - return false; + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); } - if (NS_FAILED(mPathSegList->AppendSeg( - absCoords ? PATHSEG_MOVETO_ABS : PATHSEG_MOVETO_REL, - x, y))) { - return false; - } + ENSURE_MATCHED(MatchLinetoArgSeq(absCoords)); + + return NS_OK; +} + +nsresult nsSVGPathDataParser::MatchLinetoArgSeq(bool absCoords) +{ + while(1) { + float x, y; + ENSURE_MATCHED(MatchCoordPair(&x, &y)); + + nsresult rv = StoreLineTo(absCoords, x, y); + NS_ENSURE_SUCCESS(rv, rv); + + const char* pos = mTokenPos; - if (!SkipWsp() || IsAlpha(*mIter)) { - // End of data, or start of a new command - return true; - } + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); + } - // Per SVG 1.1 Section 8.3.2 - // If a moveto is followed by multiple pairs of coordinates, - // the subsequent pairs are treated as implicit lineto commands - return ParseLineto(absCoords); + if (!IsTokenCoordPairStarter()) { + if (pos != mTokenPos) RewindTo(pos); + return NS_OK; + } + } + + return NS_OK; +} + +bool nsSVGPathDataParser::IsTokenLinetoArgSeqStarter() +{ + return IsTokenCoordPairStarter(); } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseClosePath() +nsresult nsSVGPathDataParser::MatchHorizontalLineto() { - return NS_SUCCEEDED(mPathSegList->AppendSeg(PATHSEG_CLOSEPATH)); -} + bool absCoords; + + switch (mTokenVal) { + case 'H': + absCoords = true; + break; + case 'h': + absCoords = false; + break; + default: + return NS_ERROR_FAILURE; + } -//---------------------------------------------------------------------- + GetNextToken(); -bool -nsSVGPathDataParser::ParseLineto(bool aAbsCoords) + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + ENSURE_MATCHED(MatchHorizontalLinetoArgSeq(absCoords)); + + return NS_OK; +} + +nsresult nsSVGPathDataParser::MatchHorizontalLinetoArgSeq(bool absCoords) { - while (true) { - float x, y; - if (!ParseCoordPair(x, y)) { - return false; + while(1) { + float x; + ENSURE_MATCHED(MatchCoord(&x)); + + nsresult rv = StoreHLineTo(absCoords, x); + NS_ENSURE_SUCCESS(rv, rv); + + const char* pos = mTokenPos; + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } - if (NS_FAILED(mPathSegList->AppendSeg( - aAbsCoords ? PATHSEG_LINETO_ABS : PATHSEG_LINETO_REL, - x, y))) { - return false; + if (!IsTokenCoordStarter()) { + if (pos != mTokenPos) RewindTo(pos); + return NS_OK; } - - if (!SkipWsp() || IsAlpha(*mIter)) { - // End of data, or start of a new command - return true; - } - SkipCommaWsp(); } + + return NS_OK; } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseHorizontalLineto(bool aAbsCoords) +nsresult nsSVGPathDataParser::MatchVerticalLineto() { - while (true) { - float x; - if (!SVGContentUtils::ParseNumber(mIter, mEnd, x)) { - return false; + bool absCoords; + + switch (mTokenVal) { + case 'V': + absCoords = true; + break; + case 'v': + absCoords = false; + break; + default: + return NS_ERROR_FAILURE; + } + + GetNextToken(); + + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + ENSURE_MATCHED(MatchVerticalLinetoArgSeq(absCoords)); + + return NS_OK; +} + +nsresult nsSVGPathDataParser::MatchVerticalLinetoArgSeq(bool absCoords) +{ + while(1) { + float y; + ENSURE_MATCHED(MatchCoord(&y)); + + nsresult rv = StoreVLineTo(absCoords, y); + NS_ENSURE_SUCCESS(rv, rv); + + const char* pos = mTokenPos; + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } - if (NS_FAILED(mPathSegList->AppendSeg( - aAbsCoords ? PATHSEG_LINETO_HORIZONTAL_ABS : PATHSEG_LINETO_HORIZONTAL_REL, - x))) { - return false; + if (!IsTokenCoordStarter()) { + if (pos != mTokenPos) RewindTo(pos); + return NS_OK; } - - if (!SkipWsp() || IsAlpha(*mIter)) { - // End of data, or start of a new command - return true; - } - SkipCommaWsp(); } + + return NS_OK; } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseVerticalLineto(bool aAbsCoords) +nsresult nsSVGPathDataParser::MatchCurveto() { - while (true) { - float y; - if (!SVGContentUtils::ParseNumber(mIter, mEnd, y)) { - return false; + bool absCoords; + + switch (mTokenVal) { + case 'C': + absCoords = true; + break; + case 'c': + absCoords = false; + break; + default: + return NS_ERROR_FAILURE; + } + + GetNextToken(); + + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + ENSURE_MATCHED(MatchCurvetoArgSeq(absCoords)); + + return NS_OK; +} + + +nsresult nsSVGPathDataParser::MatchCurvetoArgSeq(bool absCoords) +{ + while(1) { + float x, y, x1, y1, x2, y2; + ENSURE_MATCHED(MatchCurvetoArg(&x, &y, &x1, &y1, &x2, &y2)); + + nsresult rv = StoreCurveTo(absCoords, x, y, x1, y1, x2, y2); + NS_ENSURE_SUCCESS(rv, rv); + + const char* pos = mTokenPos; + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } - if (NS_FAILED(mPathSegList->AppendSeg( - aAbsCoords ? PATHSEG_LINETO_VERTICAL_ABS : PATHSEG_LINETO_VERTICAL_REL, - y))) { - return false; + if (!IsTokenCurvetoArgStarter()) { + if (pos != mTokenPos) RewindTo(pos); + return NS_OK; } + } + + return NS_OK; +} + +nsresult +nsSVGPathDataParser::MatchCurvetoArg(float* x, float* y, float* x1, + float* y1, float* x2, float* y2) +{ + ENSURE_MATCHED(MatchCoordPair(x1, y1)); - if (!SkipWsp() || IsAlpha(*mIter)) { - // End of data, or start of a new command - return true; - } - SkipCommaWsp(); + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); + } + + ENSURE_MATCHED(MatchCoordPair(x2, y2)); + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } + + ENSURE_MATCHED(MatchCoordPair(x, y)); + + return NS_OK; +} + +bool nsSVGPathDataParser::IsTokenCurvetoArgStarter() +{ + return IsTokenCoordPairStarter(); } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseCurveto(bool aAbsCoords) +nsresult nsSVGPathDataParser::MatchSmoothCurveto() { - while (true) { - float x1, y1, x2, y2, x, y; + bool absCoords; + + switch (mTokenVal) { + case 'S': + absCoords = true; + break; + case 's': + absCoords = false; + break; + default: + return NS_ERROR_FAILURE; + } + + GetNextToken(); - if (!(ParseCoordPair(x1, y1) && - SkipCommaWsp() && - ParseCoordPair(x2, y2) && - SkipCommaWsp() && - ParseCoordPair(x, y))) { - return false; + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + ENSURE_MATCHED(MatchSmoothCurvetoArgSeq(absCoords)); + + return NS_OK; +} + +nsresult nsSVGPathDataParser::MatchSmoothCurvetoArgSeq(bool absCoords) +{ + while(1) { + float x, y, x2, y2; + ENSURE_MATCHED(MatchSmoothCurvetoArg(&x, &y, &x2, &y2)); + + nsresult rv = StoreSmoothCurveTo(absCoords, x, y, x2, y2); + NS_ENSURE_SUCCESS(rv, rv); + + const char* pos = mTokenPos; + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } - if (NS_FAILED(mPathSegList->AppendSeg( - aAbsCoords ? PATHSEG_CURVETO_CUBIC_ABS : PATHSEG_CURVETO_CUBIC_REL, - x1, y1, x2, y2, x, y))) { - return false; + if (!IsTokenSmoothCurvetoArgStarter()) { + if (pos != mTokenPos) RewindTo(pos); + return NS_OK; } + } + + return NS_OK; +} + +nsresult nsSVGPathDataParser::MatchSmoothCurvetoArg(float* x, float* y, float* x2, float* y2) +{ + ENSURE_MATCHED(MatchCoordPair(x2, y2)); - if (!SkipWsp() || IsAlpha(*mIter)) { - // End of data, or start of a new command - return true; - } - SkipCommaWsp(); + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } + + ENSURE_MATCHED(MatchCoordPair(x, y)); + + return NS_OK; +} + +bool nsSVGPathDataParser::IsTokenSmoothCurvetoArgStarter() +{ + return IsTokenCoordPairStarter(); } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseSmoothCurveto(bool aAbsCoords) +nsresult nsSVGPathDataParser::MatchQuadBezierCurveto() { - while (true) { - float x2, y2, x, y; - if (!(ParseCoordPair(x2, y2) && - SkipCommaWsp() && - ParseCoordPair(x, y))) { - return false; - } + bool absCoords; + + switch (mTokenVal) { + case 'Q': + absCoords = true; + break; + case 'q': + absCoords = false; + break; + default: + return NS_ERROR_FAILURE; + } - if (NS_FAILED(mPathSegList->AppendSeg( - aAbsCoords ? PATHSEG_CURVETO_CUBIC_SMOOTH_ABS : PATHSEG_CURVETO_CUBIC_SMOOTH_REL, - x2, y2, x, y))) { - return false; - } + GetNextToken(); - if (!SkipWsp() || IsAlpha(*mIter)) { - // End of data, or start of a new command - return true; - } - SkipCommaWsp(); + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); } + + ENSURE_MATCHED(MatchQuadBezierCurvetoArgSeq(absCoords)); + + return NS_OK; } -//---------------------------------------------------------------------- - -bool -nsSVGPathDataParser::ParseQuadBezierCurveto(bool aAbsCoords) +nsresult nsSVGPathDataParser::MatchQuadBezierCurvetoArgSeq(bool absCoords) { - while (true) { - float x1, y1, x, y; - if (!(ParseCoordPair(x1, y1) && - SkipCommaWsp() && - ParseCoordPair(x, y))) { - return false; + while(1) { + float x, y, x1, y1; + ENSURE_MATCHED(MatchQuadBezierCurvetoArg(&x, &y, &x1, &y1)); + + nsresult rv = StoreQuadCurveTo(absCoords, x, y, x1, y1); + NS_ENSURE_SUCCESS(rv, rv); + + const char* pos = mTokenPos; + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } - if (NS_FAILED(mPathSegList->AppendSeg( - aAbsCoords ? PATHSEG_CURVETO_QUADRATIC_ABS : PATHSEG_CURVETO_QUADRATIC_REL, - x1, y1, x, y))) { - return false; + if (!IsTokenQuadBezierCurvetoArgStarter()) { + if (pos != mTokenPos) RewindTo(pos); + return NS_OK; } + } + + return NS_OK; +} + +nsresult nsSVGPathDataParser::MatchQuadBezierCurvetoArg(float* x, float* y, float* x1, float* y1) +{ + ENSURE_MATCHED(MatchCoordPair(x1, y1)); - if (!SkipWsp() || IsAlpha(*mIter)) { - // Start of a new command - return true; - } - SkipCommaWsp(); + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } + + ENSURE_MATCHED(MatchCoordPair(x, y)); + + return NS_OK; +} + +bool nsSVGPathDataParser::IsTokenQuadBezierCurvetoArgStarter() +{ + return IsTokenCoordPairStarter(); } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseSmoothQuadBezierCurveto(bool aAbsCoords) +nsresult nsSVGPathDataParser::MatchSmoothQuadBezierCurveto() { - while (true) { + bool absCoords; + + switch (mTokenVal) { + case 'T': + absCoords = true; + break; + case 't': + absCoords = false; + break; + default: + return NS_ERROR_FAILURE; + } + + GetNextToken(); + + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } + + ENSURE_MATCHED(MatchSmoothQuadBezierCurvetoArgSeq(absCoords)); + + return NS_OK; +} + +nsresult nsSVGPathDataParser::MatchSmoothQuadBezierCurvetoArgSeq(bool absCoords) +{ + while(1) { float x, y; - if (!ParseCoordPair(x, y)) { - return false; + ENSURE_MATCHED(MatchCoordPair(&x, &y)); + + nsresult rv = StoreSmoothQuadCurveTo(absCoords, x, y); + NS_ENSURE_SUCCESS(rv, rv); + + const char* pos = mTokenPos; + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } - if (NS_FAILED(mPathSegList->AppendSeg( - aAbsCoords ? PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS : PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, - x, y))) { - return false; + if (!IsTokenCoordPairStarter()) { + if (pos != mTokenPos) RewindTo(pos); + return NS_OK; } - - if (!SkipWsp() || IsAlpha(*mIter)) { - // End of data, or start of a new command - return true; - } - SkipCommaWsp(); } + + return NS_OK; } //---------------------------------------------------------------------- -bool -nsSVGPathDataParser::ParseEllipticalArc(bool aAbsCoords) +nsresult nsSVGPathDataParser::MatchEllipticalArc() { - while (true) { - float r1, r2, angle, x, y; - bool largeArcFlag, sweepFlag; + bool absCoords; + + switch (mTokenVal) { + case 'A': + absCoords = true; + break; + case 'a': + absCoords = false; + break; + default: + return NS_ERROR_FAILURE; + } + + GetNextToken(); + + while (IsTokenWspStarter()) { + ENSURE_MATCHED(MatchWsp()); + } - if (!(SVGContentUtils::ParseNumber(mIter, mEnd, r1) && - SkipCommaWsp() && - SVGContentUtils::ParseNumber(mIter, mEnd, r2) && - SkipCommaWsp() && - SVGContentUtils::ParseNumber(mIter, mEnd, angle)&& - SkipCommaWsp() && - ParseFlag(largeArcFlag) && - SkipCommaWsp() && - ParseFlag(sweepFlag) && - SkipCommaWsp() && - ParseCoordPair(x, y))) { - return false; + ENSURE_MATCHED(MatchEllipticalArcArgSeq(absCoords)); + + return NS_OK; +} + + +nsresult nsSVGPathDataParser::MatchEllipticalArcArgSeq(bool absCoords) +{ + while(1) { + float x, y, r1, r2, angle; + bool largeArcFlag, sweepFlag; + + ENSURE_MATCHED(MatchEllipticalArcArg(&x, &y, &r1, &r2, &angle, &largeArcFlag, &sweepFlag)); + + nsresult rv = StoreEllipticalArc(absCoords, x, y, r1, r2, angle, + largeArcFlag, sweepFlag); + NS_ENSURE_SUCCESS(rv, rv); + + const char* pos = mTokenPos; + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } - // We can only pass floats after 'type', and per the SVG spec for arc, - // non-zero args are treated at 'true'. - if (NS_FAILED(mPathSegList->AppendSeg( - aAbsCoords ? PATHSEG_ARC_ABS : PATHSEG_ARC_REL, - r1, r2, angle, - largeArcFlag ? 1.0f : 0.0f, - sweepFlag ? 1.0f : 0.0f, - x, y))) { - return false; + if (!IsTokenEllipticalArcArgStarter()) { + if (pos != mTokenPos) RewindTo(pos); + return NS_OK; } + } + + return NS_OK; +} - if (!SkipWsp() || IsAlpha(*mIter)) { - // End of data, or start of a new command - return true; - } - SkipCommaWsp(); +nsresult nsSVGPathDataParser::MatchEllipticalArcArg(float* x, float* y, + float* r1, float* r2, float* angle, + bool* largeArcFlag, bool* sweepFlag) +{ + ENSURE_MATCHED(MatchNumber(r1)); + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); + } + + ENSURE_MATCHED(MatchNumber(r2)); + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); + } + + ENSURE_MATCHED(MatchNumber(angle)); + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); } + + ENSURE_MATCHED(MatchFlag(largeArcFlag)); + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); + } + + ENSURE_MATCHED(MatchFlag(sweepFlag)); + + if (IsTokenCommaWspStarter()) { + ENSURE_MATCHED(MatchCommaWsp()); + } + + ENSURE_MATCHED(MatchCoordPair(x, y)); + + return NS_OK; + } +bool nsSVGPathDataParser::IsTokenEllipticalArcArgStarter() +{ + return IsTokenNumberStarter(); +} + + //----------------------------------------------------------------------- static double CalcVectorAngle(double ux, double uy, double vx, double vy) { @@ -506,8 +942,102 @@ nsSVGArcConverter::GetNextSegment(Point* // do next segment mTheta = theta2; mFrom = *to; ++mSegIndex; return true; } + + +// --------------------------------------------------------------- +// nsSVGPathDataParserToInternal + +nsresult +nsSVGPathDataParserToInternal::Parse(const nsAString &aValue) +{ + mPathSegList->Clear(); + return nsSVGPathDataParser::Parse(aValue); +} + +nsresult +nsSVGPathDataParserToInternal::StoreMoveTo(bool absCoords, float x, float y) +{ + return mPathSegList->AppendSeg(absCoords ? PATHSEG_MOVETO_ABS : PATHSEG_MOVETO_REL, x, y); +} + +nsresult +nsSVGPathDataParserToInternal::StoreClosePath() +{ + return mPathSegList->AppendSeg(PATHSEG_CLOSEPATH); +} + +nsresult +nsSVGPathDataParserToInternal::StoreLineTo(bool absCoords, float x, float y) +{ + return mPathSegList->AppendSeg(absCoords ? PATHSEG_LINETO_ABS : PATHSEG_LINETO_REL, x, y); +} + +nsresult +nsSVGPathDataParserToInternal::StoreHLineTo(bool absCoords, float x) +{ + return mPathSegList->AppendSeg(absCoords ? PATHSEG_LINETO_HORIZONTAL_ABS : PATHSEG_LINETO_HORIZONTAL_REL, x); +} + +nsresult +nsSVGPathDataParserToInternal::StoreVLineTo(bool absCoords, float y) +{ + return mPathSegList->AppendSeg(absCoords ? PATHSEG_LINETO_VERTICAL_ABS : PATHSEG_LINETO_VERTICAL_REL, y); +} + +nsresult +nsSVGPathDataParserToInternal::StoreCurveTo(bool absCoords, + float x, float y, + float x1, float y1, + float x2, float y2) +{ + return mPathSegList->AppendSeg(absCoords ? PATHSEG_CURVETO_CUBIC_ABS : PATHSEG_CURVETO_CUBIC_REL, + x1, y1, x2, y2, x, y); +} + +nsresult +nsSVGPathDataParserToInternal::StoreSmoothCurveTo(bool absCoords, + float x, float y, + float x2, float y2) +{ + return mPathSegList->AppendSeg(absCoords ? PATHSEG_CURVETO_CUBIC_SMOOTH_ABS : PATHSEG_CURVETO_CUBIC_SMOOTH_REL, + x2, y2, x, y); +} + +nsresult +nsSVGPathDataParserToInternal::StoreQuadCurveTo(bool absCoords, + float x, float y, + float x1, float y1) +{ + return mPathSegList->AppendSeg(absCoords ? PATHSEG_CURVETO_QUADRATIC_ABS : PATHSEG_CURVETO_QUADRATIC_REL, + x1, y1, x, y); +} + +nsresult +nsSVGPathDataParserToInternal::StoreSmoothQuadCurveTo(bool absCoords, + float x, float y) +{ + return mPathSegList->AppendSeg(absCoords ? PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS : PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, x, y); +} + +nsresult +nsSVGPathDataParserToInternal::StoreEllipticalArc(bool absCoords, + float x, float y, + float r1, float r2, + float angle, + bool largeArcFlag, + bool sweepFlag) +{ + // We can only pass floats after 'type', and per the SVG spec for arc, + // non-zero args are treated at 'true'. + return mPathSegList->AppendSeg(absCoords ? PATHSEG_ARC_ABS : PATHSEG_ARC_REL, + r1, r2, angle, + largeArcFlag ? 1.0f : 0.0f, + sweepFlag ? 1.0f : 0.0f, + x, y); +} +
--- a/content/svg/content/src/nsSVGPathDataParser.h +++ b/content/svg/content/src/nsSVGPathDataParser.h @@ -17,53 +17,101 @@ class SVGPathData; //////////////////////////////////////////////////////////////////////// // nsSVGPathDataParser: a simple recursive descent parser that builds // DOMSVGPathSegs from path data strings. The grammar for path data // can be found in SVG CR 20001102, chapter 8. class nsSVGPathDataParser : public nsSVGDataParser { -public: - nsSVGPathDataParser(const nsAString& aValue, - mozilla::SVGPathData* aList) - : nsSVGDataParser(aValue), - mPathSegList(aList) - { - MOZ_ASSERT(aList, "null path data"); - } +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(); - bool Parse(); + nsresult MatchCoord(float* aX); + bool IsTokenCoordStarter(); + + 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(); + 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(); - bool ParseSubPathElements(); - bool ParseSubPathElement(PRUnichar aCommandType, - bool aAbsCoords); - - 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; -}; + 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(); + + }; class nsSVGArcConverter { typedef mozilla::gfx::Point Point; public: nsSVGArcConverter(const Point& from, const Point& to, @@ -75,9 +123,39 @@ public: protected: int32_t mNumSegs, mSegIndex; double mTheta, mDelta, mT; double mSinPhi, mCosPhi; double mRx, mRy; 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__