Bug 919319 - Simplify number parsing. r=dholbert
authorRobert Longson <longsonr@gmail.com>
Tue, 01 Oct 2013 08:50:40 +0100
changeset 149439 f550869d86bc6e7b3733fc33d49ca952f6e50af7
parent 149438 58a39673d3d70aaecf49c3b9f2d2ce29e2e13b7f
child 149440 af4605d0cc6b64214f3b344c12a376a03d43fd13
push id25389
push userryanvm@gmail.com
push dateTue, 01 Oct 2013 20:35:30 +0000
treeherdermozilla-central@4364824a4cab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs919319
milestone27.0a1
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
Bug 919319 - Simplify number parsing. r=dholbert
content/svg/content/src/SVGContentUtils.cpp
content/svg/content/src/SVGContentUtils.h
content/svg/content/src/SVGLength.cpp
content/svg/content/src/SVGLengthList.cpp
content/svg/content/src/SVGNumberList.cpp
content/svg/content/src/SVGPointList.cpp
content/svg/content/src/nsSVGAngle.cpp
content/svg/content/src/nsSVGAnimatedTransformList.cpp
content/svg/content/src/nsSVGBoolean.cpp
content/svg/content/src/nsSVGInteger.cpp
content/svg/content/src/nsSVGIntegerPair.cpp
content/svg/content/src/nsSVGLength2.cpp
content/svg/content/src/nsSVGNumber2.cpp
content/svg/content/src/nsSVGNumberPair.cpp
content/svg/content/src/nsSVGViewBox.cpp
content/svg/content/test/test_dataTypes.html
--- a/content/svg/content/src/SVGContentUtils.cpp
+++ b/content/svg/content/src/SVGContentUtils.cpp
@@ -4,23 +4,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // 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 "mozilla/dom/SVGSVGElement.h"
 #include "SVGAnimatedPreserveAspectRatio.h"
 #include "nsContentUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 SVGSVGElement*
 SVGContentUtils::GetOuterSVGElement(nsSVGElement *aSVGElement)
@@ -361,8 +362,196 @@ SVGContentUtils::GetViewBoxTransform(flo
     else NS_NOTREACHED("Unknown value for meetOrSlice");
   }
   
   if (aViewboxX) e += -a * aViewboxX;
   if (aViewboxY) f += -d * aViewboxY;
   
   return gfxMatrix(a, 0.0f, 0.0f, d, e, f);
 }
+
+/**
+ * True if 'aCh' is a decimal digit.
+ */
+static inline bool
+IsDigit(PRUnichar aCh)
+{
+  return aCh >= '0' && aCh <= '9';
+}
+
+/**
+ * Assuming that 'aCh' is a decimal digit, return its numeric value.
+ */
+static inline uint32_t
+DecimalDigitValue(PRUnichar aCh)
+{
+  MOZ_ASSERT(IsDigit(aCh), "Digit expected");
+  return aCh - '0';
+}
+
+template<class floatType>
+bool
+SVGContentUtils::ParseNumber(const nsAString& aString, 
+                             floatType& aValue,
+                             nsAString& aLeftOver)
+{
+  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;
+  }
+
+  // Sign of the mantissa (-1 or 1).
+  int32_t sign = *iter == '-' ? -1 : 1;
+
+  if (*iter == '-' || *iter == '+') {
+    ++iter;
+    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 != end && IsDigit(*iter));
+
+    if (iter != end) {
+      gotDot = *iter == '.';
+    }
+  }
+
+  // Fractional part of the mantissa.
+  floatType fracPart = floatType(0);
+
+  if (gotDot) {
+    ++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 != end && IsDigit(*iter));
+  }
+
+  bool gotE = false;
+  int32_t exponent = 0;
+  int32_t expSign;
+
+  if (iter != end && (*iter == 'e' || *iter == 'E')) {
+
+    mozilla::RangedPtr<const PRUnichar> expIter(iter);
+
+    ++expIter;
+    if (expIter != end) {
+      expSign = *expIter == '-' ? -1 : 1;
+      if (*expIter == '-' || *expIter == '+') {
+        ++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 != end && IsDigit(*iter));
+    }
+  }
+
+  // Assemble the number
+  aValue = sign * (intPart + fracPart);
+  if (gotE) {
+    aValue *= pow(floatType(10), floatType(expSign * exponent));
+  }
+  
+  aLeftOver = Substring(iter.get(), end.get());
+  return NS_finite(aValue);
+}
+
+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)
+{
+  nsAutoString leftOver;
+
+  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)
+{
+  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 == '+') {
+    ++iter;
+    if (iter == end) {
+      return false;
+    }
+  }
+
+  int64_t value = 0;
+
+  do {
+    if (!IsDigit(*iter)) {
+      return false;
+    }
+    if (value <= std::numeric_limits<int32_t>::max()) {
+      value = 10 * value + DecimalDigitValue(*iter);
+    }
+    ++iter;
+  } while (iter != end);
+
+  aValue = int32_t(clamped(sign * value,
+                           int64_t(std::numeric_limits<int32_t>::min()),
+                           int64_t(std::numeric_limits<int32_t>::max())));
+  return true;
+}
--- a/content/svg/content/src/SVGContentUtils.h
+++ b/content/svg/content/src/SVGContentUtils.h
@@ -127,11 +127,39 @@ public:
                       const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio);
 
   static gfxMatrix
   GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
                       float aViewboxX, float aViewboxY,
                       float aViewboxWidth, float aViewboxHeight,
                       const SVGPreserveAspectRatio &aPreserveAspectRatio);
 
