From 7da2b696d439c8603824edfe1a9369a285777a65 Mon Sep 17 00:00:00 2001 From: "tor%cs.brown.edu" Date: Thu, 14 Sep 2006 13:48:36 +0000 Subject: [PATCH] Bug 294137 - mozilla accepts illegal syntax on transform attribute. Patch by wellsk@us.ibm.com, tor. r+sr=roc git-svn-id: svn://10.0.0.236/trunk@211686 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/content/svg/content/src/Makefile.in | 1 + .../svg/content/src/nsSVGTransformList.cpp | 167 +------- .../content/src/nsSVGTransformListParser.cpp | 405 ++++++++++++++++++ .../content/src/nsSVGTransformListParser.h | 88 ++++ 4 files changed, 502 insertions(+), 159 deletions(-) create mode 100644 mozilla/content/svg/content/src/nsSVGTransformListParser.cpp create mode 100644 mozilla/content/svg/content/src/nsSVGTransformListParser.h diff --git a/mozilla/content/svg/content/src/Makefile.in b/mozilla/content/svg/content/src/Makefile.in index 9b9de1e4a87..c51c86de0b6 100644 --- a/mozilla/content/svg/content/src/Makefile.in +++ b/mozilla/content/svg/content/src/Makefile.in @@ -134,6 +134,7 @@ CPPSRCS = \ nsSVGTitleElement.cpp \ nsSVGTransform.cpp \ nsSVGTransformList.cpp \ + nsSVGTransformListParser.cpp \ nsSVGUseElement.cpp \ nsSVGValue.cpp \ $(NULL) diff --git a/mozilla/content/svg/content/src/nsSVGTransformList.cpp b/mozilla/content/svg/content/src/nsSVGTransformList.cpp index 68bac0e9c78..57ba7583bd3 100644 --- a/mozilla/content/svg/content/src/nsSVGTransformList.cpp +++ b/mozilla/content/svg/content/src/nsSVGTransformList.cpp @@ -38,6 +38,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nsSVGTransformList.h" +#include "nsSVGTransformListParser.h" #include "nsSVGTransform.h" #include "nsSVGMatrix.h" #include "nsDOMError.h" @@ -130,119 +131,17 @@ NS_IMETHODIMP nsSVGTransformList::SetValueString(const nsAString& aValue) { // XXX: we don't implement the _exact_ BNF given in the - // specs. - + // specs. + nsresult rv = NS_OK; - char *str = ToNewCString(aValue); - - char* rest = str; - char* keyword; - char* args; - const char* delimiters1 = "\x20\x9\xD\xA,("; - const char* delimiters2 = "()"; + // parse transform attribute value nsCOMArray xforms; - - while ((keyword = nsCRT::strtok(rest, delimiters1, &rest))) { + nsSVGTransformListParser parser(&xforms); + rv = parser.Parse(aValue); - while (rest && isspace(*rest)) - ++rest; - - if (!(args = nsCRT::strtok(rest, delimiters2, &rest))) { - rv = NS_ERROR_FAILURE; - break; // parse error - } - nsCOMPtr transform; - NS_NewSVGTransform(getter_AddRefs(transform)); - if (!transform) { - rv = NS_ERROR_OUT_OF_MEMORY; - break; - } - - nsCOMPtr keyatom = do_GetAtom(keyword); - - if (keyatom == nsSVGAtoms::translate) { - // tx [ty=0] - float t[2] = { 0.f }; - PRInt32 num_parsed = ParseParameterList(args, t, 2); - if (num_parsed != 1 && num_parsed != 2) { - rv = NS_ERROR_FAILURE; - break; // parse error - } - - transform->SetTranslate(t[0], t[1]); - } - else if (keyatom == nsSVGAtoms::scale) { - // sx [sy=sx] - float s[2] = { 0.f }; - PRInt32 num_parsed = ParseParameterList(args, s, 2); - if (num_parsed != 1 && num_parsed != 2) { - rv = NS_ERROR_FAILURE; - break; // parse error - } - - if (num_parsed == 1) - s[1] = s[0]; - - transform->SetScale(s[0], s[1]); - } - else if (keyatom == nsSVGAtoms::rotate) { - // r [x0=0 y0=0] - float r[3] = { 0.f }; - PRInt32 num_parsed = ParseParameterList(args, r, 3); - if (num_parsed != 1 && num_parsed != 3) { - rv = NS_ERROR_FAILURE; - break; // parse error - } - - transform->SetRotate(r[0], r[1], r[2]); - } - else if (keyatom == nsSVGAtoms::skewX) { - // x-angle - float angle; - PRInt32 num_parsed = ParseParameterList(args, &angle, 1); - if (num_parsed != 1) { - rv = NS_ERROR_FAILURE; - break; // parse error - } - - transform->SetSkewX(angle); - } - else if (keyatom == nsSVGAtoms::skewY) { - // y-angle - float angle; - PRInt32 num_parsed = ParseParameterList(args, &angle, 1); - if (num_parsed != 1) { - rv = NS_ERROR_FAILURE; - break; // parse error - } - - transform->SetSkewY(angle); - } - else if (keyatom == nsSVGAtoms::matrix) { - // a b c d e f - float m[6]; - PRInt32 num_parsed = ParseParameterList(args, m, 6); - if (num_parsed != 6) { - rv = NS_ERROR_FAILURE; - break; // parse error - } - - nsCOMPtr matrix; - NS_NewSVGMatrix(getter_AddRefs(matrix), - m[0], m[1], m[2], m[3], m[4], m[5]); - NS_ASSERTION(matrix, "couldn't create matrix"); - transform->SetMatrix(matrix); - } - else { // parse error - rv = NS_ERROR_FAILURE; - break; - } - xforms.AppendObject(transform); - } - - if (keyword || NS_FAILED(rv)) { - // there was a parse error. + if (NS_FAILED(rv)) { + // there was a parse error. rv = NS_ERROR_FAILURE; } else { @@ -255,59 +154,9 @@ nsSVGTransformList::SetValueString(const nsAString& aValue) DidModify(); } - nsMemory::Free(str); - return rv; } -// helper for SetValueString -// parse up to nvars comma-separated parameters into vars, returning -// the number of variables actually provided. -// -// -1 will be returned if any of the arguments can't be converted to a -// float. The return value may be higher than nvars, if more -// arguments were provided; no attempt is made to actually parse any -// more arguments than nvars. -// -// note that this would accept ",,," and will just return 0 -// arguments found, instead of -1. This is because the numeric -// values can have spaces surrounding them, but the spaces can also -// be used as delimiters. strtok doesn't tell us what the -// delimiter is, so we have no way to distinguish -// ' 20,20 ' from ',,20,20,,' (or from ',,20 20,,', for that matter). -// -// XXX If someone knows for sure that it's not ok to mix commas and -// spaces as delimiters, then we can scan the string for a comma, -// and if found use a delimiter of just ",", otherwise use a set of -// spaces (without a comma). -PRInt32 -nsSVGTransformList::ParseParameterList(char *paramstr, float *vars, PRInt32 nvars) -{ - if (!paramstr) - return 0; - - char *arg, *argend, *argrest = paramstr; - int num_args_found = 0; - float f; - - const char* arg_delimiters = "\x20\x09\x0d\x0a,"; - - while ((arg = nsCRT::strtok(argrest, arg_delimiters, &argrest))) { - if (num_args_found < nvars) { - f = (float) PR_strtod(arg, &argend); - if (arg == argend || *argend != '\0') - return -1; - - vars[num_args_found] = f; - } - - arg = argrest; - num_args_found++; - } - - return num_args_found; -} - NS_IMETHODIMP nsSVGTransformList::GetValueString(nsAString& aValue) { diff --git a/mozilla/content/svg/content/src/nsSVGTransformListParser.cpp b/mozilla/content/svg/content/src/nsSVGTransformListParser.cpp new file mode 100644 index 00000000000..abd72e542a3 --- /dev/null +++ b/mozilla/content/svg/content/src/nsSVGTransformListParser.cpp @@ -0,0 +1,405 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla SVG project. + * + * The Initial Developer of the Original Code is + * IBM Corporation + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsSVGTransformListParser.h" +#include "prdtoa.h" +#include "nsSVGTransform.h" +#include "nsSVGMatrix.h" +#include "nsDOMError.h" +#include "nsSVGAtoms.h" +#include "nsReadableUtils.h" +#include "nsCRT.h" +#include "nsCOMArray.h" +#include "nsContentUtils.h" +#include "nsIDOMClassInfo.h" + +//---------------------------------------------------------------------- +// public interface + +nsSVGTransformListParser::nsSVGTransformListParser(nsCOMArray* aTransforms) + : mTransform(aTransforms) +{ +} + +//---------------------------------------------------------------------- +// private methods + +nsresult +nsSVGTransformListParser::Match() +{ + return MatchTransformList(); +} + + +nsresult +nsSVGTransformListParser::MatchTransformList() +{ + MatchWsp(); + + if (IsTokenTransformStarter()) { + ENSURE_MATCHED(MatchTransforms()); + } + + MatchWsp(); + + return NS_OK; +} + + +nsresult +nsSVGTransformListParser::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 +nsSVGTransformListParser::GetTransformToken(nsIAtom** aKeyAtom, + PRBool 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'; + + if (mTokenPos != 0 && nsCRT::strlen(mTokenPos) > 0) { + *aKeyAtom = NS_NewAtom(mTokenPos); + + if (aAdvancePos) { + mInputPos = mTokenPos + nsCRT::strlen(mTokenPos); + mTokenPos = mInputPos; + } + } + else { + rv = NS_ERROR_FAILURE; + } + /* reset character back to original */ + *delimiterStart = holdingChar; + } + else { + rv = NS_ERROR_FAILURE; + } + + return rv; +} + + +nsresult +nsSVGTransformListParser::MatchTransform() +{ + nsCOMPtr keyatom; + + nsresult rv = GetTransformToken(getter_AddRefs(keyatom), PR_TRUE); + NS_ENSURE_SUCCESS(rv, rv); + + if (keyatom == nsSVGAtoms::translate) { + ENSURE_MATCHED(MatchTranslate()); + } + else if (keyatom == nsSVGAtoms::scale) { + ENSURE_MATCHED(MatchScale()); + } + else if (keyatom == nsSVGAtoms::rotate) { + ENSURE_MATCHED(MatchRotate()); + } + else if (keyatom == nsSVGAtoms::skewX) { + ENSURE_MATCHED(MatchSkewX()); + } + else if (keyatom == nsSVGAtoms::skewY) { + ENSURE_MATCHED(MatchSkewY()); + } + else if (keyatom == nsSVGAtoms::matrix) { + ENSURE_MATCHED(MatchMatrix()); + } + else { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + + +PRBool +nsSVGTransformListParser::IsTokenTransformStarter() +{ + nsCOMPtr keyatom; + + nsresult rv = GetTransformToken(getter_AddRefs(keyatom), PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + + if (keyatom == nsSVGAtoms::translate || + keyatom == nsSVGAtoms::scale || + keyatom == nsSVGAtoms::rotate || + keyatom == nsSVGAtoms::skewX || + keyatom == nsSVGAtoms::skewY || + keyatom == nsSVGAtoms::matrix) { + return PR_TRUE; + } + + return PR_FALSE; +} + +nsresult +nsSVGTransformListParser::MatchNumberArguments(float *aResult, + PRUint32 aMaxNum, + PRUint32 *aParsedNum) +{ + *aParsedNum = 0; + + 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)++])); + } + + MatchWsp(); + + ENSURE_MATCHED(MatchRightParen()); + + return NS_OK; +} + +nsIDOMSVGTransform * +nsSVGTransformListParser::AppendTransform() +{ + nsCOMPtr transform; + NS_NewSVGTransform(getter_AddRefs(transform)); + if (transform) { + mTransform->AppendObject(transform); + } + return transform; +} + +nsresult +nsSVGTransformListParser::MatchTranslate() +{ + GetNextToken(); + + float t[2]; + PRUint32 count; + + ENSURE_MATCHED(MatchNumberArguments(t, NS_ARRAY_LENGTH(t), &count)); + + switch (count) { + case 1: + t[1] = 0.f; + // fall-through + case 2: + { + nsIDOMSVGTransform *transform = AppendTransform(); + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); + transform->SetTranslate(t[0], t[1]); + break; + } + default: + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + + +nsresult +nsSVGTransformListParser::MatchScale() +{ + GetNextToken(); + + float s[2]; + PRUint32 count; + + ENSURE_MATCHED(MatchNumberArguments(s, NS_ARRAY_LENGTH(s), &count)); + + switch (count) { + case 1: + s[1] = s[0]; + // fall-through + case 2: + { + nsIDOMSVGTransform *transform = AppendTransform(); + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); + transform->SetScale(s[0], s[1]); + break; + } + default: + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + + +nsresult +nsSVGTransformListParser::MatchRotate() +{ + GetNextToken(); + + float r[3]; + PRUint32 count; + + ENSURE_MATCHED(MatchNumberArguments(r, NS_ARRAY_LENGTH(r), &count)); + + switch (count) { + case 1: + r[1] = r[2] = 0.f; + // fall-through + case 3: + { + nsIDOMSVGTransform *transform = AppendTransform(); + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); + transform->SetRotate(r[0], r[1], r[2]); + break; + } + default: + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + + +nsresult +nsSVGTransformListParser::MatchSkewX() +{ + GetNextToken(); + + float skew; + PRUint32 count; + + ENSURE_MATCHED(MatchNumberArguments(&skew, 1, &count)); + + if (count != 1) { + return NS_ERROR_FAILURE; + } + + nsIDOMSVGTransform *transform = AppendTransform(); + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); + transform->SetSkewX(skew); + + return NS_OK; +} + + +nsresult +nsSVGTransformListParser::MatchSkewY() +{ + GetNextToken(); + + float skew; + PRUint32 count; + + ENSURE_MATCHED(MatchNumberArguments(&skew, 1, &count)); + + if (count != 1) { + return NS_ERROR_FAILURE; + } + + nsIDOMSVGTransform *transform = AppendTransform(); + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); + transform->SetSkewY(skew); + + return NS_OK; +} + + +nsresult +nsSVGTransformListParser::MatchMatrix() +{ + GetNextToken(); + + float m[6]; + PRUint32 count; + + ENSURE_MATCHED(MatchNumberArguments(m, NS_ARRAY_LENGTH(m), &count)); + + if (count != 6) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr matrix; + NS_NewSVGMatrix(getter_AddRefs(matrix), + m[0], m[1], m[2], m[3], m[4], m[5]); + NS_ENSURE_TRUE(matrix, NS_ERROR_OUT_OF_MEMORY); + + nsIDOMSVGTransform *transform = AppendTransform(); + NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY); + transform->SetMatrix(matrix); + + return NS_OK; +} diff --git a/mozilla/content/svg/content/src/nsSVGTransformListParser.h b/mozilla/content/svg/content/src/nsSVGTransformListParser.h new file mode 100644 index 00000000000..7b62f8822d3 --- /dev/null +++ b/mozilla/content/svg/content/src/nsSVGTransformListParser.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla SVG project. + * + * The Initial Developer of the Original Code is + * IBM Corporation + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __NS_SVGTRANSFORMLISTPARSER_H__ +#define __NS_SVGTRANSFORMLISTPARSER_H__ + +#include "nsSVGDataParser.h" +#include "nsCOMPtr.h" +#include "nsCOMArray.h" +#include "nsIDOMSVGTransformList.h" +#include "nsIAtom.h" + +//////////////////////////////////////////////////////////////////////// +// nsSVGTransformListParser: 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 nsSVGTransformListParser : public nsSVGDataParser +{ +public: + nsSVGTransformListParser(nsCOMArray* aTransforms); + +private: + nsCOMArray *mTransform; + + // helpers + nsresult Match(); + + nsresult MatchNumberArguments(float *aResult, + PRUint32 aMaxNum, + PRUint32 *aParsedNum); + nsIDOMSVGTransform *AppendTransform(); + + nsresult MatchTransformList(); + + nsresult GetTransformToken(nsIAtom** aKeyatom, PRBool aAdvancePos); + nsresult MatchTransforms(); + + nsresult MatchTransform(); + + PRBool IsTokenTransformStarter(); + + nsresult MatchTranslate(); + + nsresult MatchScale(); + nsresult MatchRotate(); + nsresult MatchSkewX(); + nsresult MatchSkewY(); + nsresult MatchMatrix(); +}; + +#endif // __NS_SVGTRANSFORMLISTPARSER_H__