+  /**
+   * 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.
+   */
+  template<class floatType>
+  static bool
+  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>
+  static bool
+  ParseNumber(const nsAString& aString, floatType& aValue);
+
+  /**
+   * Parse an integer of the form:
+   * integer ::= [+-]? [0-9]+
+   * Parsing fails if the number cannot be represented by an int32_t.
+   */
+  static bool
+  ParseInteger(const nsAString& aString, int32_t& aValue);
 };
 
 #endif
--- a/content/svg/content/src/SVGLength.cpp
+++ b/content/svg/content/src/SVGLength.cpp
@@ -3,20 +3,17 @@
  * 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 "SVGLength.h"
 #include "nsSVGElement.h"
 #include "mozilla/dom/SVGSVGElement.h"
-#include "nsString.h"
 #include "nsTextFormatter.h"
-#include "prdtoa.h"
-#include "nsMathUtils.h"
 #include "SVGContentUtils.h"
 #include <limits>
 #include <algorithm>
 
 namespace mozilla {
 
 // Declare some helpers defined below:
 static void GetUnitString(nsAString& unit, uint16_t unitType);
@@ -32,50 +29,32 @@ SVGLength::GetValueAsString(nsAString &a
   aValue.Assign(buf);
 
   nsAutoString unitString;
   GetUnitString(unitString, mUnit);
   aValue.Append(unitString);
 }
 
 bool
-SVGLength::SetValueFromString(const nsAString &aValue)
+SVGLength::SetValueFromString(const nsAString &aValueAsString)
 {
-  float tmpValue;
-  uint16_t tmpUnit;
+  nsAutoString units;
+  float value;
 
-  NS_ConvertUTF16toUTF8 value(aValue);
-  const char *str = value.get();
-
-  while (*str != '\0' && IsSVGWhitespace(*str)) {
-    ++str;
+  if (!SVGContentUtils::ParseNumber(aValueAsString, value, units)) {
+    return false;
   }
-  char *unit;
-  tmpValue = float(PR_strtod(str, &unit));
-  if (unit != str && NS_finite(tmpValue)) {
-    char *theRest = unit;
-    while (*theRest != '\0' && !IsSVGWhitespace(*theRest)) {
-      ++theRest;
-    }
-    tmpUnit = GetUnitTypeForString(
-                Substring(aValue, unit - str, theRest - unit));
-    if (tmpUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN) {
-      // SVGContentUtils::ReportToConsole
-      return false;
-    }
-    while (*theRest && IsSVGWhitespace(*theRest)) {
-      ++theRest;
-    }
-    if (!*theRest) {
-      mValue = tmpValue;
-      mUnit = tmpUnit;
-      return true;
-    }
+
+  uint16_t unitType = GetUnitTypeForString(units);
+  if (!IsValidUnitType(unitType)) {
+    return false;
   }
-  return false;
+  mValue = value;
+  mUnit = uint8_t(unitType);
+  return true;
 }
 
 inline static bool
 IsAbsoluteUnit(uint8_t aUnit)
 {
   return aUnit >= nsIDOMSVGLength::SVG_LENGTHTYPE_CM &&
          aUnit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC;
 }
--- a/content/svg/content/src/SVGLengthList.cpp
+++ b/content/svg/content/src/SVGLengthList.cpp
@@ -44,18 +44,16 @@ SVGLengthList::GetValueAsString(nsAStrin
 nsresult
 SVGLengthList::SetValueFromString(const nsAString& aValue)
 {
   SVGLengthList temp;
 
   nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
     tokenizer(aValue, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
 
-  nsAutoCString str;  // outside loop to minimize memory churn
-
   while (tokenizer.hasMoreTokens()) {
     SVGLength length;
     if (!length.SetValueFromString(tokenizer.nextToken())) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
     if (!temp.AppendItem(length)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
--- a/content/svg/content/src/SVGNumberList.cpp
+++ b/content/svg/content/src/SVGNumberList.cpp
@@ -2,21 +2,18 @@
 /* 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 "SVGNumberList.h"
 #include "nsCharSeparatedTokenizer.h"
-#include "nsError.h"
-#include "nsMathUtils.h"
 #include "nsString.h"
 #include "nsTextFormatter.h"
-#include "prdtoa.h"
 #include "SVGContentUtils.h"
 
 namespace mozilla {
 
 nsresult
 SVGNumberList::CopyFrom(const SVGNumberList& rhs)
 {
   if (!mNumbers.SetCapacity(rhs.Length())) {
@@ -50,27 +47,19 @@ SVGNumberList::GetValueAsString(nsAStrin
 nsresult
 SVGNumberList::SetValueFromString(const nsAString& aValue)
 {
   SVGNumberList temp;
 
   nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
     tokenizer(aValue, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
 
-  nsAutoCString str;  // outside loop to minimize memory churn
-
   while (tokenizer.hasMoreTokens()) {
-    CopyUTF16toUTF8(tokenizer.nextToken(), str); // NS_ConvertUTF16toUTF8
-    const char *token = str.get();
-    if (*token == '\0') {
-      return NS_ERROR_DOM_SYNTAX_ERR; // nothing between commas
-    }
-    char *end;
-    float num = float(PR_strtod(token, &end));
-    if (*end != '\0' || !NS_finite(num)) {
+    float num;
+    if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), num)) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
     if (!temp.AppendItem(num)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
   if (tokenizer.separatorAfterCurrentToken()) {
     return NS_ERROR_DOM_SYNTAX_ERR; // trailing comma
--- a/content/svg/content/src/SVGPointList.cpp
+++ b/content/svg/content/src/SVGPointList.cpp
@@ -1,22 +1,18 @@
 /* -*- 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/. */
 
 #include "mozilla/Util.h"
 
 #include "SVGPointList.h"
-#include "nsError.h"
 #include "nsCharSeparatedTokenizer.h"
-#include "nsMathUtils.h"
-#include "nsString.h"
 #include "nsTextFormatter.h"
-#include "prdtoa.h"
 #include "SVGContentUtils.h"
 
 namespace mozilla {
 
 nsresult
 SVGPointList::CopyFrom(const SVGPointList& rhs)
 {
   if (!SetCapacity(rhs.Length())) {
@@ -57,55 +53,40 @@ SVGPointList::SetValueFromString(const n
 
   nsresult rv = NS_OK;
 
   SVGPointList temp;
 
   nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
     tokenizer(aValue, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
 
-  nsAutoCString str1, str2;  // outside loop to minimize memory churn
+  while (tokenizer.hasMoreTokens()) {
 
-  while (tokenizer.hasMoreTokens()) {
-    CopyUTF16toUTF8(tokenizer.nextToken(), str1);
-    const char *token1 = str1.get();
-    if (*token1 == '\0') {
+    float x;
+    nsAutoString leftOver;
+    if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), x, leftOver)) {
       rv = NS_ERROR_DOM_SYNTAX_ERR;
       break;
     }
-    char *end;
-    float x = float(PR_strtod(token1, &end));
-    if (end == token1 || !NS_finite(x)) {
-      rv = NS_ERROR_DOM_SYNTAX_ERR;
-      break;
-    }
-    const char *token2;
-    if (*end == '-') {
-      // It's possible for the token to be 10-30 which has
-      // no separator but needs to be parsed as 10, -30
-      token2 = end;
-    } else {
-      if (!tokenizer.hasMoreTokens()) {
+
+    float y;
+    if (leftOver.IsEmpty()) {
+      if (!tokenizer.hasMoreTokens() ||
+          !SVGContentUtils::ParseNumber(tokenizer.nextToken(), y)) {
         rv = NS_ERROR_DOM_SYNTAX_ERR;
         break;
       }
-      CopyUTF16toUTF8(tokenizer.nextToken(), str2);
-      token2 = str2.get();
-      if (*token2 == '\0') {
+    } else {
+      // It's possible for the token to be 10-30 which has
+      // no separator but needs to be parsed as 10, -30
+      if (leftOver[0] != '-' || !SVGContentUtils::ParseNumber(leftOver, y)) {
         rv = NS_ERROR_DOM_SYNTAX_ERR;
         break;
       }
     }
-
-    float y = float(PR_strtod(token2, &end));
-    if (*end != '\0' || !NS_finite(y)) {
-      rv = NS_ERROR_DOM_SYNTAX_ERR;
-      break;
-    }
-
     temp.AppendItem(SVGPoint(x, y));
   }
   if (tokenizer.separatorAfterCurrentToken()) {
     rv = NS_ERROR_DOM_SYNTAX_ERR; // trailing comma
   }
   nsresult rv2 = CopyFrom(temp);
   if (NS_FAILED(rv2)) {
     return rv2; // prioritize OOM error code over syntax errors
--- a/content/svg/content/src/nsSVGAngle.cpp
+++ b/content/svg/content/src/nsSVGAngle.cpp
@@ -6,17 +6,16 @@
 #include "mozilla/Util.h"
 
 #include "nsSVGAngle.h"
 #include "mozilla/dom/SVGMarkerElement.h"
 #include "nsContentUtils.h" // NS_ENSURE_FINITE
 #include "nsSMILValue.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "nsTextFormatter.h"
-#include "prdtoa.h"
 #include "SVGAngle.h"
 #include "SVGAnimatedAngle.h"
 #include "SVGOrientSMILType.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static nsIAtom** const unitMap[] =
@@ -89,38 +88,29 @@ GetValueString(nsAString &aValueAsString
                             (double)aValue);
   aValueAsString.Assign(buf);
 
   nsAutoString unitString;
   GetUnitString(unitString, aUnitType);
   aValueAsString.Append(unitString);
 }
 
-static nsresult
-GetValueFromString(const nsAString &aValueAsString,
-                   float *aValue,
-                   uint16_t *aUnitType)
+static bool
+GetValueFromString(const nsAString& aValueAsString,
+                   float& aValue,
+                   uint16_t* aUnitType)
 {
-  NS_ConvertUTF16toUTF8 value(aValueAsString);
-  const char *str = value.get();
+  nsAutoString units;
 
-  if (IsSVGWhitespace(*str))
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  
-  char *rest;
-  *aValue = float(PR_strtod(str, &rest));
-  if (rest != str && NS_finite(*aValue)) {
-    *aUnitType = GetUnitTypeForString(
-      Substring(aValueAsString, rest - str));
-    if (IsValidUnitType(*aUnitType)) {
-      return NS_OK;
-    }
+  if (!SVGContentUtils::ParseNumber(aValueAsString, aValue, units)) {
+    return false;
   }
-  
-  return NS_ERROR_DOM_SYNTAX_ERR;
+
+  *aUnitType = GetUnitTypeForString(units);
+  return IsValidUnitType(*aUnitType);
 }
 
 /* static */ float
 nsSVGAngle::GetDegreesPerUnit(uint8_t aUnit)
 {
   switch (aUnit) {
   case SVG_ANGLETYPE_UNSPECIFIED:
   case SVG_ANGLETYPE_DEG:
@@ -253,22 +243,21 @@ SVGAngle::~SVGAngle()
 
 /* Implementation */
 
 nsresult
 nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString,
                                nsSVGElement *aSVGElement,
                                bool aDoSetAttr)
 {
-  float value = 0;
-  uint16_t unitType = 0;
+  float value;
+  uint16_t unitType;
   
-  nsresult rv = GetValueFromString(aValueAsString, &value, &unitType);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!GetValueFromString(aValueAsString, value, &unitType)) {
+     return NS_ERROR_DOM_SYNTAX_ERR;
   }
   if (mBaseVal == value && mBaseValUnit == uint8_t(unitType)) {
     return NS_OK;
   }
 
   nsAttrValue emptyOrOldValue;
   if (aDoSetAttr) {
     emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
@@ -377,19 +366,18 @@ nsSVGAngle::SMILOrient::ValueFromString(
   nsSMILValue val(&SVGOrientSMILType::sSingleton);
   if (aStr.EqualsLiteral("auto")) {
     val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_AUTO;
   } else if (aStr.EqualsLiteral("auto-start-reverse")) {
     val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_AUTO_START_REVERSE;
   } else {
     float value;
     uint16_t unitType;
-    nsresult rv = GetValueFromString(aStr, &value, &unitType);
-    if (NS_FAILED(rv)) {
-      return rv;
+    if (!GetValueFromString(aStr, value, &unitType)) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
     }
     val.mU.mOrient.mAngle = value;
     val.mU.mOrient.mUnit = unitType;
     val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_ANGLE;
   }
   aValue.Swap(val);
   aPreventCachingOfSandwich = false;
 
--- a/content/svg/content/src/nsSVGAnimatedTransformList.cpp
+++ b/content/svg/content/src/nsSVGAnimatedTransformList.cpp
@@ -1,20 +1,20 @@
 /* -*- 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/. */
 
 #include "nsSVGAnimatedTransformList.h"
 #include "mozilla/dom/SVGAnimatedTransformList.h"
 #include "mozilla/dom/SVGAnimationElement.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsSVGTransform.h"
 #include "nsSMILValue.h"
-#include "prdtoa.h"
 #include "SVGContentUtils.h"
-#include "nsSVGTransform.h"
 #include "SVGTransformListSMILType.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 nsresult
 nsSVGAnimatedTransformList::SetBaseValueString(const nsAString& aValue)
@@ -230,64 +230,37 @@ nsSVGAnimatedTransformList::SMILAnimated
   if (NS_FAILED(SVGTransformListSMILType::AppendTransform(transform, val))) {
     return; // OOM
   }
 
   // Success! Populate our outparam with parsed value.
   aResult.Swap(val);
 }
 
-namespace
-{
-  inline void
-  SkipWsp(nsACString::const_iterator& aIter,
-          const nsACString::const_iterator& aIterEnd)
-  {
-    while (aIter != aIterEnd && IsSVGWhitespace(*aIter))
-      ++aIter;
-  }
-} // end anonymous namespace block
-
 int32_t
 nsSVGAnimatedTransformList::SMILAnimatedTransformList::ParseParameterList(
   const nsAString& aSpec,
   float* aVars,
   int32_t aNVars)
 {
-  NS_ConvertUTF16toUTF8 spec(aSpec);
-
-  nsACString::const_iterator start, end;
-  spec.BeginReading(start);
-  spec.EndReading(end);
-
-  SkipWsp(start, end);
+  nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
+    tokenizer(aSpec, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
 
   int numArgsFound = 0;
 
-  while (start != end) {
-    char const *arg = start.get();
-    char *argend;
-    float f = float(PR_strtod(arg, &argend));
-    if (arg == argend || argend > end.get() || !NS_finite(f))
-      return -1;
-
+  while (tokenizer.hasMoreTokens()) {
+    float f;
+    if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), f)) {
+      return -1;    
+    }
     if (numArgsFound < aNVars) {
       aVars[numArgsFound] = f;
     }
-
-    start.advance(argend - arg);
     numArgsFound++;
-
-    SkipWsp(start, end);
-    if (*start == ',') {
-      ++start;
-      SkipWsp(start, end);
-    }
   }
-
   return numArgsFound;
 }
 
 nsSMILValue
 nsSVGAnimatedTransformList::SMILAnimatedTransformList::GetBaseValue() const
 {
   // To benefit from Return Value Optimization and avoid copy constructor calls
   // due to our use of return-by-value, we must return the exact same object
--- a/content/svg/content/src/nsSVGBoolean.cpp
+++ b/content/svg/content/src/nsSVGBoolean.cpp
@@ -19,29 +19,29 @@ static inline
 nsSVGAttrTearoffTable<nsSVGBoolean, SVGAnimatedBoolean>&
 SVGAnimatedBooleanTearoffTable()
 {
   static nsSVGAttrTearoffTable<nsSVGBoolean, SVGAnimatedBoolean>
     sSVGAnimatedBooleanTearoffTable;
   return sSVGAnimatedBooleanTearoffTable;
 }
 
-static nsresult
-GetValueFromString(const nsAString &aValueAsString,
-                   bool *aValue)
+static bool
+GetValueFromString(const nsAString& aValueAsString,
+                   bool& aValue)
 {
   if (aValueAsString.EqualsLiteral("true")) {
-    *aValue = true;
-    return NS_OK;
+    aValue = true;
+    return true;
   }
   if (aValueAsString.EqualsLiteral("false")) {
-    *aValue = false;
-    return NS_OK;
+    aValue = false;
+    return true;
   }
-  return NS_ERROR_DOM_SYNTAX_ERR;
+  return false;
 }
 
 static nsresult
 GetValueFromAtom(const nsIAtom* aValueAsAtom, bool *aValue)
 {
   if (aValueAsAtom == nsGkAtoms::_true) {
     *aValue = true;
     return NS_OK;
@@ -136,19 +136,18 @@ nsSVGBoolean::ToSMILAttr(nsSVGElement *a
 
 nsresult
 nsSVGBoolean::SMILBool::ValueFromString(const nsAString& aStr,
                                         const SVGAnimationElement* /*aSrcElement*/,
                                         nsSMILValue& aValue,
                                         bool& aPreventCachingOfSandwich) const
 {
   bool value;
-  nsresult rv = GetValueFromString(aStr, &value);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!GetValueFromString(aStr, value)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   nsSMILValue val(SMILBoolType::Singleton());
   val.mU.mBool = value;
   aValue = val;
   aPreventCachingOfSandwich = false;
 
   return NS_OK;
--- a/content/svg/content/src/nsSVGInteger.cpp
+++ b/content/svg/content/src/nsSVGInteger.cpp
@@ -13,46 +13,24 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /* Implementation */
 
 static nsSVGAttrTearoffTable<nsSVGInteger, nsSVGInteger::DOMAnimatedInteger>
   sSVGAnimatedIntegerTearoffTable;
 
-static nsresult
-GetValueFromString(const nsAString &aValueAsString,
-                   int32_t *aValue)
-{
-  NS_ConvertUTF16toUTF8 value(aValueAsString);
-  const char *str = value.get();
-
-  if (IsSVGWhitespace(*str))
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  
-  char *rest;
-  *aValue = strtol(str, &rest, 10);
-  if (rest == str || *rest != '\0') {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-  if (*rest == '\0') {
-    return NS_OK;
-  }
-  return NS_ERROR_DOM_SYNTAX_ERR;
-}
-
 nsresult
 nsSVGInteger::SetBaseValueString(const nsAString &aValueAsString,
                                  nsSVGElement *aSVGElement)
 {
   int32_t value;
 
-  nsresult rv = GetValueFromString(aValueAsString, &value);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!SVGContentUtils::ParseInteger(aValueAsString, value)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   mIsBaseSet = true;
   mBaseVal = value;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
   else {
@@ -128,19 +106,18 @@ nsSVGInteger::ToSMILAttr(nsSVGElement *a
 nsresult
 nsSVGInteger::SMILInteger::ValueFromString(const nsAString& aStr,
                                            const dom::SVGAnimationElement* /*aSrcElement*/,
                                            nsSMILValue& aValue,
                                            bool& aPreventCachingOfSandwich) const
 {
   int32_t val;
 
-  nsresult rv = GetValueFromString(aStr, &val);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!SVGContentUtils::ParseInteger(aStr, val)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   nsSMILValue smilVal(SMILIntegerType::Singleton());
   smilVal.mU.mInt = val;
   aValue = smilVal;
   aPreventCachingOfSandwich = false;
   return NS_OK;
 }
--- a/content/svg/content/src/nsSVGIntegerPair.cpp
+++ b/content/svg/content/src/nsSVGIntegerPair.cpp
@@ -30,26 +30,18 @@ ParseIntegerOptionalInteger(const nsAStr
     tokenizer(aValue, ',',
               nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
   if (tokenizer.whitespaceBeforeFirstToken()) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   uint32_t i;
   for (i = 0; i < 2 && tokenizer.hasMoreTokens(); ++i) {
-    NS_ConvertUTF16toUTF8 utf8Token(tokenizer.nextToken());
-    const char *token = utf8Token.get();
-    if (*token == '\0') {
-      return NS_ERROR_DOM_SYNTAX_ERR; // empty string (e.g. two commas in a row)
-    }
-
-    char *end;
-    aValues[i] = strtol(token, &end, 10);
-    if (*end != '\0' || !NS_finite(aValues[i])) {
-      return NS_ERROR_DOM_SYNTAX_ERR; // parse error
+    if (!SVGContentUtils::ParseInteger(tokenizer.nextToken(), aValues[i])) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
     }
   }
   if (i == 1) {
     aValues[1] = aValues[0];
   }
 
   if (i == 0                    ||                // Too few values.
       tokenizer.hasMoreTokens() ||                // Too many values.
--- a/content/svg/content/src/nsSVGLength2.cpp
+++ b/content/svg/content/src/nsSVGLength2.cpp
@@ -10,17 +10,16 @@
 #include "mozilla/dom/SVGSVGElement.h"
 #include "nsContentUtils.h" // NS_ENSURE_FINITE
 #include "nsIFrame.h"
 #include "nsSMILFloatType.h"
 #include "nsSMILValue.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsTextFormatter.h"
-#include "prdtoa.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGLength2::DOMBaseVal, mSVGElement)
 
 NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGLength2::DOMAnimVal, mSVGElement)
 
@@ -117,38 +116,27 @@ GetValueString(nsAString &aValueAsString
                             (double)aValue);
   aValueAsString.Assign(buf);
 
   nsAutoString unitString;
   GetUnitString(unitString, aUnitType);
   aValueAsString.Append(unitString);
 }
 
-static nsresult
-GetValueFromString(const nsAString &aValueAsString,
-                   float *aValue,
-                   uint16_t *aUnitType)
+static bool
+GetValueFromString(const nsAString& aValueAsString,
+                   float& aValue,
+                   uint16_t* aUnitType)
 {
-  NS_ConvertUTF16toUTF8 value(aValueAsString);
-  const char *str = value.get();
-
-  if (IsSVGWhitespace(*str))
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  
-  char *rest;
-  *aValue = float(PR_strtod(str, &rest));
-  if (rest != str && NS_finite(*aValue)) {
-    *aUnitType = GetUnitTypeForString(
-      Substring(aValueAsString, rest - str));
-    if (IsValidUnitType(*aUnitType)) {
-      return NS_OK;
-    }
+  nsAutoString units;
+  if (!SVGContentUtils::ParseNumber(aValueAsString, aValue, units)) {
+    return false;
   }
-  
-  return NS_ERROR_DOM_SYNTAX_ERR;
+  *aUnitType = GetUnitTypeForString(units);
+  return IsValidUnitType(*aUnitType);
 }
 
 static float
 FixAxisLength(float aLength)
 {
   if (aLength == 0.0f) {
     NS_WARNING("zero axis length");
     return 1e-20f;
@@ -394,22 +382,21 @@ nsSVGLength2::DOMAnimVal::~DOMAnimVal()
 nsresult
 nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString,
                                  nsSVGElement *aSVGElement,
                                  bool aDoSetAttr)
 {
   float value;
   uint16_t unitType;
 
-  nsresult rv = GetValueFromString(aValueAsString, &value, &unitType);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!GetValueFromString(aValueAsString, value, &unitType)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
-  if (mIsBaseSet && mBaseVal == value &&
+  if (mIsBaseSet && mBaseVal == float(value) &&
       mSpecifiedUnitType == uint8_t(unitType)) {
     return NS_OK;
   }
 
   nsAttrValue emptyOrOldValue;
   if (aDoSetAttr) {
     emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
   }
@@ -498,19 +485,18 @@ nsresult
 nsSVGLength2::SMILLength::ValueFromString(const nsAString& aStr,
                                  const SVGAnimationElement* /*aSrcElement*/,
                                  nsSMILValue& aValue,
                                  bool& aPreventCachingOfSandwich) const
 {
   float value;
   uint16_t unitType;
   
-  nsresult rv = GetValueFromString(aStr, &value, &unitType);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!GetValueFromString(aStr, value, &unitType)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   nsSMILValue val(nsSMILFloatType::Singleton());
   val.mU.mDouble = value / mVal->GetUnitScaleFactor(mSVGElement, unitType);
   aValue = val;
   aPreventCachingOfSandwich =
               (unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE ||
                unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS ||
--- a/content/svg/content/src/nsSVGNumber2.cpp
+++ b/content/svg/content/src/nsSVGNumber2.cpp
@@ -1,22 +1,20 @@
 /* -*- 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/. */
 
 #include "nsSVGNumber2.h"
 #include "mozilla/Attributes.h"
 #include "nsContentUtils.h" // NS_ENSURE_FINITE
-#include "nsError.h"
 #include "nsIDOMSVGNumber.h"
 #include "nsSMILFloatType.h"
 #include "nsSMILValue.h"
 #include "nsSVGAttrTearoffTable.h"
-#include "prdtoa.h"
 #include "SVGContentUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 class DOMSVGNumber MOZ_FINAL : public nsIDOMSVGNumber
 {
 public:
@@ -45,53 +43,45 @@ NS_INTERFACE_MAP_BEGIN(DOMSVGNumber)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGNumber)
 NS_INTERFACE_MAP_END
 
 /* Implementation */
 
 static nsSVGAttrTearoffTable<nsSVGNumber2, nsSVGNumber2::DOMAnimatedNumber>
   sSVGAnimatedNumberTearoffTable;
 
-static nsresult
-GetValueFromString(const nsAString &aValueAsString,
+static bool
+GetValueFromString(const nsAString& aValueAsString,
                    bool aPercentagesAllowed,
-                   float *aValue)
+                   float& aValue)
 {
-  NS_ConvertUTF16toUTF8 value(aValueAsString);
-  const char *str = value.get();
+  nsAutoString units;
 
-  if (IsSVGWhitespace(*str))
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  
-  char *rest;
-  *aValue = float(PR_strtod(str, &rest));
-  if (rest == str || !NS_finite(*aValue)) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
+  if (!SVGContentUtils::ParseNumber(aValueAsString, aValue, units)) {
+    return false;
   }
-  if (*rest == '%' && aPercentagesAllowed) {
-    *aValue /= 100;
-    ++rest;
+
+  if (aPercentagesAllowed && units.EqualsLiteral("%")) {
+    aValue /= 100;
+    return true;
   }
-  if (*rest == '\0') {
-    return NS_OK;
-  }
-  return NS_ERROR_DOM_SYNTAX_ERR;
+
+  return units.IsEmpty();
 }
 
 nsresult
 nsSVGNumber2::SetBaseValueString(const nsAString &aValueAsString,
                                  nsSVGElement *aSVGElement)
 {
   float val;
 
-  nsresult rv = GetValueFromString(
-    aValueAsString, aSVGElement->NumberAttrAllowsPercentage(mAttrEnum), &val);
-
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!GetValueFromString(aValueAsString,
+                          aSVGElement->NumberAttrAllowsPercentage(mAttrEnum),
+                          val)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   mBaseVal = val;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
   else {
@@ -167,21 +157,20 @@ nsSVGNumber2::ToSMILAttr(nsSVGElement *a
 nsresult
 nsSVGNumber2::SMILNumber::ValueFromString(const nsAString& aStr,
                                           const mozilla::dom::SVGAnimationElement* /*aSrcElement*/,
                                           nsSMILValue& aValue,
                                           bool& aPreventCachingOfSandwich) const
 {
   float value;
 
-  nsresult rv = GetValueFromString(
-    aStr, mSVGElement->NumberAttrAllowsPercentage(mVal->mAttrEnum), &value);
-
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!GetValueFromString(aStr,
+                          mSVGElement->NumberAttrAllowsPercentage(mVal->mAttrEnum),
+                          value)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   nsSMILValue val(nsSMILFloatType::Singleton());
   val.mU.mDouble = value;
   aValue = val;
   aPreventCachingOfSandwich = false;
 
   return NS_OK;
--- a/content/svg/content/src/nsSVGNumberPair.cpp
+++ b/content/svg/content/src/nsSVGNumberPair.cpp
@@ -1,19 +1,16 @@
 /* -*- 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/. */
 
 #include "nsSVGNumberPair.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "nsCharSeparatedTokenizer.h"
-#include "prdtoa.h"
-#include "nsError.h"
-#include "nsMathUtils.h"
 #include "nsSMILValue.h"
 #include "SVGContentUtils.h"
 #include "SVGNumberPairSMILType.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static nsSVGAttrTearoffTable<nsSVGNumberPair, nsSVGNumberPair::DOMAnimatedNumber>
@@ -29,26 +26,18 @@ ParseNumberOptionalNumber(const nsAStrin
     tokenizer(aValue, ',',
               nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
   if (tokenizer.whitespaceBeforeFirstToken()) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   uint32_t i;
   for (i = 0; i < 2 && tokenizer.hasMoreTokens(); ++i) {
-    NS_ConvertUTF16toUTF8 utf8Token(tokenizer.nextToken());
-    const char *token = utf8Token.get();
-    if (*token == '\0') {
-      return NS_ERROR_DOM_SYNTAX_ERR; // empty string (e.g. two commas in a row)
-    }
-
-    char *end;
-    aValues[i] = float(PR_strtod(token, &end));
-    if (*end != '\0' || !NS_finite(aValues[i])) {
-      return NS_ERROR_DOM_SYNTAX_ERR; // parse error
+    if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), aValues[i])) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
     }
   }
   if (i == 1) {
     aValues[1] = aValues[0];
   }
 
   if (i == 0 ||                                   // Too few values.
       tokenizer.hasMoreTokens() ||                // Too many values.
--- a/content/svg/content/src/nsSVGViewBox.cpp
+++ b/content/svg/content/src/nsSVGViewBox.cpp
@@ -1,18 +1,17 @@
 /* -*- 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/. */
 
 #include "nsSVGViewBox.h"
-#include "prdtoa.h"
-#include "nsTextFormatter.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsSMILValue.h"
+#include "nsTextFormatter.h"
 #include "SVGContentUtils.h"
 #include "SVGViewBoxSMILType.h"
 
 #define NUM_VIEWBOX_COMPONENTS 4
 using namespace mozilla;
 
 /* Implementation of nsSVGViewBoxRect methods */
 
@@ -117,26 +116,18 @@ ToSVGViewBoxRect(const nsAString& aStr, 
   }
 
   nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
     tokenizer(aStr, ',',
               nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
   float vals[NUM_VIEWBOX_COMPONENTS];
   uint32_t i;
   for (i = 0; i < NUM_VIEWBOX_COMPONENTS && tokenizer.hasMoreTokens(); ++i) {
-    NS_ConvertUTF16toUTF8 utf8Token(tokenizer.nextToken());
-    const char *token = utf8Token.get();
-    if (*token == '\0') {
-      return NS_ERROR_DOM_SYNTAX_ERR; // empty string (e.g. two commas in a row)
-    }
-
-    char *end;
-    vals[i] = float(PR_strtod(token, &end));
-    if (*end != '\0' || !NS_finite(vals[i])) {
-      return NS_ERROR_DOM_SYNTAX_ERR; // parse error
+    if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), vals[i])) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
     }
   }
 
   if (i != NUM_VIEWBOX_COMPONENTS ||              // Too few values.
       tokenizer.hasMoreTokens() ||                // Too many values.
       tokenizer.separatorAfterCurrentToken()) {   // Trailing comma.
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
--- a/content/svg/content/test/test_dataTypes.html
+++ b/content/svg/content/test/test_dataTypes.html
@@ -92,20 +92,20 @@ function runTests()
   is(blur.stdDeviationX.animVal, 8.5, "number-optional-number first animVal");
   is(blur.stdDeviationY.animVal, 20.5, "number-optional-number second animVal");
   is(blur.getAttribute("stdDeviation"), "8.5, 20.5", "number-optional-number attribute");
 
   blur.stdDeviationY.baseVal = 8.5;
   is(blur.getAttribute("stdDeviation"), "8.5", "number-optional-number attribute");
 
   blur.setStdDeviation(24.5, 0.5);
-  is(blur.stdDeviationX.baseVal, 24.5, "integer-optional-integer first baseVal");
-  is(blur.stdDeviationX.animVal, 24.5, "integer-optional-integer first animVal");
-  is(blur.stdDeviationY.baseVal, 0.5, "integer-optional-integer second baseVal");
-  is(blur.stdDeviationY.animVal, 0.5, "integer-optional-integer second animVal");
+  is(blur.stdDeviationX.baseVal, 24.5, "number-optional-number first baseVal");
+  is(blur.stdDeviationX.animVal, 24.5, "number-optional-number first animVal");
+  is(blur.stdDeviationY.baseVal, 0.5, "number-optional-number second baseVal");
+  is(blur.stdDeviationY.animVal, 0.5, "number-optional-number second animVal");
 
   blur.setAttribute("stdDeviation", "");
   ok(blur.getAttribute("stdDeviation") === "",
      "empty number-optional-number attribute");
   blur.removeAttribute("stdDeviation");
   ok(blur.getAttribute("stdDeviation") === null,
      "removed number-optional-number attribute");
 
@@ -139,16 +139,37 @@ function runTests()
   is(filter.getAttribute("filterRes"), "50", "integer-optional-integer attribute");
 
   filter.setFilterRes(80, 90);
   is(filter.filterResX.baseVal, 80, "integer-optional-integer first baseVal");
   is(filter.filterResX.animVal, 80, "integer-optional-integer first animVal");
   is(filter.filterResY.baseVal, 90, "integer-optional-integer second baseVal");
   is(filter.filterResY.animVal, 90, "integer-optional-integer second animVal");
 
+  // 32 bit integer range
+  filter.setFilterRes(-2147483648, 2147483647);
+  is(filter.filterResX.baseVal, -2147483648, "integer-optional-integer first baseVal");
+  is(filter.filterResX.animVal, -2147483648, "integer-optional-integer first animVal");
+  is(filter.filterResY.baseVal, 2147483647, "integer-optional-integer second baseVal");
+  is(filter.filterResY.animVal, 2147483647, "integer-optional-integer second animVal");
+
+  // too big, clamp
+  filter.setAttribute("filterRes", "-2147483649, 2147483648");
+  is(filter.filterResX.baseVal, -2147483648, "integer-optional-integer first baseVal");
+  is(filter.filterResX.animVal, -2147483648, "integer-optional-integer first animVal");
+  is(filter.filterResY.baseVal, 2147483647, "integer-optional-integer second baseVal");
+  is(filter.filterResY.animVal, 2147483647, "integer-optional-integer second animVal");
+
+  // invalid
+  filter.setAttribute("filterRes", "-00000000000invalid, 214748364720invalid");
+  is(filter.filterResX.baseVal, 0, "integer-optional-integer first baseVal");
+  is(filter.filterResX.animVal, 0, "integer-optional-integer first animVal");
+  is(filter.filterResY.baseVal, 0, "integer-optional-integer second baseVal");
+  is(filter.filterResY.animVal, 0, "integer-optional-integer second animVal");
+
   filter.setAttribute("filterRes", "");
   ok(filter.getAttribute("filterRes") === "",
      "empty integer-optional-integer attribute");
   filter.removeAttribute("filterRes");
   ok(filter.getAttribute("filterRes") === null,
      "removed integer-optional-integer attribute");
 
   // angle attribute