Bug 617623 - animate number-optional-number and integer-optional-integer attributes properly. r=dholbert
authorRobert Longson <longsonr@gmail.com>
Fri, 01 Jul 2011 08:19:52 +0100
changeset 72209 6ef387126d0f1e1dfcaa041729734ded1f47005e
parent 72208 74635b831e9e3fb2e3f5c15cb52e47625f2adfb4
child 72210 21735c9f4122daa8897384e56e5bec5d9e3dc782
push id16
push usergsharp@mozilla.com
push dateSat, 02 Jul 2011 15:25:48 +0000
treeherderfx-team@dad2f5b0bc1f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs617623
milestone7.0a1
Bug 617623 - animate number-optional-number and integer-optional-integer attributes properly. r=dholbert
content/smil/nsSMILValue.h
content/svg/content/src/Makefile.in
content/svg/content/src/SVGAnimatedNumberList.cpp
content/svg/content/src/SVGAnimatedNumberList.h
content/svg/content/src/SVGIntegerPairSMILType.cpp
content/svg/content/src/SVGIntegerPairSMILType.h
content/svg/content/src/SVGNumberPairSMILType.cpp
content/svg/content/src/SVGNumberPairSMILType.h
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGElement.h
content/svg/content/src/nsSVGFilterElement.cpp
content/svg/content/src/nsSVGFilterElement.h
content/svg/content/src/nsSVGFilters.cpp
content/svg/content/src/nsSVGFilters.h
content/svg/content/src/nsSVGImageElement.cpp
content/svg/content/src/nsSVGInteger.cpp
content/svg/content/src/nsSVGInteger.h
content/svg/content/src/nsSVGIntegerPair.cpp
content/svg/content/src/nsSVGIntegerPair.h
content/svg/content/src/nsSVGNumberPair.cpp
content/svg/content/src/nsSVGNumberPair.h
content/svg/content/src/nsSVGString.cpp
content/svg/content/src/nsSVGString.h
content/svg/content/test/dataTypes-helper.svg
content/svg/content/test/test_dataTypes.html
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
layout/reftests/svg/smil/anim-feGaussianBlur-01.svg
layout/reftests/svg/smil/anim-filter-filterRes-01.svg
layout/reftests/svg/smil/reftest.list
layout/svg/base/src/nsSVGFilterFrame.cpp
layout/svg/base/src/nsSVGFilterInstance.cpp
--- a/content/smil/nsSMILValue.h
+++ b/content/smil/nsSMILValue.h
@@ -92,16 +92,18 @@ public:
     PRUint64 mUint;
     PRInt64 mInt;
     double mDouble;
     struct {
       float mAngle;
       PRUint16 mUnit;
       PRUint16 mOrientType;
     } mOrient;
+    PRInt32 mIntPair[2];
+    float mNumberPair[2];
     void* mPtr;
   } mU;
   const nsISMILType* mType;
 
 protected:
   void InitAndCheckPostcondition(const nsISMILType* aNewType);
   void DestroyAndCheckPostcondition();
   void DestroyAndReinit(const nsISMILType* aNewType);
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -80,23 +80,25 @@ CPPSRCS		= \
 		nsSVGFilterElement.cpp \
 		nsSVGFilters.cpp \
 		nsSVGForeignObjectElement.cpp \
 		nsSVGGElement.cpp \
 		nsSVGGradientElement.cpp \
 		nsSVGGraphicElement.cpp \
 		nsSVGImageElement.cpp \
 		nsSVGInteger.cpp \
+		nsSVGIntegerPair.cpp \
 		nsSVGLength2.cpp \
 		nsSVGLineElement.cpp \
 		nsSVGMarkerElement.cpp \
 		nsSVGMaskElement.cpp \
 		nsSVGMatrix.cpp \
 		nsSVGMetadataElement.cpp \
 		nsSVGNumber2.cpp \
+		nsSVGNumberPair.cpp \
 		nsSVGPathDataParser.cpp \
 		nsSVGPathElement.cpp \
 		nsSVGPathGeometryElement.cpp \
 		nsSVGPatternElement.cpp \
 		nsSVGPolyElement.cpp \
 		nsSVGPolygonElement.cpp \
 		nsSVGPolylineElement.cpp \
 		nsSVGScriptElement.cpp \
@@ -139,22 +141,24 @@ ifdef MOZ_SMIL
 CPPSRCS += nsSVGAnimateElement.cpp \
            nsSVGAnimateTransformElement.cpp \
            nsSVGAnimateMotionElement.cpp \
            nsSVGAnimationElement.cpp \
            nsSVGMpathElement.cpp \
            nsSVGSetElement.cpp \
            nsSVGTransformSMILType.cpp \
            nsSVGTransformSMILAttr.cpp \
+           SVGIntegerPairSMILType.cpp \
            SVGLengthListSMILType.cpp \
            SVGMotionSMILType.cpp \
            SVGMotionSMILAttr.cpp \
            SVGMotionSMILAnimationFunction.cpp \
            SVGMotionSMILPathUtils.cpp \
            SVGNumberListSMILType.cpp \
+           SVGNumberPairSMILType.cpp \
            SVGOrientSMILType.cpp \
            SVGPathSegListSMILType.cpp \
            SVGPointListSMILType.cpp \
            SVGViewBoxSMILType.cpp \
            $(NULL)
 endif
 
 include $(topsrcdir)/config/config.mk
--- a/content/svg/content/src/SVGAnimatedNumberList.cpp
+++ b/content/svg/content/src/SVGAnimatedNumberList.cpp
@@ -64,16 +64,17 @@ SVGAnimatedNumberList::SetBaseValueStrin
     //
     domWrapper->InternalBaseValListWillChangeTo(newBaseValue);
   }
 
   // We don't need to call DidChange* here - we're only called by
   // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
   // which takes care of notifying.
 
+  mIsBaseSet = PR_TRUE;
   rv = mBaseVal.CopyFrom(newBaseValue);
   if (NS_FAILED(rv) && domWrapper) {
     // Attempting to increase mBaseVal's length failed - reduce domWrapper
     // back to the same length:
     domWrapper->InternalBaseValListWillChangeTo(mBaseVal);
   }
   return rv;
 }
@@ -83,16 +84,17 @@ SVGAnimatedNumberList::ClearBaseValue(PR
 {
   DOMSVGAnimatedNumberList *domWrapper =
     DOMSVGAnimatedNumberList::GetDOMWrapperIfExists(this);
   if (domWrapper) {
     // We must send this notification *before* changing mBaseVal! (See above.)
     domWrapper->InternalBaseValListWillChangeTo(SVGNumberList());
   }
   mBaseVal.Clear();
+  mIsBaseSet = PR_FALSE;
   // Caller notifies
 }
 
 nsresult
 SVGAnimatedNumberList::SetAnimValue(const SVGNumberList& aNewAnimValue,
                                     nsSVGElement *aElement,
                                     PRUint32 aAttrEnum)
 {
--- a/content/svg/content/src/SVGAnimatedNumberList.h
+++ b/content/svg/content/src/SVGAnimatedNumberList.h
@@ -91,16 +91,24 @@ public:
 
   nsresult SetAnimValue(const SVGNumberList& aValue,
                         nsSVGElement *aElement,
                         PRUint32 aAttrEnum);
 
   void ClearAnimValue(nsSVGElement *aElement,
                       PRUint32 aAttrEnum);
 
+  // Returns PR_TRUE if the animated value of this list has been explicitly
+  // set (either by animation, or by taking on the base value which has been
+  // explicitly set by markup or a DOM call), PR_FALSE otherwise.
+  // If this returns PR_FALSE, the animated value is still valid, that is,
+  // useable, and represents the default base value of the attribute.
+  PRBool IsExplicitlySet() const
+    { return !!mAnimVal || mIsBaseSet; }
+  
   PRBool IsAnimating() const {
     return !!mAnimVal;
   }
 
 #ifdef MOZ_SMIL
   /// Callers own the returned nsISMILAttr
   nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement, PRUint8 aAttrEnum);
 #endif // MOZ_SMIL
@@ -109,16 +117,17 @@ private:
 
   // mAnimVal is a pointer to allow us to determine if we're being animated or
   // not. Making it a non-pointer member and using mAnimVal.IsEmpty() to check
   // if we're animating is not an option, since that would break animation *to*
   // the empty string (<set to="">).
 
   SVGNumberList mBaseVal;
   nsAutoPtr<SVGNumberList> mAnimVal;
+  PRPackedBool mIsBaseSet;
 
 #ifdef MOZ_SMIL
   struct SMILAnimatedNumberList : public nsISMILAttr
   {
   public:
     SMILAnimatedNumberList(SVGAnimatedNumberList* aVal,
                            nsSVGElement* aSVGElement,
                            PRUint8 aAttrEnum)
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGIntegerPairSMILType.cpp
@@ -0,0 +1,141 @@
+/* -*- 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 Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * 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 "SVGIntegerPairSMILType.h"
+#include "nsSMILValue.h"
+#include "nsMathUtils.h"
+#include "nsDebug.h"
+#include <math.h>
+
+namespace mozilla {
+  
+/*static*/ SVGIntegerPairSMILType SVGIntegerPairSMILType::sSingleton;
+
+void
+SVGIntegerPairSMILType::Init(nsSMILValue& aValue) const
+{
+  NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type");
+
+  aValue.mU.mIntPair[0] = 0;
+  aValue.mU.mIntPair[1] = 0;
+  aValue.mType = this;
+}
+
+void
+SVGIntegerPairSMILType::Destroy(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value");
+  aValue.mU.mIntPair[0] = 0;
+  aValue.mU.mIntPair[1] = 0;
+  aValue.mType = &nsSMILNullType::sSingleton;
+}
+
+nsresult
+SVGIntegerPairSMILType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
+{
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
+
+  aDest.mU.mIntPair[0] = aSrc.mU.mIntPair[0];
+  aDest.mU.mIntPair[1] = aSrc.mU.mIntPair[1];
+  return NS_OK;
+}
+
+PRBool
+SVGIntegerPairSMILType::IsEqual(const nsSMILValue& aLeft,
+                                const nsSMILValue& aRight) const
+{
+  NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value");
+
+  return aLeft.mU.mIntPair[0] == aRight.mU.mIntPair[0] &&
+         aLeft.mU.mIntPair[1] == aRight.mU.mIntPair[1];
+}
+
+nsresult
+SVGIntegerPairSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
+                            PRUint32 aCount) const
+{
+  NS_PRECONDITION(aValueToAdd.mType == aDest.mType,
+                  "Trying to add invalid types");
+  NS_PRECONDITION(aValueToAdd.mType == this, "Unexpected source type");
+
+  aDest.mU.mIntPair[0] += aValueToAdd.mU.mIntPair[0] * aCount;
+  aDest.mU.mIntPair[1] += aValueToAdd.mU.mIntPair[1] * aCount;
+
+  return NS_OK;
+}
+
+nsresult
+SVGIntegerPairSMILType::ComputeDistance(const nsSMILValue& aFrom,
+                                        const nsSMILValue& aTo,
+                                        double& aDistance) const
+{
+  NS_PRECONDITION(aFrom.mType == aTo.mType,"Trying to compare different types");
+  NS_PRECONDITION(aFrom.mType == this, "Unexpected source type");
+
+  double delta[2];
+  delta[0] = aTo.mU.mIntPair[0] - aFrom.mU.mIntPair[0];
+  delta[1] = aTo.mU.mIntPair[1] - aFrom.mU.mIntPair[1];
+
+  aDistance = NS_hypot(delta[0], delta[1]);
+  return NS_OK;
+}
+
+nsresult
+SVGIntegerPairSMILType::Interpolate(const nsSMILValue& aStartVal,
+                                    const nsSMILValue& aEndVal,
+                                    double aUnitDistance,
+                                    nsSMILValue& aResult) const
+{
+  NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
+                  "Trying to interpolate different types");
+  NS_PRECONDITION(aStartVal.mType == this,
+                  "Unexpected types for interpolation");
+  NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
+
+  double currentVal[2];
+  currentVal[0] = aStartVal.mU.mIntPair[0] +
+                  (aEndVal.mU.mIntPair[0] - aStartVal.mU.mIntPair[0]) * aUnitDistance;
+  currentVal[1] = aStartVal.mU.mIntPair[1] +
+                  (aEndVal.mU.mIntPair[1] - aStartVal.mU.mIntPair[1]) * aUnitDistance;
+
+  aResult.mU.mIntPair[0] = NS_lround(currentVal[0]);
+  aResult.mU.mIntPair[1] = NS_lround(currentVal[1]);
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGIntegerPairSMILType.h
@@ -0,0 +1,77 @@
+/* -*- 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 Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * 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 MOZILLA_SVGINTEGERPAIRSMILTYPE_H_
+#define MOZILLA_SVGINTEGERPAIRSMILTYPE_H_
+
+#include "nsISMILType.h"
+
+namespace mozilla {
+
+class SVGIntegerPairSMILType : public nsISMILType
+{
+public:
+  // Singleton for nsSMILValue objects to hold onto.
+  static SVGIntegerPairSMILType sSingleton;
+
+protected:
+  // nsISMILType Methods
+  // -------------------
+  virtual void     Init(nsSMILValue& aValue) const;
+  virtual void     Destroy(nsSMILValue&) const;
+  virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const;
+  virtual PRBool   IsEqual(const nsSMILValue& aLeft,
+                           const nsSMILValue& aRight) const;
+  virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
+                       PRUint32 aCount) const;
+  virtual nsresult ComputeDistance(const nsSMILValue& aFrom,
+                                   const nsSMILValue& aTo,
+                                   double& aDistance) const;
+  virtual nsresult Interpolate(const nsSMILValue& aStartVal,
+                               const nsSMILValue& aEndVal,
+                               double aUnitDistance,
+                               nsSMILValue& aResult) const;
+
+private:
+  // Private constructor & destructor: prevent instances beyond my singleton,
+  // and prevent others from deleting my singleton.
+  SVGIntegerPairSMILType()  {}
+  ~SVGIntegerPairSMILType() {}
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGINTEGERPAIRSMILTYPE_H_
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGNumberPairSMILType.cpp
@@ -0,0 +1,139 @@
+/* -*- 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 Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * 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 "SVGNumberPairSMILType.h"
+#include "nsSMILValue.h"
+#include "nsMathUtils.h"
+#include "nsDebug.h"
+#include <math.h>
+
+namespace mozilla {
+  
+/*static*/ SVGNumberPairSMILType SVGNumberPairSMILType::sSingleton;
+
+void
+SVGNumberPairSMILType::Init(nsSMILValue& aValue) const
+{
+  NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type");
+
+  aValue.mU.mNumberPair[0] = 0;
+  aValue.mU.mNumberPair[1] = 0;
+  aValue.mType = this;
+}
+
+void
+SVGNumberPairSMILType::Destroy(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value");
+  aValue.mU.mNumberPair[0] = 0;
+  aValue.mU.mNumberPair[1] = 0;
+  aValue.mType = &nsSMILNullType::sSingleton;
+}
+
+nsresult
+SVGNumberPairSMILType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
+{
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
+
+  aDest.mU.mNumberPair[0] = aSrc.mU.mNumberPair[0];
+  aDest.mU.mNumberPair[1] = aSrc.mU.mNumberPair[1];
+  return NS_OK;
+}
+
+PRBool
+SVGNumberPairSMILType::IsEqual(const nsSMILValue& aLeft,
+                               const nsSMILValue& aRight) const
+{
+  NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value");
+
+  return aLeft.mU.mNumberPair[0] == aRight.mU.mNumberPair[0] &&
+         aLeft.mU.mNumberPair[1] == aRight.mU.mNumberPair[1];
+}
+
+nsresult
+SVGNumberPairSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
+                           PRUint32 aCount) const
+{
+  NS_PRECONDITION(aValueToAdd.mType == aDest.mType,
+                  "Trying to add invalid types");
+  NS_PRECONDITION(aValueToAdd.mType == this, "Unexpected source type");
+
+  aDest.mU.mNumberPair[0] += aValueToAdd.mU.mNumberPair[0] * aCount;
+  aDest.mU.mNumberPair[1] += aValueToAdd.mU.mNumberPair[1] * aCount;
+
+  return NS_OK;
+}
+
+nsresult
+SVGNumberPairSMILType::ComputeDistance(const nsSMILValue& aFrom,
+                                       const nsSMILValue& aTo,
+                                       double& aDistance) const
+{
+  NS_PRECONDITION(aFrom.mType == aTo.mType,"Trying to compare different types");
+  NS_PRECONDITION(aFrom.mType == this, "Unexpected source type");
+
+  double delta[2];
+  delta[0] = aTo.mU.mNumberPair[0] - aFrom.mU.mNumberPair[0];
+  delta[1] = aTo.mU.mNumberPair[1] - aFrom.mU.mNumberPair[1];
+
+  aDistance = NS_hypot(delta[0], delta[1]);
+  return NS_OK;
+}
+
+nsresult
+SVGNumberPairSMILType::Interpolate(const nsSMILValue& aStartVal,
+                                   const nsSMILValue& aEndVal,
+                                   double aUnitDistance,
+                                   nsSMILValue& aResult) const
+{
+  NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
+                  "Trying to interpolate different types");
+  NS_PRECONDITION(aStartVal.mType == this,
+                  "Unexpected types for interpolation");
+  NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
+
+  aResult.mU.mNumberPair[0] =
+    float(aStartVal.mU.mNumberPair[0] +
+          (aEndVal.mU.mNumberPair[0] - aStartVal.mU.mNumberPair[0]) * aUnitDistance);
+  aResult.mU.mNumberPair[1] =
+    float(aStartVal.mU.mNumberPair[1] +
+          (aEndVal.mU.mNumberPair[1] - aStartVal.mU.mNumberPair[1]) * aUnitDistance);
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGNumberPairSMILType.h
@@ -0,0 +1,77 @@
+/* -*- 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 Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * 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 MOZILLA_SVGNUMBERPAIRSMILTYPE_H_
+#define MOZILLA_SVGNUMBERPAIRSMILTYPE_H_
+
+#include "nsISMILType.h"
+
+namespace mozilla {
+
+class SVGNumberPairSMILType : public nsISMILType
+{
+public:
+  // Singleton for nsSMILValue objects to hold onto.
+  static SVGNumberPairSMILType sSingleton;
+
+protected:
+  // nsISMILType Methods
+  // -------------------
+  virtual void     Init(nsSMILValue& aValue) const;
+  virtual void     Destroy(nsSMILValue&) const;
+  virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const;
+  virtual PRBool   IsEqual(const nsSMILValue& aLeft,
+                           const nsSMILValue& aRight) const;
+  virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
+                       PRUint32 aCount) const;
+  virtual nsresult ComputeDistance(const nsSMILValue& aFrom,
+                                   const nsSMILValue& aTo,
+                                   double& aDistance) const;
+  virtual nsresult Interpolate(const nsSMILValue& aStartVal,
+                               const nsSMILValue& aEndVal,
+                               double aUnitDistance,
+                               nsSMILValue& aResult) const;
+
+private:
+  // Private constructor & destructor: prevent instances beyond my singleton,
+  // and prevent others from deleting my singleton.
+  SVGNumberPairSMILType()  {}
+  ~SVGNumberPairSMILType() {}
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGNUMBERPAIRSMILTYPE_H_
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -61,17 +61,19 @@
 #include "nsCSSParser.h"
 #include "nsGenericHTMLElement.h"
 #include "nsNodeInfoManager.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsEventListenerManager.h"
 #include "nsSVGUtils.h"
 #include "nsSVGLength2.h"
 #include "nsSVGNumber2.h"
+#include "nsSVGNumberPair.h"
 #include "nsSVGInteger.h"
+#include "nsSVGIntegerPair.h"
 #include "nsSVGAngle.h"
 #include "nsSVGBoolean.h"
 #include "nsSVGEnum.h"
 #include "nsSVGViewBox.h"
 #include "nsSVGString.h"
 #include "nsSVGClass.h"
 #include "SVGAnimatedNumberList.h"
 #include "SVGAnimatedLengthList.h"
@@ -131,22 +133,34 @@ nsSVGElement::Init()
   }
 
   NumberAttributesInfo numberInfo = GetNumberInfo();
 
   for (i = 0; i < numberInfo.mNumberCount; i++) {
     numberInfo.Reset(i);
   }
 
+  NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
+
+  for (i = 0; i < numberPairInfo.mNumberPairCount; i++) {
+    numberPairInfo.Reset(i);
+  }
+
   IntegerAttributesInfo integerInfo = GetIntegerInfo();
 
   for (i = 0; i < integerInfo.mIntegerCount; i++) {
     integerInfo.Reset(i);
   }
 
+  IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
+
+  for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) {
+    integerPairInfo.Reset(i);
+  }
+
   AngleAttributesInfo angleInfo = GetAngleInfo();
 
   for (i = 0; i < angleInfo.mAngleCount; i++) {
     angleInfo.Reset(i);
   }
 
   BooleanAttributesInfo booleanInfo = GetBooleanInfo();
 
@@ -433,58 +447,72 @@ nsSVGElement::ParseAttribute(PRInt32 aNa
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGNumber2 attribute
       NumberAttributesInfo numberInfo = GetNumberInfo();
       for (i = 0; i < numberInfo.mNumberCount; i++) {
         if (aAttribute == *numberInfo.mNumberInfo[i].mName) {
-          if (i + 1 < numberInfo.mNumberCount &&
-              aAttribute == *numberInfo.mNumberInfo[i + 1].mName) {
-            rv = ParseNumberOptionalNumber(aValue, i, i + 1);
-            if (NS_FAILED(rv)) {
-              numberInfo.Reset(i + 1);
-            }
-          } else {
-            rv = numberInfo.mNumbers[i].SetBaseValueString(aValue, this, PR_FALSE);
-          }
+          rv = numberInfo.mNumbers[i].SetBaseValueString(aValue, this, PR_FALSE);
           if (NS_FAILED(rv)) {
             numberInfo.Reset(i);
           }
           foundMatch = PR_TRUE;
           break;
         }
       }
     }
 
     if (!foundMatch) {
+      // Check for nsSVGNumberPair attribute
+      NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
+      for (i = 0; i < numberPairInfo.mNumberPairCount; i++) {
+        if (aAttribute == *numberPairInfo.mNumberPairInfo[i].mName) {
+          rv = numberPairInfo.mNumberPairs[i].SetBaseValueString(aValue, this, PR_FALSE);
+          if (NS_FAILED(rv)) {
+            numberPairInfo.Reset(i);
+          }
+          foundMatch = PR_TRUE;
+          break;
+        }
+      }
+    }
+
+    if (!foundMatch) {
       // Check for nsSVGInteger attribute
       IntegerAttributesInfo integerInfo = GetIntegerInfo();
       for (i = 0; i < integerInfo.mIntegerCount; i++) {
         if (aAttribute == *integerInfo.mIntegerInfo[i].mName) {
-          if (i + 1 < integerInfo.mIntegerCount &&
-              aAttribute == *integerInfo.mIntegerInfo[i + 1].mName) {
-            rv = ParseIntegerOptionalInteger(aValue, i, i + 1);
-            if (NS_FAILED(rv)) {
-              integerInfo.Reset(i + 1);
-            }
-          } else {
-            rv = integerInfo.mIntegers[i].SetBaseValueString(aValue, this, PR_FALSE);
-          }
+          rv = integerInfo.mIntegers[i].SetBaseValueString(aValue, this, PR_FALSE);
           if (NS_FAILED(rv)) {
             integerInfo.Reset(i);
           }
           foundMatch = PR_TRUE;
           break;
         }
       }
     }
 
     if (!foundMatch) {
+      // Check for nsSVGIntegerPair attribute
+      IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
+      for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) {
+        if (aAttribute == *integerPairInfo.mIntegerPairInfo[i].mName) {
+          rv = integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this, PR_FALSE);
+          if (NS_FAILED(rv)) {
+            integerPairInfo.Reset(i);
+          }
+          foundMatch = PR_TRUE;
+          break;
+        }
+      }
+    }
+
+    if (!foundMatch) {
       // Check for nsSVGAngle attribute
       AngleAttributesInfo angleInfo = GetAngleInfo();
       for (i = 0; i < angleInfo.mAngleCount; i++) {
         if (aAttribute == *angleInfo.mAngleInfo[i].mName) {
           rv = angleInfo.mAngles[i].SetBaseValueString(aValue, this, PR_FALSE);
           if (NS_FAILED(rv)) {
             angleInfo.Reset(i);
           }
@@ -658,45 +686,55 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespa
       }
     }
 
     // Check if this is a number attribute going away
     NumberAttributesInfo numInfo = GetNumberInfo();
 
     for (PRUint32 i = 0; i < numInfo.mNumberCount; i++) {
       if (aName == *numInfo.mNumberInfo[i].mName) {
-        if (i + 1 < numInfo.mNumberCount &&
-            aName == *numInfo.mNumberInfo[i + 1].mName) {
-          // found a number-optional-number
-          numInfo.Reset(i + 1);
-          DidChangeNumber(i + 1, PR_FALSE);
-        }
         numInfo.Reset(i);
         DidChangeNumber(i, PR_FALSE);
         return rv;
       }
     }
 
+    // Check if this is a number pair attribute going away
+    NumberPairAttributesInfo numPairInfo = GetNumberPairInfo();
+
+    for (PRUint32 i = 0; i < numPairInfo.mNumberPairCount; i++) {
+      if (aName == *numPairInfo.mNumberPairInfo[i].mName) {
+        numPairInfo.Reset(i);
+        DidChangeNumberPair(i, PR_FALSE);
+        return rv;
+      }
+    }
+
     // Check if this is an integer attribute going away
     IntegerAttributesInfo intInfo = GetIntegerInfo();
 
     for (PRUint32 i = 0; i < intInfo.mIntegerCount; i++) {
       if (aName == *intInfo.mIntegerInfo[i].mName) {
-        if (i + 1 < intInfo.mIntegerCount &&
-            aName == *intInfo.mIntegerInfo[i + 1].mName) {
-          // found a number-optional-number
-          intInfo.Reset(i + 1);
-          DidChangeNumber(i + 1, PR_FALSE);
-        }
         intInfo.Reset(i);
         DidChangeInteger(i, PR_FALSE);
         return rv;
       }
     }
 
+    // Check if this is an integer pair attribute going away
+    IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo();
+
+    for (PRUint32 i = 0; i < intPairInfo.mIntegerPairCount; i++) {
+      if (aName == *intPairInfo.mIntegerPairInfo[i].mName) {
+        intPairInfo.Reset(i);
+        DidChangeIntegerPair(i, PR_FALSE);
+        return rv;
+      }
+    }
+
     // Check if this is an angle attribute going away
     AngleAttributesInfo angleInfo = GetAngleInfo();
 
     for (PRUint32 i = 0; i < angleInfo.mAngleCount; i++) {
       if (aName == *angleInfo.mAngleInfo[i].mName) {
         angleInfo.Reset(i);
         DidChangeAngle(i, PR_FALSE);
         return rv;
@@ -1802,16 +1840,63 @@ nsSVGElement::GetAnimatedNumberValues(fl
 
   while (f && i < info.mNumberCount) {
     *f = info.mNumbers[i++].GetAnimValue();
     f = va_arg(args, float*);
   }
   va_end(args);
 }
 
+nsSVGElement::NumberPairAttributesInfo
+nsSVGElement::GetNumberPairInfo()
+{
+  return NumberPairAttributesInfo(nsnull, nsnull, 0);
+}
+
+void nsSVGElement::NumberPairAttributesInfo::Reset(PRUint8 aAttrEnum)
+{
+  mNumberPairs[aAttrEnum].Init(aAttrEnum,
+                               mNumberPairInfo[aAttrEnum].mDefaultValue1,
+                               mNumberPairInfo[aAttrEnum].mDefaultValue2);
+}
+
+void
+nsSVGElement::DidChangeNumberPair(PRUint8 aAttrEnum, PRBool aDoSetAttr)
+{
+  if (!aDoSetAttr)
+    return;
+
+  NumberPairAttributesInfo info = GetNumberPairInfo();
+
+  NS_ASSERTION(info.mNumberPairCount > 0,
+               "DidChangePairNumber on element with no number pair attribs");
+
+  NS_ASSERTION(aAttrEnum < info.mNumberPairCount, "aAttrEnum out of range");
+
+  nsAutoString serializedValue;
+  info.mNumberPairs[aAttrEnum].GetBaseValueString(serializedValue);
+
+  nsAttrValue attrValue(serializedValue);
+  SetParsedAttr(kNameSpaceID_None, *info.mNumberPairInfo[aAttrEnum].mName, nsnull,
+                attrValue, PR_TRUE);
+}
+
+void
+nsSVGElement::DidAnimateNumberPair(PRUint8 aAttrEnum)
+{
+  nsIFrame* frame = GetPrimaryFrame();
+
+  if (frame) {
+    NumberPairAttributesInfo info = GetNumberPairInfo();
+    frame->AttributeChanged(kNameSpaceID_None,
+                            *info.mNumberPairInfo[aAttrEnum].mName,
+                            nsIDOMMutationEvent::MODIFICATION);
+  }
+}
+
 nsSVGElement::IntegerAttributesInfo
 nsSVGElement::GetIntegerInfo()
 {
   return IntegerAttributesInfo(nsnull, nsnull, 0);
 }
 
 void nsSVGElement::IntegerAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
@@ -1869,16 +1954,63 @@ nsSVGElement::GetAnimatedIntegerValues(P
 
   while (n && i < info.mIntegerCount) {
     *n = info.mIntegers[i++].GetAnimValue();
     n = va_arg(args, PRInt32*);
   }
   va_end(args);
 }
 
+nsSVGElement::IntegerPairAttributesInfo
+nsSVGElement::GetIntegerPairInfo()
+{
+  return IntegerPairAttributesInfo(nsnull, nsnull, 0);
+}
+
+void nsSVGElement::IntegerPairAttributesInfo::Reset(PRUint8 aAttrEnum)
+{
+  mIntegerPairs[aAttrEnum].Init(aAttrEnum,
+                                mIntegerPairInfo[aAttrEnum].mDefaultValue1,
+                                mIntegerPairInfo[aAttrEnum].mDefaultValue2);
+}
+
+void
+nsSVGElement::DidChangeIntegerPair(PRUint8 aAttrEnum, PRBool aDoSetAttr)
+{
+  if (!aDoSetAttr)
+    return;
+
+  IntegerPairAttributesInfo info = GetIntegerPairInfo();
+
+  NS_ASSERTION(info.mIntegerPairCount > 0,
+               "DidChangeIntegerPair on element with no integer pair attribs");
+
+  NS_ASSERTION(aAttrEnum < info.mIntegerPairCount, "aAttrEnum out of range");
+
+  nsAutoString serializedValue;
+  info.mIntegerPairs[aAttrEnum].GetBaseValueString(serializedValue);
+
+  nsAttrValue attrValue(serializedValue);
+  SetParsedAttr(kNameSpaceID_None, *info.mIntegerPairInfo[aAttrEnum].mName, nsnull,
+                attrValue, PR_TRUE);
+}
+
+void
+nsSVGElement::DidAnimateIntegerPair(PRUint8 aAttrEnum)
+{
+  nsIFrame* frame = GetPrimaryFrame();
+  
+  if (frame) {
+    IntegerPairAttributesInfo info = GetIntegerPairInfo();
+    frame->AttributeChanged(kNameSpaceID_None,
+                            *info.mIntegerPairInfo[aAttrEnum].mName,
+                            nsIDOMMutationEvent::MODIFICATION);
+  }
+}
+
 nsSVGElement::AngleAttributesInfo
 nsSVGElement::GetAngleInfo()
 {
   return AngleAttributesInfo(nsnull, nsnull, 0);
 }
 
 void nsSVGElement::AngleAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
@@ -2162,99 +2294,16 @@ nsSVGElement::DidAnimateClass()
 
   if (frame) {
     frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::_class,
                             nsIDOMMutationEvent::MODIFICATION);
   }
 }
 
 nsresult
-nsSVGElement::ParseNumberOptionalNumber(const nsAString& aValue,
-                                        PRUint32 aIndex1, PRUint32 aIndex2)
-{
-  NS_ConvertUTF16toUTF8 value(aValue);
-  const char *str = value.get();
-
-  if (NS_IsAsciiWhitespace(*str))
-    return NS_ERROR_FAILURE;
-
-  char *rest;
-  float x = float(PR_strtod(str, &rest));
-  float y = x;
-
-  if (str == rest || !NS_FloatIsFinite(x)) {
-    //first value was illformed
-    return NS_ERROR_FAILURE;
-  }
-  
-  if (*rest != '\0') {
-    while (NS_IsAsciiWhitespace(*rest)) {
-      ++rest;
-    }
-    if (*rest == ',') {
-      ++rest;
-    }
-
-    y = float(PR_strtod(rest, &rest));
-    if (*rest != '\0' || !NS_FloatIsFinite(y)) {
-      //second value was illformed or there was trailing content
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  NumberAttributesInfo numberInfo = GetNumberInfo();
-
-  numberInfo.mNumbers[aIndex1].SetBaseValue(x, this, PR_FALSE);
-  numberInfo.mNumbers[aIndex2].SetBaseValue(y, this, PR_FALSE);
-  return NS_OK;
-}
-
-nsresult
-nsSVGElement::ParseIntegerOptionalInteger(const nsAString& aValue,
-                                          PRUint32 aIndex1, PRUint32 aIndex2)
-{
-  NS_ConvertUTF16toUTF8 value(aValue);
-  const char *str = value.get();
-
-  if (NS_IsAsciiWhitespace(*str))
-    return NS_ERROR_FAILURE;
-
-  char *rest;
-  PRInt32 x = strtol(str, &rest, 10);
-  PRInt32 y = x;
-
-  if (str == rest) {
-    //first value was illformed
-    return NS_ERROR_FAILURE;
-  }
-  
-  if (*rest != '\0') {
-    while (NS_IsAsciiWhitespace(*rest)) {
-      ++rest;
-    }
-    if (*rest == ',') {
-      ++rest;
-    }
-
-    y = strtol(rest, &rest, 10);
-    if (*rest != '\0') {
-      //second value was illformed or there was trailing content
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  IntegerAttributesInfo integerInfo = GetIntegerInfo();
-
-  integerInfo.mIntegers[aIndex1].SetBaseValue(x, this, PR_FALSE);
-  integerInfo.mIntegers[aIndex2].SetBaseValue(y, this, PR_FALSE);
-
-  return NS_OK;
-}
-
-nsresult
 nsSVGElement::ReportAttributeParseFailure(nsIDocument* aDocument,
                                           nsIAtom* aAttribute,
                                           const nsAString& aValue)
 {
   const nsAFlatString& attributeValue = PromiseFlatString(aValue);
   const PRUnichar *strings[] = { aAttribute->GetUTF16String(),
                                  attributeValue.get() };
   return nsSVGUtils::ReportToConsole(aDocument,
@@ -2338,40 +2387,52 @@ nsSVGElement::GetAnimatedAttr(PRInt32 aN
         return info.mLengths[i].ToSMILAttr(this);
       }
     }
 
     // Numbers:
     {
       NumberAttributesInfo info = GetNumberInfo();
       for (PRUint32 i = 0; i < info.mNumberCount; i++) {
-        // XXX this isn't valid for either of the two properties corresponding to
-        // attributes of type <number-optional-number> - see filter,
-        // feConvolveMatrix, feDiffuseLighting, feGaussianBlur, feMorphology and
-        // feTurbulence.
-        // The way to fix this is probably to handle them as 2-item number lists
-        // once we implement number list animation, and put the number list loop
-        // *above* this one at that time to catch those properties before we get
-        // here. The separate properties should then point into the list.
         if (aName == *info.mNumberInfo[i].mName) {
           return info.mNumbers[i].ToSMILAttr(this);
         }
       }
     }
 
+    // Number Pairs:
+    {
+      NumberPairAttributesInfo info = GetNumberPairInfo();
+      for (PRUint32 i = 0; i < info.mNumberPairCount; i++) {
+        if (aName == *info.mNumberPairInfo[i].mName) {
+          return info.mNumberPairs[i].ToSMILAttr(this);
+        }
+      }
+    }
+
     // Integers:
     {
       IntegerAttributesInfo info = GetIntegerInfo();
       for (PRUint32 i = 0; i < info.mIntegerCount; i++) {
         if (aName == *info.mIntegerInfo[i].mName) {
           return info.mIntegers[i].ToSMILAttr(this);
         }
       }
     }
 
+    // Integer Pairs:
+    {
+      IntegerPairAttributesInfo info = GetIntegerPairInfo();
+      for (PRUint32 i = 0; i < info.mIntegerPairCount; i++) {
+        if (aName == *info.mIntegerPairInfo[i].mName) {
+          return info.mIntegerPairs[i].ToSMILAttr(this);
+        }
+      }
+    }
+
     // Enumerations:
     {
       EnumAttributesInfo info = GetEnumInfo();
       for (PRUint32 i = 0; i < info.mEnumCount; i++) {
         if (aName == *info.mEnumInfo[i].mName) {
           return info.mEnums[i].ToSMILAttr(this);
         }
       }
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -56,17 +56,19 @@
 #ifdef MOZ_SMIL
 #include "nsISMILAttr.h"
 #include "nsSMILAnimationController.h"
 #endif
 
 class nsSVGSVGElement;
 class nsSVGLength2;
 class nsSVGNumber2;
+class nsSVGNumberPair;
 class nsSVGInteger;
+class nsSVGIntegerPair;
 class nsSVGAngle;
 class nsSVGBoolean;
 class nsSVGEnum;
 struct nsSVGEnumMapping;
 class nsSVGViewBox;
 class nsSVGString;
 class nsSVGClass;
 struct gfxMatrix;
@@ -169,31 +171,35 @@ public:
     return GetStringInfo().mStringInfo[aAttrEnum].mIsAnimatable;
   }
   PRBool NumberAttrAllowsPercentage(PRUint8 aAttrEnum) {
     return GetNumberInfo().mNumberInfo[aAttrEnum].mPercentagesAllowed;
   }
   void SetLength(nsIAtom* aName, const nsSVGLength2 &aLength);
   virtual void DidChangeLength(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeNumber(PRUint8 aAttrEnum, PRBool aDoSetAttr);
+  virtual void DidChangeNumberPair(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeInteger(PRUint8 aAttrEnum, PRBool aDoSetAttr);
+  virtual void DidChangeIntegerPair(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeAngle(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeBoolean(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeEnum(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeViewBox(PRBool aDoSetAttr);
   virtual void DidChangePreserveAspectRatio(PRBool aDoSetAttr);
   virtual void DidChangeNumberList(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeLengthList(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangePointList(PRBool aDoSetAttr);
   virtual void DidChangePathSegList(PRBool aDoSetAttr);
   virtual void DidChangeString(PRUint8 aAttrEnum) {}
 
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateNumber(PRUint8 aAttrEnum);
+  virtual void DidAnimateNumberPair(PRUint8 aAttrEnum);
   virtual void DidAnimateInteger(PRUint8 aAttrEnum);
+  virtual void DidAnimateIntegerPair(PRUint8 aAttrEnum);
   virtual void DidAnimateAngle(PRUint8 aAttrEnum);
   virtual void DidAnimateBoolean(PRUint8 aAttrEnum);
   virtual void DidAnimateEnum(PRUint8 aAttrEnum);
   virtual void DidAnimateViewBox();
   virtual void DidAnimatePreserveAspectRatio();
   virtual void DidAnimateNumberList(PRUint8 aAttrEnum);
   virtual void DidAnimateLengthList(PRUint8 aAttrEnum);
   virtual void DidAnimatePointList();
@@ -301,16 +307,37 @@ protected:
                          NumberInfo *aNumberInfo,
                          PRUint32 aNumberCount) :
       mNumbers(aNumbers), mNumberInfo(aNumberInfo), mNumberCount(aNumberCount)
       {}
 
     void Reset(PRUint8 aAttrEnum);
   };
 
+  struct NumberPairInfo {
+    nsIAtom** mName;
+    float     mDefaultValue1;
+    float     mDefaultValue2;
+  };
+
+  struct NumberPairAttributesInfo {
+    nsSVGNumberPair* mNumberPairs;
+    NumberPairInfo*  mNumberPairInfo;
+    PRUint32         mNumberPairCount;
+
+    NumberPairAttributesInfo(nsSVGNumberPair *aNumberPairs,
+                             NumberPairInfo *aNumberPairInfo,
+                             PRUint32 aNumberPairCount) :
+      mNumberPairs(aNumberPairs), mNumberPairInfo(aNumberPairInfo),
+      mNumberPairCount(aNumberPairCount)
+      {}
+
+    void Reset(PRUint8 aAttrEnum);
+  };
+
   struct IntegerInfo {
     nsIAtom** mName;
     PRInt32   mDefaultValue;
   };
 
   struct IntegerAttributesInfo {
     nsSVGInteger* mIntegers;
     IntegerInfo*  mIntegerInfo;
@@ -320,16 +347,37 @@ protected:
                           IntegerInfo *aIntegerInfo,
                           PRUint32 aIntegerCount) :
       mIntegers(aIntegers), mIntegerInfo(aIntegerInfo), mIntegerCount(aIntegerCount)
       {}
 
     void Reset(PRUint8 aAttrEnum);
   };
 
+  struct IntegerPairInfo {
+    nsIAtom** mName;
+    PRInt32   mDefaultValue1;
+    PRInt32   mDefaultValue2;
+  };
+
+  struct IntegerPairAttributesInfo {
+    nsSVGIntegerPair* mIntegerPairs;
+    IntegerPairInfo*  mIntegerPairInfo;
+    PRUint32          mIntegerPairCount;
+
+    IntegerPairAttributesInfo(nsSVGIntegerPair *aIntegerPairs,
+                              IntegerPairInfo *aIntegerPairInfo,
+                              PRUint32 aIntegerPairCount) :
+      mIntegerPairs(aIntegerPairs), mIntegerPairInfo(aIntegerPairInfo),
+      mIntegerPairCount(aIntegerPairCount)
+      {}
+
+    void Reset(PRUint8 aAttrEnum);
+  };
+
   struct AngleInfo {
     nsIAtom** mName;
     float     mDefaultValue;
     PRUint8   mDefaultUnitType;
   };
 
   struct AngleAttributesInfo {
     nsSVGAngle* mAngles;
@@ -453,42 +501,34 @@ protected:
       mStrings(aStrings), mStringInfo(aStringInfo), mStringCount(aStringCount)
       {}
 
     void Reset(PRUint8 aAttrEnum);
   };
 
   virtual LengthAttributesInfo GetLengthInfo();
   virtual NumberAttributesInfo GetNumberInfo();
+  virtual NumberPairAttributesInfo GetNumberPairInfo();
   virtual IntegerAttributesInfo GetIntegerInfo();
+  virtual IntegerPairAttributesInfo GetIntegerPairInfo();
   virtual AngleAttributesInfo GetAngleInfo();
   virtual BooleanAttributesInfo GetBooleanInfo();
   virtual EnumAttributesInfo GetEnumInfo();
   // We assume all viewboxes and preserveAspectRatios are alike
   // so we don't need to wrap the class
   virtual nsSVGViewBox *GetViewBox();
   virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio();
   virtual NumberListAttributesInfo GetNumberListInfo();
   virtual LengthListAttributesInfo GetLengthListInfo();
   virtual StringAttributesInfo GetStringInfo();
   virtual nsSVGClass *GetClass();
 
   static nsSVGEnumMapping sSVGUnitTypesMap[];
 
 private:
-  /* read <number-optional-number> */
-  nsresult
-  ParseNumberOptionalNumber(const nsAString& aValue,
-                            PRUint32 aIndex1, PRUint32 aIndex2);
-
-  /* read <integer-optional-integer> */
-  nsresult
-  ParseIntegerOptionalInteger(const nsAString& aValue,
-                              PRUint32 aIndex1, PRUint32 aIndex2);
-
   void ResetOldStyleBaseType(nsISVGValue *svg_value);
 
   struct ObservableModificationData {
     // Only to be used if |name| is non-null.  Otherwise, modType will
     // be 0 to indicate NS_OK should be returned and 1 to indicate
     // NS_ERROR_UNEXPECTED should be returned.
     ObservableModificationData(const nsAttrName* aName, PRUint32 aModType):
       name(aName), modType(aModType)
--- a/content/svg/content/src/nsSVGFilterElement.cpp
+++ b/content/svg/content/src/nsSVGFilterElement.cpp
@@ -42,19 +42,18 @@
 nsSVGElement::LengthInfo nsSVGFilterElement::sLengthInfo[4] =
 {
   { &nsGkAtoms::x, -10, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, nsSVGUtils::X },
   { &nsGkAtoms::y, -10, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, nsSVGUtils::Y },
   { &nsGkAtoms::width, 120, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, nsSVGUtils::X },
   { &nsGkAtoms::height, 120, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, nsSVGUtils::Y },
 };
 
-nsSVGElement::IntegerInfo nsSVGFilterElement::sIntegerInfo[2] =
+nsSVGElement::IntegerPairInfo nsSVGFilterElement::sIntegerPairInfo[1] =
 {
-  { &nsGkAtoms::filterRes, 0 },
   { &nsGkAtoms::filterRes, 0 }
 };
 
 nsSVGElement::EnumInfo nsSVGFilterElement::sEnumInfo[2] =
 {
   { &nsGkAtoms::filterUnits,
     sSVGUnitTypesMap,
     nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
@@ -139,32 +138,35 @@ NS_IMETHODIMP nsSVGFilterElement::GetFil
 NS_IMETHODIMP nsSVGFilterElement::GetPrimitiveUnits(nsIDOMSVGAnimatedEnumeration * *aUnits)
 {
   return mEnumAttributes[PRIMITIVEUNITS].ToDOMAnimatedEnum(aUnits, this);
 }
 
 /* readonly attribute nsIDOMSVGAnimatedEnumeration filterResY; */
 NS_IMETHODIMP nsSVGFilterElement::GetFilterResX(nsIDOMSVGAnimatedInteger * *aFilterResX)
 {
-  return mIntegerAttributes[FILTERRES_X].ToDOMAnimatedInteger(aFilterResX, this);
+  return mIntegerPairAttributes[FILTERRES].ToDOMAnimatedInteger(aFilterResX,
+                                                                nsSVGIntegerPair::eFirst,
+                                                                this);
 }
 
 /* readonly attribute nsIDOMSVGAnimatedEnumeration filterResY; */
 NS_IMETHODIMP nsSVGFilterElement::GetFilterResY(nsIDOMSVGAnimatedInteger * *aFilterResY)
 {
-  return mIntegerAttributes[FILTERRES_Y].ToDOMAnimatedInteger(aFilterResY, this);
+  return mIntegerPairAttributes[FILTERRES].ToDOMAnimatedInteger(aFilterResY,
+                                                                nsSVGIntegerPair::eSecond,
+                                                                this);
 }
 
 /* void setFilterRes (in unsigned long filterResX, in unsigned long filterResY);
  */
 NS_IMETHODIMP
 nsSVGFilterElement::SetFilterRes(PRUint32 filterResX, PRUint32 filterResY)
 {
-  mIntegerAttributes[FILTERRES_X].SetBaseValue(filterResX, this, PR_FALSE);
-  mIntegerAttributes[FILTERRES_Y].SetBaseValue(filterResY, this, PR_FALSE);
+  mIntegerPairAttributes[FILTERRES].SetBaseValues(filterResX, filterResY, this, PR_FALSE);
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 // nsIDOMSVGURIReference methods
 
 /* readonly attribute nsIDOMSVGAnimatedString href; */
 NS_IMETHODIMP 
@@ -214,21 +216,21 @@ nsSVGFilterElement::Invalidate()
 
 nsSVGElement::LengthAttributesInfo
 nsSVGFilterElement::GetLengthInfo()
 {
   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
                               NS_ARRAY_LENGTH(sLengthInfo));
 }
 
-nsSVGElement::IntegerAttributesInfo
-nsSVGFilterElement::GetIntegerInfo()
+nsSVGElement::IntegerPairAttributesInfo
+nsSVGFilterElement::GetIntegerPairInfo()
 {
-  return IntegerAttributesInfo(mIntegerAttributes, sIntegerInfo,
-                               NS_ARRAY_LENGTH(sIntegerInfo));
+  return IntegerPairAttributesInfo(mIntegerPairAttributes, sIntegerPairInfo,
+                                   NS_ARRAY_LENGTH(sIntegerPairInfo));
 }
 
 nsSVGElement::EnumAttributesInfo
 nsSVGFilterElement::GetEnumInfo()
 {
   return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
                             NS_ARRAY_LENGTH(sEnumInfo));
 }
--- a/content/svg/content/src/nsSVGFilterElement.h
+++ b/content/svg/content/src/nsSVGFilterElement.h
@@ -37,17 +37,17 @@
 #ifndef __NS_SVGFILTERELEMENT_H__
 #define __NS_SVGFILTERELEMENT_H__
 
 #include "nsSVGGraphicElement.h"
 #include "nsIDOMSVGFilterElement.h"
 #include "nsIDOMSVGURIReference.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsSVGLength2.h"
-#include "nsSVGInteger.h"
+#include "nsSVGIntegerPair.h"
 #include "nsSVGEnum.h"
 #include "nsSVGString.h"
 
 typedef nsSVGGraphicElement nsSVGFilterElementBase;
 
 class nsSVGFilterElement : public nsSVGFilterElementBase,
                            public nsIDOMSVGFilterElement,
                            public nsIDOMSVGURIReference,
@@ -79,30 +79,30 @@ public:
 
   // Invalidate users of this filter
   void Invalidate();
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
 
   virtual LengthAttributesInfo GetLengthInfo();
-  virtual IntegerAttributesInfo GetIntegerInfo();
+  virtual IntegerPairAttributesInfo GetIntegerPairInfo();
   virtual EnumAttributesInfo GetEnumInfo();
   virtual StringAttributesInfo GetStringInfo();
 
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateEnum(PRUint8 aAttrEnum);
 
   enum { X, Y, WIDTH, HEIGHT };
   nsSVGLength2 mLengthAttributes[4];
   static LengthInfo sLengthInfo[4];
 
-  enum { FILTERRES_X, FILTERRES_Y };
-  nsSVGInteger mIntegerAttributes[2];
-  static IntegerInfo sIntegerInfo[2];
+  enum { FILTERRES };
+  nsSVGIntegerPair mIntegerPairAttributes[1];
+  static IntegerPairInfo sIntegerPairInfo[1];
 
   enum { FILTERUNITS, PRIMITIVEUNITS };
   nsSVGEnum mEnumAttributes[2];
   static EnumInfo sEnumInfo[2];
 
   enum { HREF };
   nsSVGString mStringAttributes[1];
   static StringInfo sStringInfo[1];
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -32,17 +32,19 @@
  * 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 "nsSVGElement.h"
 #include "nsGkAtoms.h"
 #include "nsSVGNumber2.h"
+#include "nsSVGNumberPair.h"
 #include "nsSVGInteger.h"
+#include "nsSVGIntegerPair.h"
 #include "nsSVGBoolean.h"
 #include "nsIDOMSVGFilters.h"
 #include "nsCOMPtr.h"
 #include "nsSVGFilterInstance.h"
 #include "nsSVGValue.h"
 #include "nsISVGValueObserver.h"
 #include "nsWeakReference.h"
 #include "nsIDOMSVGFilterElement.h"
@@ -131,40 +133,37 @@ NS_INTERFACE_MAP_END_INHERITING(nsSVGFEB
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsSVGFE::ScaleInfo
 nsSVGFE::SetupScalingFilter(nsSVGFilterInstance *aInstance,
                             const Image *aSource, const Image *aTarget,
                             const nsIntRect& aDataRect,
-                            nsSVGNumber2 *aUnitX, nsSVGNumber2 *aUnitY)
+                            nsSVGNumberPair *aKernelUnitLength)
 {
   ScaleInfo result;
-  result.mRescaling = HasAttr(kNameSpaceID_None, nsGkAtoms::kernelUnitLength);
+  result.mRescaling = aKernelUnitLength->IsExplicitlySet();
   if (!result.mRescaling) {
     result.mSource = aSource->mImage;
     result.mTarget = aTarget->mImage;
     result.mDataRect = aDataRect;
     return result;
   }
 
   float kernelX, kernelY;
   nsSVGLength2 val;
   val.Init(nsSVGUtils::X, 0xff,
-           aUnitX->GetAnimValue(),
+           aKernelUnitLength->GetAnimValue(nsSVGNumberPair::eFirst),
            nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   kernelX = aInstance->GetPrimitiveLength(&val);
   val.Init(nsSVGUtils::Y, 0xff,
-           aUnitY->GetAnimValue(),
+           aKernelUnitLength->GetAnimValue(nsSVGNumberPair::eSecond),
            nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   kernelY = aInstance->GetPrimitiveLength(&val);
-#ifdef DEBUG_tor
-  fprintf(stderr, "scaling kernelX/Y %f %f\n", kernelX, kernelY);
-#endif
   if (kernelX <= 0 || kernelY <= 0)
     return result;
 
   PRBool overflow = PR_FALSE;
   gfxIntSize scaledSize =
     nsSVGUtils::ConvertToSurfaceSize(gfxSize(aTarget->mImage->Width() / kernelX,
                                              aTarget->mImage->Height() / kernelY),
                                      &overflow);
@@ -177,19 +176,16 @@ nsSVGFE::SetupScalingFilter(nsSVGFilterI
 
   gfxRect r(aDataRect.x, aDataRect.y, aDataRect.width, aDataRect.height);
   r.Scale(gfxFloat(scaledSize.width)/aTarget->mImage->Width(),
           gfxFloat(scaledSize.height)/aTarget->mImage->Height());
   r.RoundOut();
   if (!gfxUtils::GfxRectToIntRect(r, &result.mDataRect))
     return result;
   
-#ifdef DEBUG_tor
-  fprintf(stderr, "scaled size %d %d\n", scaledSize.width, scaledSize.height);
-#endif
   result.mSource = new gfxImageSurface(scaledSize,
                                        gfxASurface::ImageFormatARGB32);
   result.mTarget = new gfxImageSurface(scaledSize,
                                        gfxASurface::ImageFormatARGB32);
   if (!result.mSource || result.mSource->CairoStatus() ||
       !result.mTarget || result.mTarget->CairoStatus()) {
     result.mSource = nsnull;
     result.mTarget = nsnull;
@@ -331,28 +327,40 @@ nsSVGFE::DidAnimateLength(PRUint8 aAttrE
 
 void
 nsSVGFE::DidAnimateNumber(PRUint8 aAttrEnum)
 {
   DidAnimateAttr(this);
 }
 
 void
+nsSVGFE::DidAnimateNumberPair(PRUint8 aAttrEnum)
+{
+  DidAnimateAttr(this);
+}
+
+void
 nsSVGFE::DidAnimateNumberList(PRUint8 aAttrEnum)
 {
   DidAnimateAttr(this);
 }
 
 void
 nsSVGFE::DidAnimateInteger(PRUint8 aAttrEnum)
 {
   DidAnimateAttr(this);
 }
 
 void
+nsSVGFE::DidAnimateIntegerPair(PRUint8 aAttrEnum)
+{
+  DidAnimateAttr(this);
+}
+
+void
 nsSVGFE::DidAnimateEnum(PRUint8 aAttrEnum)
 {
   DidAnimateAttr(this);
 }
 
 void
 nsSVGFE::DidAnimatePreserveAspectRatio()
 {
@@ -411,40 +419,39 @@ public:
 
   NS_FORWARD_NSIDOMNODE(nsSVGFEGaussianBlurElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGFEGaussianBlurElementBase::)
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
-  virtual NumberAttributesInfo GetNumberInfo();
+  virtual NumberPairAttributesInfo GetNumberPairInfo();
   virtual StringAttributesInfo GetStringInfo();
 
-  enum { STD_DEV_X, STD_DEV_Y };
-  nsSVGNumber2 mNumberAttributes[2];
-  static NumberInfo sNumberInfo[2];
+  enum { STD_DEV };
+  nsSVGNumberPair mNumberPairAttributes[1];
+  static NumberPairInfo sNumberPairInfo[1];
 
   enum { RESULT, IN1 };
   nsSVGString mStringAttributes[2];
   static StringInfo sStringInfo[2];
 
 private:
   nsresult GetDXY(PRUint32 *aDX, PRUint32 *aDY, const nsSVGFilterInstance& aInstance);
   nsIntRect InflateRectForBlur(const nsIntRect& aRect, const nsSVGFilterInstance& aInstance);
 
   void GaussianBlur(const Image *aSource, const Image *aTarget,
                     const nsIntRect& aDataRect,
                     PRUint32 aDX, PRUint32 aDY);
 };
 
-nsSVGElement::NumberInfo nsSVGFEGaussianBlurElement::sNumberInfo[2] =
-{
-  { &nsGkAtoms::stdDeviation, 0, PR_FALSE },
-  { &nsGkAtoms::stdDeviation, 0, PR_FALSE }
+nsSVGElement::NumberPairInfo nsSVGFEGaussianBlurElement::sNumberPairInfo[1] =
+{
+  { &nsGkAtoms::stdDeviation, 0, 0 }
 };
 
 nsSVGElement::StringInfo nsSVGFEGaussianBlurElement::sStringInfo[2] =
 {
   { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
   { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
@@ -480,31 +487,30 @@ NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEG
 NS_IMETHODIMP nsSVGFEGaussianBlurElement::GetIn1(nsIDOMSVGAnimatedString * *aIn)
 {
   return mStringAttributes[IN1].ToDOMAnimatedString(aIn, this);
 }
 
 /* readonly attribute nsIDOMSVGAnimatedNumber stdDeviationX; */
 NS_IMETHODIMP nsSVGFEGaussianBlurElement::GetStdDeviationX(nsIDOMSVGAnimatedNumber * *aX)
 {
-  return mNumberAttributes[STD_DEV_X].ToDOMAnimatedNumber(aX, this);
+  return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(aX, nsSVGNumberPair::eFirst, this);
 }
 
 /* readonly attribute nsIDOMSVGAnimatedNumber stdDeviationY; */
 NS_IMETHODIMP nsSVGFEGaussianBlurElement::GetStdDeviationY(nsIDOMSVGAnimatedNumber * *aY)
 {
-  return mNumberAttributes[STD_DEV_Y].ToDOMAnimatedNumber(aY, this);
+  return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(aY, nsSVGNumberPair::eSecond, this);
 }
 
 NS_IMETHODIMP
 nsSVGFEGaussianBlurElement::SetStdDeviation(float stdDeviationX, float stdDeviationY)
 {
   NS_ENSURE_FINITE2(stdDeviationX, stdDeviationY, NS_ERROR_ILLEGAL_VALUE);
-  mNumberAttributes[STD_DEV_X].SetBaseValue(stdDeviationX, this, PR_TRUE);
-  mNumberAttributes[STD_DEV_Y].SetBaseValue(stdDeviationY, this, PR_TRUE);
+  mNumberPairAttributes[STD_DEV].SetBaseValues(stdDeviationX, stdDeviationY, this, PR_TRUE);
   return NS_OK;
 }
 
 /**
  * We want to speed up 1/N integer divisions --- integer division is
  * often rather slow.
  * We know that our input numerators V are constrained to be <= 255*N,
  * so the result of dividing by N always fits in 8 bits.
@@ -639,20 +645,20 @@ GetBlurBoxSize(double aStdDev)
     return max;
   return PRUint32(floor(size + 0.5));
 }
 
 nsresult
 nsSVGFEGaussianBlurElement::GetDXY(PRUint32 *aDX, PRUint32 *aDY,
                                    const nsSVGFilterInstance& aInstance)
 {
-  float stdX, stdY;
+  float stdX = mNumberPairAttributes[STD_DEV].GetAnimValue(nsSVGNumberPair::eFirst);
+  float stdY = mNumberPairAttributes[STD_DEV].GetAnimValue(nsSVGNumberPair::eSecond);
+
   nsSVGLength2 val;
-
-  GetAnimatedNumberValues(&stdX, &stdY, nsnull);
   val.Init(nsSVGUtils::X, 0xff, stdX, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   stdX = aInstance.GetPrimitiveLength(&val);
 
   val.Init(nsSVGUtils::Y, 0xff, stdY, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   stdY = aInstance.GetPrimitiveLength(&val);
   
   if (stdX < 0 || stdY < 0)
     return NS_ERROR_FAILURE;
@@ -828,21 +834,21 @@ nsSVGFEGaussianBlurElement::ComputeChang
                                               const nsSVGFilterInstance& aInstance)
 {
   return InflateRectForBlur(aSourceChangeBoxes[0], aInstance);
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
-nsSVGElement::NumberAttributesInfo
-nsSVGFEGaussianBlurElement::GetNumberInfo()
-{
-  return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
-                              NS_ARRAY_LENGTH(sNumberInfo));
+nsSVGElement::NumberPairAttributesInfo
+nsSVGFEGaussianBlurElement::GetNumberPairInfo()
+{
+  return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
+                                  NS_ARRAY_LENGTH(sNumberPairInfo));
 }
 
 nsSVGElement::StringAttributesInfo
 nsSVGFEGaussianBlurElement::GetStringInfo()
 {
   return StringAttributesInfo(mStringAttributes, sStringInfo,
                               NS_ARRAY_LENGTH(sStringInfo));
 }
@@ -1196,24 +1202,19 @@ nsSVGFEColorMatrixElement::Filter(nsSVGF
                                   const Image* aTarget,
                                   const nsIntRect& rect)
 {
   PRUint8* sourceData = aSources[0]->mImage->Data();
   PRUint8* targetData = aTarget->mImage->Data();
   PRUint32 stride = aTarget->mImage->Stride();
 
   PRUint16 type = mEnumAttributes[TYPE].GetAnimValue();
-  const SVGNumberList &values = GetAnimatedNumberList(VALUES)->GetAnimValue();
-
-#ifdef DEBUG_tor
-  fprintf(stderr, "FILTER COLOR MATRIX rect: %d,%d  %dx%d\n",
-          rect.x, rect.y, rect.width, rect.height);
-#endif
-
-  if (!HasAttr(kNameSpaceID_None, nsGkAtoms::values) &&
+  const SVGNumberList &values = mNumberListAttributes[VALUES].GetAnimValue();
+
+  if (!mNumberListAttributes[VALUES].IsExplicitlySet() &&
       (type == nsSVGFEColorMatrixElement::SVG_FECOLORMATRIX_TYPE_MATRIX ||
        type == nsSVGFEColorMatrixElement::SVG_FECOLORMATRIX_TYPE_SATURATE ||
        type == nsSVGFEColorMatrixElement::SVG_FECOLORMATRIX_TYPE_HUE_ROTATE)) {
     // identity matrix filter
     CopyRect(aTarget, aSources[0], rect);
     return NS_OK;
   }
 
@@ -1537,21 +1538,16 @@ nsSVGFECompositeElement::Filter(nsSVGFil
 {
   PRUint16 op = mEnumAttributes[OPERATOR].GetAnimValue();
 
   // Cairo does not support arithmetic operator
   if (op == nsSVGFECompositeElement::SVG_OPERATOR_ARITHMETIC) {
     float k1, k2, k3, k4;
     GetAnimatedNumberValues(&k1, &k2, &k3, &k4, nsnull);
 
-#ifdef DEBUG_tor
-    fprintf(stderr, "FILTER COMPOSITE rect: %d,%d  %dx%d\n",
-            rect.x, rect.y, rect.width, rect.height);
-#endif
-
     // Copy the first source image
     CopyRect(aTarget, aSources[0], rect);
 
     PRUint8* sourceData = aSources[1]->mImage->Data();
     PRUint8* targetData = aTarget->mImage->Data();
     PRUint32 stride = aTarget->mImage->Stride();
 
     // Blend in the second source image
@@ -1814,21 +1810,16 @@ nsSVGFEComponentTransferElement::Filter(
                                         const nsTArray<const Image*>& aSources,
                                         const Image* aTarget,
                                         const nsIntRect& rect)
 {
   PRUint8* sourceData = aSources[0]->mImage->Data();
   PRUint8* targetData = aTarget->mImage->Data();
   PRUint32 stride = aTarget->mImage->Stride();
 
-#ifdef DEBUG_tor
-  fprintf(stderr, "FILTER COMPONENT rect: %d,%d  %dx%d\n",
-          rect.x, rect.y, rect.width, rect.height);
-#endif
-
   PRUint8 tableR[256], tableG[256], tableB[256], tableA[256];
   for (int i=0; i<256; i++)
     tableR[i] = tableG[i] = tableB[i] = tableA[i] = i;
   PRUint8* tables[] = { tableR, tableG, tableB, tableA };
   PRUint32 count = GetChildCount();
   for (PRUint32 k = 0; k < count; k++) {
     nsRefPtr<nsSVGComponentTransferFunctionElement> child;
     CallQueryInterface(GetChildAt(k),
@@ -1965,17 +1956,17 @@ nsSVGComponentTransferFunctionElement::G
 {
   PRUint16 type = mEnumAttributes[TYPE].GetAnimValue();
 
   float slope, intercept, amplitude, exponent, offset;
   GetAnimatedNumberValues(&slope, &intercept, &amplitude, 
                           &exponent, &offset, nsnull);
 
   const SVGNumberList &tableValues =
-    GetAnimatedNumberList(TABLEVALUES)->GetAnimValue();
+    mNumberListAttributes[TABLEVALUES].GetAnimValue();
   PRUint32 tvLength = tableValues.Length();
 
   PRUint32 i;
 
   switch (type) {
   case nsIDOMSVGComponentTransferFunctionElement::SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
   {
     if (tableValues.Length() <= 1)
@@ -2589,21 +2580,16 @@ nsSVGFEOffsetElement::GetOffset(const ns
 }
 
 nsresult
 nsSVGFEOffsetElement::Filter(nsSVGFilterInstance *instance,
                              const nsTArray<const Image*>& aSources,
                              const Image* aTarget,
                              const nsIntRect& rect)
 {
-#ifdef DEBUG_tor
-  fprintf(stderr, "FILTER OFFSET rect: %d,%d  %dx%d\n",
-          rect.x, rect.y, rect.width, rect.height);
-#endif
-
   nsIntPoint offset = GetOffset(*instance);
 
   gfxContext ctx(aTarget->mImage);
   ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
   // Ensure rendering is limited to the filter primitive subregion
   ctx.Clip(aTarget->mFilterPrimitiveSubregion);
   ctx.Translate(gfxPoint(offset.x, offset.y));
   ctx.SetSource(aSources[0]->mImage);
@@ -2929,20 +2915,16 @@ static PRInt32 WrapInterval(PRInt32 aVal
 // nsSVGElement methods
 
 nsresult
 nsSVGFETileElement::Filter(nsSVGFilterInstance *instance,
                            const nsTArray<const Image*>& aSources,
                            const Image* aTarget,
                            const nsIntRect& rect)
 {
-#ifdef DEBUG_tor
-  fprintf(stderr, "FILTER TILE rect: %d,%d  %dx%d\n",
-          rect.x, rect.y, rect.width, rect.height);
-#endif
   // XXX This code depends on the surface rect containing the filter
   // primitive subregion. ComputeTargetBBox, ComputeNeededSourceBBoxes
   // and ComputeChangeBBox are all pessimal, so that will normally be OK,
   // but nothing clips mFilterPrimitiveSubregion so this should be changed.
 
   nsIntRect tile;
   PRBool res = gfxUtils::GfxRectToIntRect(aSources[0]->mFilterPrimitiveSubregion, &tile);
 
@@ -3031,23 +3013,28 @@ public:
   NS_FORWARD_NSIDOMNODE(nsSVGFETurbulenceElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGFETurbulenceElementBase::)
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   virtual NumberAttributesInfo GetNumberInfo();
+  virtual NumberPairAttributesInfo GetNumberPairInfo();
   virtual IntegerAttributesInfo GetIntegerInfo();
   virtual EnumAttributesInfo GetEnumInfo();
   virtual StringAttributesInfo GetStringInfo();
 
-  enum { BASE_FREQ_X, BASE_FREQ_Y, SEED}; // floating point seed?!
-  nsSVGNumber2 mNumberAttributes[3];
-  static NumberInfo sNumberInfo[3];
+  enum { SEED }; // floating point seed?!
+  nsSVGNumber2 mNumberAttributes[1];
+  static NumberInfo sNumberInfo[1];
+
+  enum { BASE_FREQ };
+  nsSVGNumberPair mNumberPairAttributes[1];
+  static NumberPairInfo sNumberPairInfo[1];
 
   enum { OCTAVES };
   nsSVGInteger mIntegerAttributes[1];
   static IntegerInfo sIntegerInfo[1];
 
   enum { TYPE, STITCHTILES };
   nsSVGEnum mEnumAttributes[2];
   static nsSVGEnumMapping sTypeMap[];
@@ -3115,23 +3102,26 @@ private:
   double Noise2(int aColorChannel, double aVec[2], StitchInfo *aStitchInfo);
   double
   Turbulence(int aColorChannel, double *aPoint, double aBaseFreqX,
              double aBaseFreqY, int aNumOctaves, PRBool aFractalSum,
              PRBool aDoStitching, double aTileX, double aTileY,
              double aTileWidth, double aTileHeight);
 };
 
-nsSVGElement::NumberInfo nsSVGFETurbulenceElement::sNumberInfo[3] =
-{
-  { &nsGkAtoms::baseFrequency, 0, PR_FALSE },
-  { &nsGkAtoms::baseFrequency, 0, PR_FALSE },
+nsSVGElement::NumberInfo nsSVGFETurbulenceElement::sNumberInfo[1] =
+{
   { &nsGkAtoms::seed, 0, PR_FALSE }
 };
 
+nsSVGElement::NumberPairInfo nsSVGFETurbulenceElement::sNumberPairInfo[1] =
+{
+  { &nsGkAtoms::baseFrequency, 0, 0 }
+};
+
 nsSVGElement::IntegerInfo nsSVGFETurbulenceElement::sIntegerInfo[1] =
 {
   { &nsGkAtoms::numOctaves, 1 }
 };
 
 nsSVGEnumMapping nsSVGFETurbulenceElement::sTypeMap[] = {
   {&nsGkAtoms::fractalNoise,
    nsIDOMSVGFETurbulenceElement::SVG_TURBULENCE_TYPE_FRACTALNOISE},
@@ -3189,23 +3179,23 @@ NS_INTERFACE_MAP_END_INHERITING(nsSVGFET
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFETurbulenceElement)
 
 //----------------------------------------------------------------------
 // nsIDOMSVGFETurbulenceElement methods
 
 /* readonly attribute nsIDOMSVGAnimatedNumber baseFrequencyX; */
 NS_IMETHODIMP nsSVGFETurbulenceElement::GetBaseFrequencyX(nsIDOMSVGAnimatedNumber * *aX)
 {
-  return mNumberAttributes[BASE_FREQ_X].ToDOMAnimatedNumber(aX, this);
+  return mNumberPairAttributes[BASE_FREQ].ToDOMAnimatedNumber(aX, nsSVGNumberPair::eFirst, this);
 }
 
 /* readonly attribute nsIDOMSVGAnimatedNumber baseFrequencyY; */
 NS_IMETHODIMP nsSVGFETurbulenceElement::GetBaseFrequencyY(nsIDOMSVGAnimatedNumber * *aY)
 {
-  return mNumberAttributes[BASE_FREQ_Y].ToDOMAnimatedNumber(aY, this);
+  return mNumberPairAttributes[BASE_FREQ].ToDOMAnimatedNumber(aY, nsSVGNumberPair::eSecond, this);
 }
 
 /* readonly attribute nsIDOMSVGAnimatedInteger numOctaves; */
 NS_IMETHODIMP nsSVGFETurbulenceElement::GetNumOctaves(nsIDOMSVGAnimatedInteger * *aNum)
 {
   return mIntegerAttributes[OCTAVES].ToDOMAnimatedInteger(aNum, this);
 }
 
@@ -3236,28 +3226,23 @@ nsSVGFETurbulenceElement::Filter(nsSVGFi
   PRUint8* targetData = aTarget->mImage->Data();
   PRUint32 stride = aTarget->mImage->Stride();
 
   nsIntRect filterSubregion(PRInt32(aTarget->mFilterPrimitiveSubregion.X()),
                             PRInt32(aTarget->mFilterPrimitiveSubregion.Y()),
                             PRInt32(aTarget->mFilterPrimitiveSubregion.Width()),
                             PRInt32(aTarget->mFilterPrimitiveSubregion.Height()));
 
-#ifdef DEBUG_tor
-  fprintf(stderr, "FILTER TURBULENCE rect: %d,%d  %dx%d\n",
-          rect.x, rect.y, rect.width, rect.height);
-#endif
-
-  float fX, fY, seed;
+  float fX = mNumberPairAttributes[BASE_FREQ].GetAnimValue(nsSVGNumberPair::eFirst);
+  float fY = mNumberPairAttributes[BASE_FREQ].GetAnimValue(nsSVGNumberPair::eSecond);
+  float seed = mNumberAttributes[OCTAVES].GetAnimValue();
   PRInt32 octaves = mIntegerAttributes[OCTAVES].GetAnimValue();
   PRUint16 type = mEnumAttributes[TYPE].GetAnimValue();
   PRUint16 stitch = mEnumAttributes[STITCHTILES].GetAnimValue();
 
-  GetAnimatedNumberValues(&fX, &fY, &seed, nsnull);
-
   InitSeed((PRInt32)seed);
 
   // XXXroc this makes absolutely no sense to me.
   float filterX = instance->GetFilterRect().X();
   float filterY = instance->GetFilterRect().Y();
   float filterWidth = instance->GetFilterRect().Width();
   float filterHeight = instance->GetFilterRect().Height();
 
@@ -3486,16 +3471,23 @@ nsSVGFETurbulenceElement::ComputeTargetB
 
 nsSVGElement::NumberAttributesInfo
 nsSVGFETurbulenceElement::GetNumberInfo()
 {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               NS_ARRAY_LENGTH(sNumberInfo));
 }
 
+nsSVGElement::NumberPairAttributesInfo
+nsSVGFETurbulenceElement::GetNumberPairInfo()
+{
+  return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
+                                 NS_ARRAY_LENGTH(sNumberPairInfo));
+}
+
 nsSVGElement::IntegerAttributesInfo
 nsSVGFETurbulenceElement::GetIntegerInfo()
 {
   return IntegerAttributesInfo(mIntegerAttributes, sIntegerInfo,
                                NS_ARRAY_LENGTH(sIntegerInfo));
 }
 
 nsSVGElement::EnumAttributesInfo
@@ -3555,38 +3547,37 @@ public:
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   void GetRXY(PRInt32 *aRX, PRInt32 *aRY, const nsSVGFilterInstance& aInstance);
   nsIntRect InflateRect(const nsIntRect& aRect, const nsSVGFilterInstance& aInstance);
 
-  virtual NumberAttributesInfo GetNumberInfo();
+  virtual NumberPairAttributesInfo GetNumberPairInfo();
   virtual EnumAttributesInfo GetEnumInfo();
   virtual StringAttributesInfo GetStringInfo();
 
-  enum { RADIUS_X, RADIUS_Y };
-  nsSVGNumber2 mNumberAttributes[2];
-  static NumberInfo sNumberInfo[2];
+  enum { RADIUS };
+  nsSVGNumberPair mNumberPairAttributes[1];
+  static NumberPairInfo sNumberPairInfo[1];
 
   enum { OPERATOR };
   nsSVGEnum mEnumAttributes[1];
   static nsSVGEnumMapping sOperatorMap[];
   static EnumInfo sEnumInfo[1];
 
   enum { RESULT, IN1 };
   nsSVGString mStringAttributes[2];
   static StringInfo sStringInfo[2];
 };
 
-nsSVGElement::NumberInfo nsSVGFEMorphologyElement::sNumberInfo[2] =
-{
-  { &nsGkAtoms::radius, 0, PR_FALSE },
-  { &nsGkAtoms::radius, 0, PR_FALSE }
+nsSVGElement::NumberPairInfo nsSVGFEMorphologyElement::sNumberPairInfo[1] =
+{
+  { &nsGkAtoms::radius, 0, 0 }
 };
 
 nsSVGEnumMapping nsSVGFEMorphologyElement::sOperatorMap[] = {
   {&nsGkAtoms::erode, nsSVGFEMorphologyElement::SVG_OPERATOR_ERODE},
   {&nsGkAtoms::dilate, nsSVGFEMorphologyElement::SVG_OPERATOR_DILATE},
   {nsnull, 0}
 };
 
@@ -3642,31 +3633,30 @@ NS_IMETHODIMP nsSVGFEMorphologyElement::
 NS_IMETHODIMP nsSVGFEMorphologyElement::GetOperator(nsIDOMSVGAnimatedEnumeration * *aOperator)
 {
   return mEnumAttributes[OPERATOR].ToDOMAnimatedEnum(aOperator, this);
 }
 
 /* readonly attribute nsIDOMSVGAnimatedNumber radiusX; */
 NS_IMETHODIMP nsSVGFEMorphologyElement::GetRadiusX(nsIDOMSVGAnimatedNumber * *aX)
 {
-  return mNumberAttributes[RADIUS_X].ToDOMAnimatedNumber(aX, this);
+  return mNumberPairAttributes[RADIUS].ToDOMAnimatedNumber(aX, nsSVGNumberPair::eFirst, this);
 }
 
 /* readonly attribute nsIDOMSVGAnimatedNumber radiusY; */
 NS_IMETHODIMP nsSVGFEMorphologyElement::GetRadiusY(nsIDOMSVGAnimatedNumber * *aY)
 {
-  return mNumberAttributes[RADIUS_Y].ToDOMAnimatedNumber(aY, this);
+  return mNumberPairAttributes[RADIUS].ToDOMAnimatedNumber(aY, nsSVGNumberPair::eSecond, this);
 }
 
 NS_IMETHODIMP
 nsSVGFEMorphologyElement::SetRadius(float rx, float ry)
 {
   NS_ENSURE_FINITE2(rx, ry, NS_ERROR_ILLEGAL_VALUE);
-  mNumberAttributes[RADIUS_X].SetBaseValue(rx, this, PR_TRUE);
-  mNumberAttributes[RADIUS_Y].SetBaseValue(ry, this, PR_TRUE);
+  mNumberPairAttributes[RADIUS].SetBaseValues(rx, ry, this, PR_TRUE);
   return NS_OK;
 }
 
 void
 nsSVGFEMorphologyElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
 }
@@ -3704,40 +3694,35 @@ nsSVGFEMorphologyElement::ComputeChangeB
 }
 
 #define MORPHOLOGY_EPSILON 0.0001
 
 void
 nsSVGFEMorphologyElement::GetRXY(PRInt32 *aRX, PRInt32 *aRY,
         const nsSVGFilterInstance& aInstance)
 {
+  float rx = mNumberPairAttributes[RADIUS].GetAnimValue(nsSVGNumberPair::eFirst);
+  float ry = mNumberPairAttributes[RADIUS].GetAnimValue(nsSVGNumberPair::eSecond);
   nsSVGLength2 val;
-  float rx, ry;
-  GetAnimatedNumberValues(&rx, &ry, nsnull);
   val.Init(nsSVGUtils::X, 0xff, rx, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   // Subtract an epsilon here because we don't want a value that's just
   // slightly larger than an integer to round up to the next integer; it's
   // probably meant to be the integer it's close to, modulo machine precision
   // issues.
   *aRX = NSToIntCeil(aInstance.GetPrimitiveLength(&val) - MORPHOLOGY_EPSILON);
   val.Init(nsSVGUtils::Y, 0xff, ry, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   *aRY = NSToIntCeil(aInstance.GetPrimitiveLength(&val) - MORPHOLOGY_EPSILON);
 }
 
 nsresult
 nsSVGFEMorphologyElement::Filter(nsSVGFilterInstance *instance,
                                  const nsTArray<const Image*>& aSources,
                                  const Image* aTarget,
                                  const nsIntRect& rect)
 {
-#ifdef DEBUG_tor
-  fprintf(stderr, "FILTER MORPH rect: %d,%d  %dx%d\n",
-          rect.x, rect.y, rect.width, rect.height);
-#endif
-
   PRInt32 rx, ry;
   GetRXY(&rx, &ry, *instance);
 
   if (rx < 0 || ry < 0) {
     // XXX nsSVGUtils::ReportToConsole()
     return NS_OK;
   }
   if (rx == 0 && ry == 0) {
@@ -3813,21 +3798,21 @@ nsSVGFEMorphologyElement::Filter(nsSVGFi
     }
   }
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
-nsSVGElement::NumberAttributesInfo
-nsSVGFEMorphologyElement::GetNumberInfo()
-{
-  return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
-                              NS_ARRAY_LENGTH(sNumberInfo));
+nsSVGElement::NumberPairAttributesInfo
+nsSVGFEMorphologyElement::GetNumberPairInfo()
+{
+  return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
+                                  NS_ARRAY_LENGTH(sNumberPairInfo));
 }
 
 nsSVGElement::EnumAttributesInfo
 nsSVGFEMorphologyElement::GetEnumInfo()
 {
   return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
                             NS_ARRAY_LENGTH(sEnumInfo));
 }
@@ -3884,29 +3869,39 @@ public:
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   virtual PRBool OperatesOnPremultipledAlpha(PRInt32) {
     return !mBooleanAttributes[PRESERVEALPHA].GetAnimValue();
   }
 
   virtual NumberAttributesInfo GetNumberInfo();
+  virtual NumberPairAttributesInfo GetNumberPairInfo();
   virtual IntegerAttributesInfo GetIntegerInfo();
+  virtual IntegerPairAttributesInfo GetIntegerPairInfo();
   virtual BooleanAttributesInfo GetBooleanInfo();
   virtual EnumAttributesInfo GetEnumInfo();
   virtual StringAttributesInfo GetStringInfo();
   virtual NumberListAttributesInfo GetNumberListInfo();
 
-  enum { DIVISOR, BIAS, KERNEL_UNIT_LENGTH_X, KERNEL_UNIT_LENGTH_Y };
-  nsSVGNumber2 mNumberAttributes[4];
-  static NumberInfo sNumberInfo[4];
-
-  enum { ORDER_X, ORDER_Y, TARGET_X, TARGET_Y };
-  nsSVGInteger mIntegerAttributes[4];
-  static IntegerInfo sIntegerInfo[4];
+  enum { DIVISOR, BIAS };
+  nsSVGNumber2 mNumberAttributes[2];
+  static NumberInfo sNumberInfo[2];
+
+  enum { KERNEL_UNIT_LENGTH };
+  nsSVGNumberPair mNumberPairAttributes[1];
+  static NumberPairInfo sNumberPairInfo[1];
+
+  enum { TARGET_X, TARGET_Y };
+  nsSVGInteger mIntegerAttributes[2];
+  static IntegerInfo sIntegerInfo[2];
+
+  enum { ORDER };
+  nsSVGIntegerPair mIntegerPairAttributes[1];
+  static IntegerPairInfo sIntegerPairInfo[1];
 
   enum { PRESERVEALPHA };
   nsSVGBoolean mBooleanAttributes[1];
   static BooleanInfo sBooleanInfo[1];
 
   enum { EDGEMODE };
   nsSVGEnum mEnumAttributes[1];
   static nsSVGEnumMapping sEdgeModeMap[];
@@ -3916,32 +3911,38 @@ protected:
   nsSVGString mStringAttributes[2];
   static StringInfo sStringInfo[2];
 
   enum { KERNELMATRIX };
   SVGAnimatedNumberList mNumberListAttributes[1];
   static NumberListInfo sNumberListInfo[1];
 };
 
-nsSVGElement::NumberInfo nsSVGFEConvolveMatrixElement::sNumberInfo[4] =
+nsSVGElement::NumberInfo nsSVGFEConvolveMatrixElement::sNumberInfo[2] =
 {
   { &nsGkAtoms::divisor, 1, PR_FALSE },
-  { &nsGkAtoms::bias, 0, PR_FALSE },
-  { &nsGkAtoms::kernelUnitLength, 0, PR_FALSE },
-  { &nsGkAtoms::kernelUnitLength, 0, PR_FALSE }
+  { &nsGkAtoms::bias, 0, PR_FALSE }
 };
 
-nsSVGElement::IntegerInfo nsSVGFEConvolveMatrixElement::sIntegerInfo[4] =
-{
-  { &nsGkAtoms::order, 3 },
-  { &nsGkAtoms::order, 3 },
+nsSVGElement::NumberPairInfo nsSVGFEConvolveMatrixElement::sNumberPairInfo[1] =
+{
+  { &nsGkAtoms::kernelUnitLength, 0, 0 }
+};
+
+nsSVGElement::IntegerInfo nsSVGFEConvolveMatrixElement::sIntegerInfo[2] =
+{
   { &nsGkAtoms::targetX, 0 },
   { &nsGkAtoms::targetY, 0 }
 };
 
+nsSVGElement::IntegerPairInfo nsSVGFEConvolveMatrixElement::sIntegerPairInfo[1] =
+{
+  { &nsGkAtoms::order, 3, 3 }
+};
+
 nsSVGElement::BooleanInfo nsSVGFEConvolveMatrixElement::sBooleanInfo[1] =
 {
   { &nsGkAtoms::preserveAlpha, PR_FALSE }
 };
 
 nsSVGEnumMapping nsSVGFEConvolveMatrixElement::sEdgeModeMap[] = {
   {&nsGkAtoms::duplicate, nsSVGFEConvolveMatrixElement::SVG_EDGEMODE_DUPLICATE},
   {&nsGkAtoms::wrap, nsSVGFEConvolveMatrixElement::SVG_EDGEMODE_WRAP},
@@ -3997,22 +3998,22 @@ NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEC
 
 NS_IMETHODIMP nsSVGFEConvolveMatrixElement::GetIn1(nsIDOMSVGAnimatedString * *aIn)
 {
   return mStringAttributes[IN1].ToDOMAnimatedString(aIn, this);
 }
 
 NS_IMETHODIMP nsSVGFEConvolveMatrixElement::GetOrderX(nsIDOMSVGAnimatedInteger * *aOrderX)
 {
-  return mIntegerAttributes[ORDER_X].ToDOMAnimatedInteger(aOrderX, this);
+  return mIntegerPairAttributes[ORDER].ToDOMAnimatedInteger(aOrderX, nsSVGIntegerPair::eFirst, this);
 }
 
 NS_IMETHODIMP nsSVGFEConvolveMatrixElement::GetOrderY(nsIDOMSVGAnimatedInteger * *aOrderY)
 {
-  return mIntegerAttributes[ORDER_Y].ToDOMAnimatedInteger(aOrderY, this);
+  return mIntegerPairAttributes[ORDER].ToDOMAnimatedInteger(aOrderY, nsSVGIntegerPair::eSecond, this);
 }
 
 NS_IMETHODIMP nsSVGFEConvolveMatrixElement::GetKernelMatrix(nsIDOMSVGAnimatedNumberList * *aKernelMatrix)
 {
   *aKernelMatrix = DOMSVGAnimatedNumberList::GetDOMWrapper(&mNumberListAttributes[KERNELMATRIX],
                                                            this, KERNELMATRIX).get();
   return NS_OK;
 }
@@ -4047,25 +4048,27 @@ NS_IMETHODIMP
 nsSVGFEConvolveMatrixElement::GetBias(nsIDOMSVGAnimatedNumber **aBias)
 {
   return mNumberAttributes[BIAS].ToDOMAnimatedNumber(aBias, this);
 }
 
 NS_IMETHODIMP
 nsSVGFEConvolveMatrixElement::GetKernelUnitLengthX(nsIDOMSVGAnimatedNumber **aKernelX)
 {
-  return mNumberAttributes[KERNEL_UNIT_LENGTH_X].ToDOMAnimatedNumber(aKernelX,
-                                                                     this);
+  return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(aKernelX,
+                                                                       nsSVGNumberPair::eFirst,
+                                                                       this);
 }
 
 NS_IMETHODIMP
 nsSVGFEConvolveMatrixElement::GetKernelUnitLengthY(nsIDOMSVGAnimatedNumber **aKernelY)
 {
-  return mNumberAttributes[KERNEL_UNIT_LENGTH_Y].ToDOMAnimatedNumber(aKernelY,
-                                                                     this);
+  return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(aKernelY,
+                                                                       nsSVGNumberPair::eSecond,
+                                                                       this);
 }
 
 void
 nsSVGFEConvolveMatrixElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
 {
   aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
 }
 
@@ -4168,35 +4171,37 @@ ConvolvePixel(const PRUint8 *aSourceData
 
 nsresult
 nsSVGFEConvolveMatrixElement::Filter(nsSVGFilterInstance *instance,
                                      const nsTArray<const Image*>& aSources,
                                      const Image* aTarget,
                                      const nsIntRect& rect)
 {
   const SVGNumberList &kernelMatrix =
-    GetAnimatedNumberList(KERNELMATRIX)->GetAnimValue();
+    mNumberListAttributes[KERNELMATRIX].GetAnimValue();
   PRUint32 kmLength = kernelMatrix.Length();
 
-  PRInt32 orderX, orderY;
-  PRInt32 targetX, targetY;
-  GetAnimatedIntegerValues(&orderX, &orderY, &targetX, &targetY, nsnull);
+  PRInt32 orderX = mIntegerPairAttributes[ORDER].GetAnimValue(nsSVGIntegerPair::eFirst);
+  PRInt32 orderY = mIntegerPairAttributes[ORDER].GetAnimValue(nsSVGIntegerPair::eSecond);
 
   if (orderX <= 0 || orderY <= 0 ||
       static_cast<PRUint32>(orderX * orderY) != kmLength) {
     return NS_ERROR_FAILURE;
   }
 
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::targetX)) {
+  PRInt32 targetX, targetY;
+  GetAnimatedIntegerValues(&targetX, &targetY, nsnull);
+
+  if (mIntegerAttributes[TARGET_X].IsExplicitlySet()) {
     if (targetX < 0 || targetX >= orderX)
       return NS_ERROR_FAILURE;
   } else {
     targetX = orderX / 2;
   }
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::targetY)) {
+  if (mIntegerAttributes[TARGET_Y].IsExplicitlySet()) {
     if (targetY < 0 || targetY >= orderY)
       return NS_ERROR_FAILURE;
   } else {
     targetY = orderY / 2;
   }
 
   if (orderX > NS_SVG_OFFSCREEN_MAX_DIMENSION ||
       orderY > NS_SVG_OFFSCREEN_MAX_DIMENSION)
@@ -4204,41 +4209,37 @@ nsSVGFEConvolveMatrixElement::Filter(nsS
   nsAutoArrayPtr<float> kernel(new float[orderX * orderY]);
   if (!kernel)
     return NS_ERROR_FAILURE;
   for (PRUint32 i = 0; i < kmLength; i++) {
     kernel[kmLength - 1 - i] = kernelMatrix[i];
   }
 
   float divisor;
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::divisor)) {
+  if (mNumberAttributes[DIVISOR].IsExplicitlySet()) {
     divisor = mNumberAttributes[DIVISOR].GetAnimValue();
     if (divisor == 0)
       return NS_ERROR_FAILURE;
   } else {
     divisor = kernel[0];
     for (PRUint32 i = 1; i < kmLength; i++)
       divisor += kernel[i];
     if (divisor == 0)
       divisor = 1;
   }
 
   ScaleInfo info = SetupScalingFilter(instance, aSources[0], aTarget, rect,
-                                      &mNumberAttributes[KERNEL_UNIT_LENGTH_X],
-                                      &mNumberAttributes[KERNEL_UNIT_LENGTH_Y]);
+                                      &mNumberPairAttributes[KERNEL_UNIT_LENGTH]);
   if (!info.mTarget)
     return NS_ERROR_FAILURE;
 
   PRUint16 edgeMode = mEnumAttributes[EDGEMODE].GetAnimValue();
   PRBool preserveAlpha = mBooleanAttributes[PRESERVEALPHA].GetAnimValue();
 
-  float bias = 0;
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::bias)) {
-    bias = mNumberAttributes[BIAS].GetAnimValue();
-  }
+  float bias = mNumberAttributes[BIAS].GetAnimValue();
 
   const nsIntRect& dataRect = info.mDataRect;
   PRInt32 stride = info.mSource->Stride();
   PRInt32 width = info.mSource->GetSize().width;
   PRInt32 height = info.mSource->GetSize().height;
   PRUint8 *sourceData = info.mSource->Data();
   PRUint8 *targetData = info.mTarget->Data();
 
@@ -4262,23 +4263,37 @@ nsSVGFEConvolveMatrixElement::Filter(nsS
 
 nsSVGElement::NumberAttributesInfo
 nsSVGFEConvolveMatrixElement::GetNumberInfo()
 {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               NS_ARRAY_LENGTH(sNumberInfo));
 }
 
+nsSVGElement::NumberPairAttributesInfo
+nsSVGFEConvolveMatrixElement::GetNumberPairInfo()
+{
+  return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
+                                  NS_ARRAY_LENGTH(sNumberPairInfo));
+}
+
 nsSVGElement::IntegerAttributesInfo
 nsSVGFEConvolveMatrixElement::GetIntegerInfo()
 {
   return IntegerAttributesInfo(mIntegerAttributes, sIntegerInfo,
                                NS_ARRAY_LENGTH(sIntegerInfo));
 }
 
+nsSVGElement::IntegerPairAttributesInfo
+nsSVGFEConvolveMatrixElement::GetIntegerPairInfo()
+{
+  return IntegerPairAttributesInfo(mIntegerPairAttributes, sIntegerPairInfo,
+                                   NS_ARRAY_LENGTH(sIntegerPairInfo));
+}
+
 nsSVGElement::BooleanAttributesInfo
 nsSVGFEConvolveMatrixElement::GetBooleanInfo()
 {
   return BooleanAttributesInfo(mBooleanAttributes, sBooleanInfo,
                                NS_ARRAY_LENGTH(sBooleanInfo));
 }
 
 nsSVGElement::EnumAttributesInfo
@@ -4492,17 +4507,18 @@ nsSVGFEPointLightElement::GetNumberInfo(
 //---------------------SpotLight------------------------
 
 typedef nsSVGElement nsSVGFESpotLightElementBase;
 
 class nsSVGFESpotLightElement : public nsSVGFESpotLightElementBase,
                                 public nsIDOMSVGFESpotLightElement
 {
   friend nsresult NS_NewSVGFESpotLightElement(nsIContent **aResult,
-                                                 already_AddRefed<nsINodeInfo> aNodeInfo);
+                                              already_AddRefed<nsINodeInfo> aNodeInfo);
+  friend class nsSVGFELightingElement;
 protected:
   nsSVGFESpotLightElement(already_AddRefed<nsINodeInfo> aNodeInfo)
     : nsSVGFESpotLightElementBase(aNodeInfo) {}
 
 public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGFESPOTLIGHTELEMENT
@@ -4660,36 +4676,43 @@ public:
   NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
 protected:
   virtual void
   LightPixel(const float *N, const float *L,
              nscolor color, PRUint8 *targetData) = 0;
 
   virtual NumberAttributesInfo GetNumberInfo();
+  virtual NumberPairAttributesInfo GetNumberPairInfo();
   virtual StringAttributesInfo GetStringInfo();
 
-  enum { SURFACE_SCALE, DIFFUSE_CONSTANT, SPECULAR_CONSTANT, SPECULAR_EXPONENT,
-         KERNEL_UNIT_LENGTH_X, KERNEL_UNIT_LENGTH_Y };
-  nsSVGNumber2 mNumberAttributes[6];
-  static NumberInfo sNumberInfo[6];
+  enum { SURFACE_SCALE, DIFFUSE_CONSTANT, SPECULAR_CONSTANT, SPECULAR_EXPONENT };
+  nsSVGNumber2 mNumberAttributes[4];
+  static NumberInfo sNumberInfo[4];
+
+  enum { KERNEL_UNIT_LENGTH };
+  nsSVGNumberPair mNumberPairAttributes[1];
+  static NumberPairInfo sNumberPairInfo[1];
 
   enum { RESULT, IN1 };
   nsSVGString mStringAttributes[2];
   static StringInfo sStringInfo[2];
 };
 
-nsSVGElement::NumberInfo nsSVGFELightingElement::sNumberInfo[6] =
+nsSVGElement::NumberInfo nsSVGFELightingElement::sNumberInfo[4] =
 {
   { &nsGkAtoms::surfaceScale, 1, PR_FALSE },
   { &nsGkAtoms::diffuseConstant, 1, PR_FALSE },
   { &nsGkAtoms::specularConstant, 1, PR_FALSE },
-  { &nsGkAtoms::specularExponent, 1, PR_FALSE },
-  { &nsGkAtoms::kernelUnitLength, 0, PR_FALSE },
-  { &nsGkAtoms::kernelUnitLength, 0, PR_FALSE }
+  { &nsGkAtoms::specularExponent, 1, PR_FALSE }
+};
+
+nsSVGElement::NumberPairInfo nsSVGFELightingElement::sNumberPairInfo[1] =
+{
+  { &nsGkAtoms::kernelUnitLength, 0, 0 }
 };
 
 nsSVGElement::StringInfo nsSVGFELightingElement::sStringInfo[2] =
 {
   { &nsGkAtoms::result, kNameSpaceID_None, PR_TRUE },
   { &nsGkAtoms::in, kNameSpaceID_None, PR_TRUE }
 };
 
@@ -4827,18 +4850,17 @@ GenerateNormal(float *N, const PRUint8 *
 
 nsresult
 nsSVGFELightingElement::Filter(nsSVGFilterInstance *instance,
                                const nsTArray<const Image*>& aSources,
                                const Image* aTarget,
                                const nsIntRect& rect)
 {
   ScaleInfo info = SetupScalingFilter(instance, aSources[0], aTarget, rect,
-                                      &mNumberAttributes[KERNEL_UNIT_LENGTH_X],
-                                      &mNumberAttributes[KERNEL_UNIT_LENGTH_Y]);
+                                      &mNumberPairAttributes[KERNEL_UNIT_LENGTH]);
   if (!info.mTarget)
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIDOMSVGFEDistantLightElement> distantLight;
   nsCOMPtr<nsIDOMSVGFEPointLightElement> pointLight;
   nsCOMPtr<nsIDOMSVGFESpotLightElement> spotLight;
 
   nsIFrame* frame = GetPrimaryFrame();
@@ -4881,31 +4903,32 @@ nsSVGFELightingElement::Filter(nsSVGFilt
       (pointLight.get())->GetAnimatedNumberValues(lightPos,
                                                   lightPos + 1,
                                                   lightPos + 2,
                                                   nsnull);
     instance->ConvertLocation(lightPos);
   }
   if (spotLight) {
     float limitingConeAngle;
-    static_cast<nsSVGFESpotLightElement*>
-      (spotLight.get())->GetAnimatedNumberValues(lightPos,
-                                                 lightPos + 1,
-                                                 lightPos + 2,
-                                                 pointsAt,
-                                                 pointsAt + 1,
-                                                 pointsAt + 2,
-                                                 &specularExponent,
-                                                 &limitingConeAngle,
-                                                 nsnull);
+    nsSVGFESpotLightElement* spot = 
+      static_cast<nsSVGFESpotLightElement*>(spotLight.get());
+    spot->GetAnimatedNumberValues(lightPos,
+                                  lightPos + 1,
+                                  lightPos + 2,
+                                  pointsAt,
+                                  pointsAt + 1,
+                                  pointsAt + 2,
+                                  &specularExponent,
+                                  &limitingConeAngle,
+                                  nsnull);
     instance->ConvertLocation(lightPos);
     instance->ConvertLocation(pointsAt);
 
-    nsCOMPtr<nsIContent> spot = do_QueryInterface(spotLight);
-    if (spot->HasAttr(kNameSpaceID_None, nsGkAtoms::limitingConeAngle)) {
+    if (spot->mNumberAttributes[nsSVGFESpotLightElement::LIMITING_CONE_ANGLE].
+                                  IsExplicitlySet()) {
       cosConeAngle = NS_MAX<double>(cos(limitingConeAngle * radPerDeg), 0.0);
     }
   }
 
   float surfaceScale = mNumberAttributes[SURFACE_SCALE].GetAnimValue();
 
   const nsIntRect& dataRect = info.mDataRect;
   PRInt32 stride = info.mSource->Stride();
@@ -4967,16 +4990,23 @@ nsSVGFELightingElement::Filter(nsSVGFilt
 
 nsSVGElement::NumberAttributesInfo
 nsSVGFELightingElement::GetNumberInfo()
 {
   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
                               NS_ARRAY_LENGTH(sNumberInfo));
 }
 
+nsSVGElement::NumberPairAttributesInfo
+nsSVGFELightingElement::GetNumberPairInfo()
+{
+  return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
+                                  NS_ARRAY_LENGTH(sNumberPairInfo));
+}
+
 nsSVGElement::StringAttributesInfo
 nsSVGFELightingElement::GetStringInfo()
 {
   return StringAttributesInfo(mStringAttributes, sStringInfo,
                               NS_ARRAY_LENGTH(sStringInfo));
 }
 
 //---------------------DiffuseLighting------------------------
@@ -5057,25 +5087,27 @@ nsSVGFEDiffuseLightingElement::GetDiffus
 {
   return mNumberAttributes[DIFFUSE_CONSTANT].ToDOMAnimatedNumber(aConstant,
                                                               this);
 }
 
 NS_IMETHODIMP
 nsSVGFEDiffuseLightingElement::GetKernelUnitLengthX(nsIDOMSVGAnimatedNumber **aKernelX)
 {
-  return mNumberAttributes[KERNEL_UNIT_LENGTH_X].ToDOMAnimatedNumber(aKernelX,
-                                                                     this);
+  return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(aKernelX,
+                                                                       nsSVGNumberPair::eFirst,
+                                                                       this);
 }
 
 NS_IMETHODIMP
 nsSVGFEDiffuseLightingElement::GetKernelUnitLengthY(nsIDOMSVGAnimatedNumber **aKernelY)
 {
-  return mNumberAttributes[KERNEL_UNIT_LENGTH_Y].ToDOMAnimatedNumber(aKernelY,
-                                                                     this);
+  return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(aKernelY,
+                                                                       nsSVGNumberPair::eSecond,
+                                                                       this);
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 void
 nsSVGFEDiffuseLightingElement::LightPixel(const float *N, const float *L,
                                           nscolor color, PRUint8 *targetData)
@@ -5189,25 +5221,27 @@ nsSVGFESpecularLightingElement::GetSpecu
 {
   return mNumberAttributes[SPECULAR_EXPONENT].ToDOMAnimatedNumber(aExponent,
                                                                   this);
 }
 
 NS_IMETHODIMP
 nsSVGFESpecularLightingElement::GetKernelUnitLengthX(nsIDOMSVGAnimatedNumber **aKernelX)
 {
-  return mNumberAttributes[KERNEL_UNIT_LENGTH_X].ToDOMAnimatedNumber(aKernelX,
-                                                                     this);
+  return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(aKernelX,
+                                                                       nsSVGNumberPair::eFirst,
+                                                                       this);
 }
 
 NS_IMETHODIMP
 nsSVGFESpecularLightingElement::GetKernelUnitLengthY(nsIDOMSVGAnimatedNumber **aKernelY)
 {
-  return mNumberAttributes[KERNEL_UNIT_LENGTH_Y].ToDOMAnimatedNumber(aKernelY,
-                                                                     this);
+  return mNumberPairAttributes[KERNEL_UNIT_LENGTH].ToDOMAnimatedNumber(aKernelY,
+                                                                       nsSVGNumberPair::eSecond,
+                                                                       this);
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsresult
 nsSVGFESpecularLightingElement::Filter(nsSVGFilterInstance *instance,
                                        const nsTArray<const Image*>& aSources,
@@ -5445,34 +5479,34 @@ nsSVGFEImageElement::AfterSetAttr(PRInt3
 
   return nsSVGFEImageElementBase::AfterSetAttr(aNamespaceID, aName,
                                                aValue, aNotify);
 }
 
 void
 nsSVGFEImageElement::MaybeLoadSVGImage()
 {
-  if (HasAttr(kNameSpaceID_XLink, nsGkAtoms::href) &&
+  if (mStringAttributes[HREF].IsExplicitlySet() &&
       (NS_FAILED(LoadSVGImage(PR_FALSE, PR_TRUE)) ||
        !LoadingEnabled())) {
     CancelImageRequests(PR_TRUE);
   }
 }
 
 nsresult
 nsSVGFEImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                 nsIContent* aBindingParent,
                                 PRBool aCompileEventHandlers)
 {
   nsresult rv = nsSVGFEImageElementBase::BindToTree(aDocument, aParent,
                                                     aBindingParent,
                                                     aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (HasAttr(kNameSpaceID_XLink, nsGkAtoms::href)) {
+  if (mStringAttributes[HREF].IsExplicitlySet()) {
     // FIXME: Bug 660963 it would be nice if we could just have
     // ClearBrokenState update our state and do it fast...
     ClearBrokenState();
     RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
     nsContentUtils::AddScriptRunner(
       NS_NewRunnableMethod(this, &nsSVGFEImageElement::MaybeLoadSVGImage));
   }
 
@@ -5505,20 +5539,16 @@ nsSVGFEImageElement::GetHref(nsIDOMSVGAn
 // nsIDOMSVGFEImageElement methods
 
 nsresult
 nsSVGFEImageElement::Filter(nsSVGFilterInstance *instance,
                             const nsTArray<const Image*>& aSources,
                             const Image* aTarget,
                             const nsIntRect& rect)
 {
-#ifdef DEBUG_tor
-  fprintf(stderr, "FILTER IMAGE rect: %d,%d  %dx%d\n",
-          rect.x, rect.y, rect.width, rect.height);
-#endif
   nsIFrame* frame = GetPrimaryFrame();
   if (!frame) return NS_ERROR_FAILURE;
 
   nsCOMPtr<imgIRequest> currentRequest;
   GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
              getter_AddRefs(currentRequest));
 
   nsCOMPtr<imgIContainer> imageContainer;
@@ -5821,21 +5851,16 @@ NS_IMETHODIMP nsSVGFEDisplacementMapElem
 }
 
 nsresult
 nsSVGFEDisplacementMapElement::Filter(nsSVGFilterInstance *instance,
                                       const nsTArray<const Image*>& aSources,
                                       const Image* aTarget,
                                       const nsIntRect& rect)
 {
-#ifdef DEBUG_tor
-  fprintf(stderr, "FILTER DISPLACEMENT rect: %d,%d  %dx%d\n",
-          rect.x, rect.y, rect.width, rect.height);
-#endif
-
   float scale = mNumberAttributes[SCALE].GetAnimValue();
   if (scale == 0.0f) {
     CopyRect(aTarget, aSources[0], rect);
     return NS_OK;
   }
 
   PRInt32 width = instance->GetSurfaceWidth();
   PRInt32 height = instance->GetSurfaceHeight();
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -41,16 +41,17 @@
 #include "nsSVGLength2.h"
 #include "nsIFrame.h"
 #include "gfxRect.h"
 #include "gfxImageSurface.h"
 #include "nsIDOMSVGFilters.h"
 
 class nsSVGFilterResource;
 class nsSVGString;
+class nsSVGNumberPair;
 class nsSVGFilterInstance;
 
 struct nsSVGStringInfo {
   nsSVGStringInfo(const nsSVGString* aString,
                   nsSVGElement *aElement) :
     mString(aString), mElement(aElement) {}
 
   const nsSVGString* mString;
@@ -108,17 +109,17 @@ protected:
     nsIntRect mDataRect; // rect in mSource and mTarget to operate on
     PRPackedBool mRescaling;
   };
 
   ScaleInfo SetupScalingFilter(nsSVGFilterInstance *aInstance,
                                const Image *aSource,
                                const Image *aTarget,
                                const nsIntRect& aDataRect,
-                               nsSVGNumber2 *aUnitX, nsSVGNumber2 *aUnitY);
+                               nsSVGNumberPair *aUnit);
 
   void FinishScalingFilter(ScaleInfo *aScaleInfo);
 
 public:
   ColorModel
   GetInputColorModel(nsSVGFilterInstance* aInstance, PRInt32 aInputIndex,
                      Image* aImage) {
     return ColorModel(
@@ -213,18 +214,20 @@ protected:
     return style->GetStyleSVG()->mColorInterpolationFilters ==
              NS_STYLE_COLOR_INTERPOLATION_SRGB;
   }
 
   // nsSVGElement specializations:
   virtual LengthAttributesInfo GetLengthInfo();
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateNumber(PRUint8 aAttrEnum);
+  virtual void DidAnimateNumberPair(PRUint8 aAttrEnum);
   virtual void DidAnimateNumberList(PRUint8 aAttrEnum);
   virtual void DidAnimateInteger(PRUint8 aAttrEnum);
+  virtual void DidAnimateIntegerPair(PRUint8 aAttrEnum);
   virtual void DidAnimateEnum(PRUint8 aAttrEnum);
   virtual void DidAnimateBoolean(PRUint8 aAttrEnum);
   virtual void DidAnimatePreserveAspectRatio();
   virtual void DidAnimateString(PRUint8 aAttrEnum);
 
   // nsIDOMSVGFitlerPrimitiveStandardAttributes values
   enum { X, Y, WIDTH, HEIGHT };
   nsSVGLength2 mLengthAttributes[4];
--- a/content/svg/content/src/nsSVGImageElement.cpp
+++ b/content/svg/content/src/nsSVGImageElement.cpp
@@ -186,34 +186,34 @@ nsSVGImageElement::AfterSetAttr(PRInt32 
   }
   return nsSVGImageElementBase::AfterSetAttr(aNamespaceID, aName,
                                              aValue, aNotify);
 }
 
 void
 nsSVGImageElement::MaybeLoadSVGImage()
 {
-  if (HasAttr(kNameSpaceID_XLink, nsGkAtoms::href) &&
+  if (mStringAttributes[HREF].IsExplicitlySet() &&
       (NS_FAILED(LoadSVGImage(PR_FALSE, PR_TRUE)) ||
        !LoadingEnabled())) {
     CancelImageRequests(PR_TRUE);
   }
 }
 
 nsresult
 nsSVGImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers)
 {
   nsresult rv = nsSVGImageElementBase::BindToTree(aDocument, aParent,
                                                   aBindingParent,
                                                   aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (HasAttr(kNameSpaceID_XLink, nsGkAtoms::href)) {
+  if (mStringAttributes[HREF].IsExplicitlySet()) {
     // FIXME: Bug 660963 it would be nice if we could just have
     // ClearBrokenState update our state and do it fast...
     ClearBrokenState();
     RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
     nsContentUtils::AddScriptRunner(
       NS_NewRunnableMethod(this, &nsSVGImageElement::MaybeLoadSVGImage));
   }
 
--- a/content/svg/content/src/nsSVGInteger.cpp
+++ b/content/svg/content/src/nsSVGInteger.cpp
@@ -52,66 +52,85 @@ DOMCI_DATA(SVGAnimatedInteger, nsSVGInte
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGInteger::DOMAnimatedInteger)
   NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedInteger)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGAnimatedInteger)
 NS_INTERFACE_MAP_END
 
 /* Implementation */
 
-nsresult
-nsSVGInteger::SetBaseValueString(const nsAString &aValueAsString,
-                                 nsSVGElement *aSVGElement,
-                                 PRBool aDoSetAttr)
+static nsresult
+GetValueFromString(const nsAString &aValueAsString,
+                   PRInt32 *aValue)
 {
   NS_ConvertUTF16toUTF8 value(aValueAsString);
   const char *str = value.get();
 
   if (NS_IsAsciiWhitespace(*str))
     return NS_ERROR_DOM_SYNTAX_ERR;
   
   char *rest;
-  PRInt32 val = strtol(str, &rest, 10);
+  *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;
+}
 
-  if (val != mBaseVal) {
-    mBaseVal = mAnimVal = val;
+nsresult
+nsSVGInteger::SetBaseValueString(const nsAString &aValueAsString,
+                                 nsSVGElement *aSVGElement,
+                                 PRBool aDoSetAttr)
+{
+  PRInt32 value;
+
+  nsresult rv = GetValueFromString(aValueAsString, &value);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  mIsBaseSet = PR_TRUE;
+  mBaseVal = value;
+  if (!mIsAnimated) {
+    mAnimVal = mBaseVal;
+  }
 #ifdef MOZ_SMIL
-    if (mIsAnimated) {
-      aSVGElement->AnimationNeedsResample();
-    }
+  else {
+    aSVGElement->AnimationNeedsResample();
+  }
 #endif
-  }
   return NS_OK;
 }
 
 void
 nsSVGInteger::GetBaseValueString(nsAString & aValueAsString)
 {
-  nsAutoString s;
-  s.AppendInt(mBaseVal);
-  aValueAsString.Assign(s);
+  aValueAsString.Truncate();
+  aValueAsString.AppendInt(mBaseVal);
 }
 
 void
 nsSVGInteger::SetBaseValue(int aValue,
                            nsSVGElement *aSVGElement,
                            PRBool aDoSetAttr)
 {
-  if (aValue != mBaseVal) {
-    mBaseVal = mAnimVal = aValue;
-    aSVGElement->DidChangeInteger(mAttrEnum, aDoSetAttr);
+  mBaseVal = aValue;
+  mIsBaseSet = PR_TRUE;
+  if (!mIsAnimated) {
+    mAnimVal = mBaseVal;
+  }
 #ifdef MOZ_SMIL
-    if (mIsAnimated) {
-      aSVGElement->AnimationNeedsResample();
-    }
+  else {
+    aSVGElement->AnimationNeedsResample();
+  }
 #endif
-  }
+  aSVGElement->DidChangeInteger(mAttrEnum, aDoSetAttr);
 }
 
 void
 nsSVGInteger::SetAnimValue(int aValue, nsSVGElement *aSVGElement)
 {
   mAnimVal = aValue;
   mIsAnimated = PR_TRUE;
   aSVGElement->DidAnimateInteger(mAttrEnum);
@@ -137,26 +156,21 @@ nsSVGInteger::ToSMILAttr(nsSVGElement *a
 }
 
 nsresult
 nsSVGInteger::SMILInteger::ValueFromString(const nsAString& aStr,
                                            const nsISMILAnimationElement* /*aSrcElement*/,
                                            nsSMILValue& aValue,
                                            PRBool& aPreventCachingOfSandwich) const
 {
-  NS_ConvertUTF16toUTF8 value(aStr);
-  const char *str = value.get();
+  PRInt32 val;
 
-  if (NS_IsAsciiWhitespace(*str))
-    return NS_ERROR_FAILURE;
-
-  char *rest;
-  PRInt32 val = strtol(str, &rest, 10);
-  if (rest == str || *rest != '\0') {
-    return NS_ERROR_FAILURE;
+  nsresult rv = GetValueFromString(aStr, &val);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
   nsSMILValue smilVal(&SMILIntegerType::sSingleton);
   smilVal.mU.mInt = val;
   aValue = smilVal;
   aPreventCachingOfSandwich = PR_FALSE;
   return NS_OK;
 }
--- a/content/svg/content/src/nsSVGInteger.h
+++ b/content/svg/content/src/nsSVGInteger.h
@@ -44,44 +44,54 @@
 class nsSVGInteger
 {
 
 public:
   void Init(PRUint8 aAttrEnum = 0xff, PRInt32 aValue = 0) {
     mAnimVal = mBaseVal = aValue;
     mAttrEnum = aAttrEnum;
     mIsAnimated = PR_FALSE;
+    mIsBaseSet = PR_FALSE;
   }
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement,
                               PRBool aDoSetAttr);
   void GetBaseValueString(nsAString& aValue);
 
   void SetBaseValue(PRInt32 aValue, nsSVGElement *aSVGElement, PRBool aDoSetAttr);
   PRInt32 GetBaseValue() const
     { return mBaseVal; }
 
   void SetAnimValue(int aValue, nsSVGElement *aSVGElement);
   int GetAnimValue() const
     { return mAnimVal; }
+
+  // Returns PR_TRUE if the animated value of this integer has been explicitly
+  // set (either by animation, or by taking on the base value which has been
+  // explicitly set by markup or a DOM call), PR_FALSE otherwise.
+  // If this returns PR_FALSE, the animated value is still valid, that is,
+  // useable, and represents the default base value of the attribute.
+  PRBool IsExplicitlySet() const
+    { return mIsAnimated || mIsBaseSet; }
   
   nsresult ToDOMAnimatedInteger(nsIDOMSVGAnimatedInteger **aResult,
                                 nsSVGElement* aSVGElement);
 #ifdef MOZ_SMIL
   // Returns a new nsISMILAttr object that the caller must delete
   nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement);
 #endif // MOZ_SMIL
   
 private:
 
   PRInt32 mAnimVal;
   PRInt32 mBaseVal;
   PRUint8 mAttrEnum; // element specified tracking for attribute
   PRPackedBool mIsAnimated;
+  PRPackedBool mIsBaseSet;
 
 public:
   struct DOMAnimatedInteger : public nsIDOMSVGAnimatedInteger
   {
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_CYCLE_COLLECTION_CLASS(DOMAnimatedInteger)
 
     DOMAnimatedInteger(nsSVGInteger* aVal, nsSVGElement *aSVGElement)
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGIntegerPair.cpp
@@ -0,0 +1,261 @@
+/* -*- 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 Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 "nsSVGIntegerPair.h"
+#include "nsSVGUtils.h"
+#include "nsTextFormatter.h"
+#include "prdtoa.h"
+#ifdef MOZ_SMIL
+#include "nsSMILValue.h"
+#include "SVGIntegerPairSMILType.h"
+#endif // MOZ_SMIL
+
+using namespace mozilla;
+
+NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGIntegerPair::DOMAnimatedIntegerPair, mSVGElement)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGIntegerPair::DOMAnimatedIntegerPair)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGIntegerPair::DOMAnimatedIntegerPair)
+
+DOMCI_DATA(SVGAnimatedIntegerPair, nsSVGIntegerPair::DOMAnimatedIntegerPair)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGIntegerPair::DOMAnimatedIntegerPair)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedInteger)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGAnimatedIntegerPair)
+NS_INTERFACE_MAP_END
+
+/* Implementation */
+
+static nsresult
+ParseIntegerOptionalInteger(const nsAString& aValue,
+                            PRInt32 aValues[2])
+{
+  NS_ConvertUTF16toUTF8 value(aValue);
+  const char *str = value.get();
+
+  if (IsSVGWhitespace(*str))
+    return NS_ERROR_FAILURE;
+
+  char* rest;
+  PRInt32 x = strtol(str, &rest, 10);
+  PRInt32 y = x;
+
+  if (str == rest) {
+    // first value was illformed
+    return NS_ERROR_FAILURE;
+  }
+  
+  if (*rest != '\0') {
+    while (IsSVGWhitespace(*rest)) {
+      ++rest;
+    }
+    if (*rest == ',') {
+      ++rest;
+    }
+
+    y = strtol(rest, &rest, 10);
+    if (*rest != '\0') {
+      // second value was illformed or there was trailing content
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  aValues[0] = x;
+  aValues[1] = y;
+
+  return NS_OK;
+}
+
+nsresult
+nsSVGIntegerPair::SetBaseValueString(const nsAString &aValueAsString,
+                                    nsSVGElement *aSVGElement,
+                                    PRBool aDoSetAttr)
+{
+  PRInt32 val[2];
+
+  nsresult rv = ParseIntegerOptionalInteger(aValueAsString, val);
+
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  mBaseVal[0] = val[0];
+  mBaseVal[1] = val[1];
+  mIsBaseSet = PR_TRUE;
+  if (!mIsAnimated) {
+    mAnimVal[0] = mBaseVal[0];
+    mAnimVal[1] = mBaseVal[1];
+  }
+#ifdef MOZ_SMIL
+  else {
+    aSVGElement->AnimationNeedsResample();
+  }
+#endif
+
+  // We don't need to call DidChange* here - we're only called by
+  // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
+  // which takes care of notifying.
+  return NS_OK;
+}
+
+void
+nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString)
+{
+  aValueAsString.Truncate();
+  aValueAsString.AppendInt(mBaseVal[0]);
+  if (mBaseVal[0] != mBaseVal[1]) {
+    aValueAsString.AppendLiteral(", ");
+    aValueAsString.AppendInt(mBaseVal[1]);
+  }
+}
+
+void
+nsSVGIntegerPair::SetBaseValue(PRInt32 aValue, PairIndex aPairIndex,
+                               nsSVGElement *aSVGElement,
+                               PRBool aDoSetAttr)
+{
+  PRUint32 index = (aPairIndex == eFirst ? 0 : 1);
+  mBaseVal[index] = aValue;
+  mIsBaseSet = PR_TRUE;
+  if (!mIsAnimated) {
+    mAnimVal[index] = aValue;
+  }
+#ifdef MOZ_SMIL
+  else {
+    aSVGElement->AnimationNeedsResample();
+  }
+#endif
+  aSVGElement->DidChangeIntegerPair(mAttrEnum, aDoSetAttr);
+}
+
+void
+nsSVGIntegerPair::SetBaseValues(PRInt32 aValue1, PRInt32 aValue2,
+                                nsSVGElement *aSVGElement,
+                                PRBool aDoSetAttr)
+{
+  mBaseVal[0] = aValue1;
+  mBaseVal[1] = aValue2;
+  mIsBaseSet = PR_TRUE;
+  if (!mIsAnimated) {
+    mAnimVal[0] = aValue1;
+    mAnimVal[1] = aValue2;
+  }
+#ifdef MOZ_SMIL
+  else {
+    aSVGElement->AnimationNeedsResample();
+  }
+#endif
+  aSVGElement->DidChangeIntegerPair(mAttrEnum, aDoSetAttr);
+}
+
+void
+nsSVGIntegerPair::SetAnimValue(const PRInt32 aValue[2], nsSVGElement *aSVGElement)
+{
+  mAnimVal[0] = aValue[0];
+  mAnimVal[1] = aValue[1];
+  mIsAnimated = PR_TRUE;
+  aSVGElement->DidAnimateIntegerPair(mAttrEnum);
+}
+
+nsresult
+nsSVGIntegerPair::ToDOMAnimatedInteger(nsIDOMSVGAnimatedInteger **aResult,
+                                       PairIndex aIndex,
+                                       nsSVGElement *aSVGElement)
+{
+  *aResult = new DOMAnimatedIntegerPair(this, aIndex, aSVGElement);
+  NS_ADDREF(*aResult);
+  return NS_OK;
+}
+
+#ifdef MOZ_SMIL
+nsISMILAttr*
+nsSVGIntegerPair::ToSMILAttr(nsSVGElement *aSVGElement)
+{
+  return new SMILIntegerPair(this, aSVGElement);
+}
+
+nsresult
+nsSVGIntegerPair::SMILIntegerPair::ValueFromString(const nsAString& aStr,
+                                                   const nsISMILAnimationElement* /*aSrcElement*/,
+                                                   nsSMILValue& aValue,
+                                                   PRBool& aPreventCachingOfSandwich) const
+{
+  PRInt32 values[2];
+
+  nsresult rv = ParseIntegerOptionalInteger(aStr, values);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsSMILValue val(&SVGIntegerPairSMILType::sSingleton);
+  val.mU.mIntPair[0] = values[0];
+  val.mU.mIntPair[1] = values[1];
+  aValue = val;
+  aPreventCachingOfSandwich = PR_FALSE;
+
+  return NS_OK;
+}
+
+nsSMILValue
+nsSVGIntegerPair::SMILIntegerPair::GetBaseValue() const
+{
+  nsSMILValue val(&SVGIntegerPairSMILType::sSingleton);
+  val.mU.mIntPair[0] = mVal->mBaseVal[0];
+  val.mU.mIntPair[1] = mVal->mBaseVal[1];
+  return val;
+}
+
+void
+nsSVGIntegerPair::SMILIntegerPair::ClearAnimValue()
+{
+  if (mVal->mIsAnimated) {
+    mVal->SetAnimValue(mVal->mBaseVal, mSVGElement);
+    mVal->mIsAnimated = PR_FALSE;
+  }
+}
+
+nsresult
+nsSVGIntegerPair::SMILIntegerPair::SetAnimValue(const nsSMILValue& aValue)
+{
+  NS_ASSERTION(aValue.mType == &SVGIntegerPairSMILType::sSingleton,
+               "Unexpected type to assign animated value");
+  if (aValue.mType == &SVGIntegerPairSMILType::sSingleton) {
+    mVal->SetAnimValue(aValue.mU.mIntPair, mSVGElement);
+  }
+  return NS_OK;
+}
+#endif // MOZ_SMIL
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGIntegerPair.h
@@ -0,0 +1,162 @@
+/* -*- 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 Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * 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_SVGINTEGERPAIR_H__
+#define __NS_SVGINTEGERPAIR_H__
+
+#include "nsIDOMSVGAnimatedInteger.h"
+#include "nsSVGElement.h"
+#include "nsDOMError.h"
+
+#ifdef MOZ_SMIL
+#include "nsISMILAttr.h"
+class nsSMILValue;
+class nsISMILType;
+#endif // MOZ_SMIL
+
+class nsSVGIntegerPair
+{
+
+public:
+  enum PairIndex {
+    eFirst,
+    eSecond
+  };
+
+  void Init(PRUint8 aAttrEnum = 0xff, PRInt32 aValue1 = 0, PRInt32 aValue2 = 0) {
+    mAnimVal[0] = mBaseVal[0] = aValue1;
+    mAnimVal[1] = mBaseVal[1] = aValue2;
+    mAttrEnum = aAttrEnum;
+    mIsAnimated = PR_FALSE;
+    mIsBaseSet = PR_FALSE;
+  }
+
+  nsresult SetBaseValueString(const nsAString& aValue,
+                              nsSVGElement *aSVGElement,
+                              PRBool aDoSetAttr);
+  void GetBaseValueString(nsAString& aValue);
+
+  void SetBaseValue(PRInt32 aValue, PairIndex aIndex, nsSVGElement *aSVGElement, PRBool aDoSetAttr);
+  void SetBaseValues(PRInt32 aValue1, PRInt32 aValue2, nsSVGElement *aSVGElement, PRBool aDoSetAttr);
+  PRInt32 GetBaseValue(PairIndex aIndex) const
+    { return mBaseVal[aIndex == eFirst ? 0 : 1]; }
+  void SetAnimValue(const PRInt32 aValue[2], nsSVGElement *aSVGElement);
+  PRInt32 GetAnimValue(PairIndex aIndex) const
+    { return mAnimVal[aIndex == eFirst ? 0 : 1]; }
+
+  // Returns PR_TRUE if the animated value of this integer has been explicitly
+  // set (either by animation, or by taking on the base value which has been
+  // explicitly set by markup or a DOM call), PR_FALSE otherwise.
+  // If this returns PR_FALSE, the animated value is still valid, that is,
+  // useable, and represents the default base value of the attribute.
+  PRBool IsExplicitlySet() const
+    { return mIsAnimated || mIsBaseSet; }
+
+  nsresult ToDOMAnimatedInteger(nsIDOMSVGAnimatedInteger **aResult,
+                                PairIndex aIndex,
+                                nsSVGElement* aSVGElement);
+#ifdef MOZ_SMIL
+  // Returns a new nsISMILAttr object that the caller must delete
+  nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement);
+#endif // MOZ_SMIL
+
+private:
+
+  PRInt32 mAnimVal[2];
+  PRInt32 mBaseVal[2];
+  PRUint8 mAttrEnum; // element specified tracking for attribute
+  PRPackedBool mIsAnimated;
+  PRPackedBool mIsBaseSet;
+
+public:
+  struct DOMAnimatedIntegerPair : public nsIDOMSVGAnimatedInteger
+  {
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_CLASS(DOMAnimatedIntegerPair)
+
+    DOMAnimatedIntegerPair(nsSVGIntegerPair* aVal, PairIndex aIndex, nsSVGElement *aSVGElement)
+      : mVal(aVal), mSVGElement(aSVGElement), mIndex(aIndex) {}
+
+    nsSVGIntegerPair* mVal; // kept alive because it belongs to content
+    nsRefPtr<nsSVGElement> mSVGElement;
+    PairIndex mIndex; // are we the first or second integer
+
+    NS_IMETHOD GetBaseVal(PRInt32* aResult)
+      { *aResult = mVal->GetBaseValue(mIndex); return NS_OK; }
+    NS_IMETHOD SetBaseVal(PRInt32 aValue)
+      {
+        mVal->SetBaseValue(aValue, mIndex, mSVGElement, PR_TRUE);
+        return NS_OK;
+      }
+
+    // Script may have modified animation parameters or timeline -- DOM getters
+    // need to flush any resample requests to reflect these modifications.
+    NS_IMETHOD GetAnimVal(PRInt32* aResult)
+    {
+#ifdef MOZ_SMIL
+      mSVGElement->FlushAnimations();
+#endif
+      *aResult = mVal->GetAnimValue(mIndex);
+      return NS_OK;
+    }
+  };
+
+#ifdef MOZ_SMIL
+  struct SMILIntegerPair : public nsISMILAttr
+  {
+  public:
+    SMILIntegerPair(nsSVGIntegerPair* aVal, nsSVGElement* aSVGElement)
+      : mVal(aVal), mSVGElement(aSVGElement) {}
+
+    // These will stay alive because a nsISMILAttr only lives as long
+    // as the Compositing step, and DOM elements don't get a chance to
+    // die during that.
+    nsSVGIntegerPair* mVal;
+    nsSVGElement* mSVGElement;
+
+    // nsISMILAttr methods
+    virtual nsresult ValueFromString(const nsAString& aStr,
+                                     const nsISMILAnimationElement* aSrcElement,
+                                     nsSMILValue& aValue,
+                                     PRBool& aPreventCachingOfSandwich) const;
+    virtual nsSMILValue GetBaseValue() const;
+    virtual void ClearAnimValue();
+    virtual nsresult SetAnimValue(const nsSMILValue& aValue);
+  };
+#endif // MOZ_SMIL
+};
+
+#endif //__NS_SVGINTEGERPAIR_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGNumberPair.cpp
@@ -0,0 +1,260 @@
+/* -*- 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 Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 "nsSVGNumberPair.h"
+#include "nsSVGUtils.h"
+#include "nsTextFormatter.h"
+#include "prdtoa.h"
+#ifdef MOZ_SMIL
+#include "nsSMILValue.h"
+#include "SVGNumberPairSMILType.h"
+#endif // MOZ_SMIL
+
+using namespace mozilla;
+
+NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGNumberPair::DOMAnimatedNumberPair, mSVGElement)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGNumberPair::DOMAnimatedNumberPair)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGNumberPair::DOMAnimatedNumberPair)
+
+DOMCI_DATA(SVGAnimatedNumberPair, nsSVGNumberPair::DOMAnimatedNumberPair)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGNumberPair::DOMAnimatedNumberPair)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedNumber)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGAnimatedNumberPair)
+NS_INTERFACE_MAP_END
+
+/* Implementation */
+
+static nsresult
+ParseNumberOptionalNumber(const nsAString& aValue,
+                          float aValues[2])
+{
+  NS_ConvertUTF16toUTF8 value(aValue);
+  const char *str = value.get();
+
+  if (IsSVGWhitespace(*str))
+    return NS_ERROR_FAILURE;
+
+  char* rest;
+  float x = float(PR_strtod(str, &rest));
+  float y = x;
+
+  if (str == rest || !NS_FloatIsFinite(x)) {
+    // first value was illformed
+    return NS_ERROR_FAILURE;
+  }
+  
+  if (*rest != '\0') {
+    while (IsSVGWhitespace(*rest)) {
+      ++rest;
+    }
+    if (*rest == ',') {
+      ++rest;
+    }
+
+    y = float(PR_strtod(rest, &rest));
+    if (*rest != '\0' || !NS_FloatIsFinite(y)) {
+      // second value was illformed or there was trailing content
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  aValues[0] = x;
+  aValues[1] = y;
+
+  return NS_OK;
+}
+
+nsresult
+nsSVGNumberPair::SetBaseValueString(const nsAString &aValueAsString,
+                                    nsSVGElement *aSVGElement,
+                                    PRBool aDoSetAttr)
+{
+  float val[2];
+
+  nsresult rv = ParseNumberOptionalNumber(aValueAsString, val);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  mBaseVal[0] = val[0];
+  mBaseVal[1] = val[1];
+  mIsBaseSet = PR_TRUE;
+  if (!mIsAnimated) {
+    mAnimVal[0] = mBaseVal[0];
+    mAnimVal[1] = mBaseVal[1];
+  }
+#ifdef MOZ_SMIL
+  else {
+    aSVGElement->AnimationNeedsResample();
+  }
+#endif
+
+  // We don't need to call DidChange* here - we're only called by
+  // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
+  // which takes care of notifying.
+  return NS_OK;
+}
+
+void
+nsSVGNumberPair::GetBaseValueString(nsAString &aValueAsString)
+{
+  aValueAsString.Truncate();
+  aValueAsString.AppendFloat(mBaseVal[0]);
+  if (mBaseVal[0] != mBaseVal[1]) {
+    aValueAsString.AppendLiteral(", ");
+    aValueAsString.AppendFloat(mBaseVal[1]);
+  }
+}
+
+void
+nsSVGNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex,
+                              nsSVGElement *aSVGElement,
+                              PRBool aDoSetAttr)
+{
+  PRUint32 index = (aPairIndex == eFirst ? 0 : 1);
+  mBaseVal[index] = aValue;
+  mIsBaseSet = PR_TRUE;
+  if (!mIsAnimated) {
+    mAnimVal[index] = aValue;
+  }
+#ifdef MOZ_SMIL
+  else {
+    aSVGElement->AnimationNeedsResample();
+  }
+#endif
+  aSVGElement->DidChangeNumberPair(mAttrEnum, aDoSetAttr);
+}
+
+void
+nsSVGNumberPair::SetBaseValues(float aValue1, float aValue2,
+                               nsSVGElement *aSVGElement,
+                               PRBool aDoSetAttr)
+{
+  mBaseVal[0] = aValue1;
+  mBaseVal[1] = aValue2;
+  mIsBaseSet = PR_TRUE;
+  if (!mIsAnimated) {
+    mAnimVal[0] = aValue1;
+    mAnimVal[1] = aValue2;
+  }
+#ifdef MOZ_SMIL
+  else {
+    aSVGElement->AnimationNeedsResample();
+  }
+#endif
+  aSVGElement->DidChangeNumberPair(mAttrEnum, aDoSetAttr);
+}
+
+void
+nsSVGNumberPair::SetAnimValue(const float aValue[2], nsSVGElement *aSVGElement)
+{
+  mAnimVal[0] = aValue[0];
+  mAnimVal[1] = aValue[1];
+  mIsAnimated = PR_TRUE;
+  aSVGElement->DidAnimateNumberPair(mAttrEnum);
+}
+
+nsresult
+nsSVGNumberPair::ToDOMAnimatedNumber(nsIDOMSVGAnimatedNumber **aResult,
+                                     PairIndex aIndex,
+                                     nsSVGElement *aSVGElement)
+{
+  *aResult = new DOMAnimatedNumberPair(this, aIndex, aSVGElement);
+  NS_ADDREF(*aResult);
+  return NS_OK;
+}
+
+#ifdef MOZ_SMIL
+nsISMILAttr*
+nsSVGNumberPair::ToSMILAttr(nsSVGElement *aSVGElement)
+{
+  return new SMILNumberPair(this, aSVGElement);
+}
+
+nsresult
+nsSVGNumberPair::SMILNumberPair::ValueFromString(const nsAString& aStr,
+                                                 const nsISMILAnimationElement* /*aSrcElement*/,
+                                                 nsSMILValue& aValue,
+                                                 PRBool& aPreventCachingOfSandwich) const
+{
+  float values[2];
+
+  nsresult rv = ParseNumberOptionalNumber(aStr, values);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsSMILValue val(&SVGNumberPairSMILType::sSingleton);
+  val.mU.mNumberPair[0] = values[0];
+  val.mU.mNumberPair[1] = values[1];
+  aValue = val;
+  aPreventCachingOfSandwich = PR_FALSE;
+
+  return NS_OK;
+}
+
+nsSMILValue
+nsSVGNumberPair::SMILNumberPair::GetBaseValue() const
+{
+  nsSMILValue val(&SVGNumberPairSMILType::sSingleton);
+  val.mU.mNumberPair[0] = mVal->mBaseVal[0];
+  val.mU.mNumberPair[1] = mVal->mBaseVal[1];
+  return val;
+}
+
+void
+nsSVGNumberPair::SMILNumberPair::ClearAnimValue()
+{
+  if (mVal->mIsAnimated) {
+    mVal->SetAnimValue(mVal->mBaseVal, mSVGElement);
+    mVal->mIsAnimated = PR_FALSE;
+  }
+}
+
+nsresult
+nsSVGNumberPair::SMILNumberPair::SetAnimValue(const nsSMILValue& aValue)
+{
+  NS_ASSERTION(aValue.mType == &SVGNumberPairSMILType::sSingleton,
+               "Unexpected type to assign animated value");
+  if (aValue.mType == &SVGNumberPairSMILType::sSingleton) {
+    mVal->SetAnimValue(aValue.mU.mNumberPair, mSVGElement);
+  }
+  return NS_OK;
+}
+#endif // MOZ_SMIL
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGNumberPair.h
@@ -0,0 +1,164 @@
+/* -*- 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 Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * 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_SVGNUMBERPAIR_H__
+#define __NS_SVGNUMBERPAIR_H__
+
+#include "nsIDOMSVGNumber.h"
+#include "nsIDOMSVGAnimatedNumber.h"
+#include "nsSVGElement.h"
+#include "nsDOMError.h"
+
+#ifdef MOZ_SMIL
+#include "nsISMILAttr.h"
+class nsSMILValue;
+class nsISMILType;
+#endif // MOZ_SMIL
+
+class nsSVGNumberPair
+{
+
+public:
+  enum PairIndex {
+    eFirst,
+    eSecond
+  };
+
+  void Init(PRUint8 aAttrEnum = 0xff, float aValue1 = 0, float aValue2 = 0) {
+    mAnimVal[0] = mBaseVal[0] = aValue1;
+    mAnimVal[1] = mBaseVal[1] = aValue2;
+    mAttrEnum = aAttrEnum;
+    mIsAnimated = PR_FALSE;
+    mIsBaseSet = PR_FALSE;
+  }
+
+  nsresult SetBaseValueString(const nsAString& aValue,
+                              nsSVGElement *aSVGElement,
+                              PRBool aDoSetAttr);
+  void GetBaseValueString(nsAString& aValue);
+
+  void SetBaseValue(float aValue, PairIndex aIndex, nsSVGElement *aSVGElement, PRBool aDoSetAttr);
+  void SetBaseValues(float aValue1, float aValue2, nsSVGElement *aSVGElement, PRBool aDoSetAttr);
+  float GetBaseValue(PairIndex aIndex) const
+    { return mBaseVal[aIndex == eFirst ? 0 : 1]; }
+  void SetAnimValue(const float aValue[2], nsSVGElement *aSVGElement);
+  float GetAnimValue(PairIndex aIndex) const
+    { return mAnimVal[aIndex == eFirst ? 0 : 1]; }
+
+  // Returns PR_TRUE if the animated value of this number has been explicitly
+  // set (either by animation, or by taking on the base value which has been
+  // explicitly set by markup or a DOM call), PR_FALSE otherwise.
+  // If this returns PR_FALSE, the animated value is still valid, that is,
+  // useable, and represents the default base value of the attribute.
+  PRBool IsExplicitlySet() const
+    { return mIsAnimated || mIsBaseSet; }
+
+  nsresult ToDOMAnimatedNumber(nsIDOMSVGAnimatedNumber **aResult,
+                               PairIndex aIndex,
+                               nsSVGElement* aSVGElement);
+#ifdef MOZ_SMIL
+  // Returns a new nsISMILAttr object that the caller must delete
+  nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement);
+#endif // MOZ_SMIL
+
+private:
+
+  float mAnimVal[2];
+  float mBaseVal[2];
+  PRUint8 mAttrEnum; // element specified tracking for attribute
+  PRPackedBool mIsAnimated;
+  PRPackedBool mIsBaseSet;
+
+public:
+  struct DOMAnimatedNumberPair : public nsIDOMSVGAnimatedNumber
+  {
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_CLASS(DOMAnimatedNumberPair)
+
+    DOMAnimatedNumberPair(nsSVGNumberPair* aVal, PairIndex aIndex, nsSVGElement *aSVGElement)
+      : mVal(aVal), mSVGElement(aSVGElement), mIndex(aIndex) {}
+
+    nsSVGNumberPair* mVal; // kept alive because it belongs to content
+    nsRefPtr<nsSVGElement> mSVGElement;
+    PairIndex mIndex; // are we the first or second number
+
+    NS_IMETHOD GetBaseVal(float* aResult)
+      { *aResult = mVal->GetBaseValue(mIndex); return NS_OK; }
+    NS_IMETHOD SetBaseVal(float aValue)
+      {
+        NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
+        mVal->SetBaseValue(aValue, mIndex, mSVGElement, PR_TRUE);
+        return NS_OK;
+      }
+
+    // Script may have modified animation parameters or timeline -- DOM getters
+    // need to flush any resample requests to reflect these modifications.
+    NS_IMETHOD GetAnimVal(float* aResult)
+    {
+#ifdef MOZ_SMIL
+      mSVGElement->FlushAnimations();
+#endif
+      *aResult = mVal->GetAnimValue(mIndex);
+      return NS_OK;
+    }
+  };
+
+#ifdef MOZ_SMIL
+  struct SMILNumberPair : public nsISMILAttr
+  {
+  public:
+    SMILNumberPair(nsSVGNumberPair* aVal, nsSVGElement* aSVGElement)
+      : mVal(aVal), mSVGElement(aSVGElement) {}
+
+    // These will stay alive because a nsISMILAttr only lives as long
+    // as the Compositing step, and DOM elements don't get a chance to
+    // die during that.
+    nsSVGNumberPair* mVal;
+    nsSVGElement* mSVGElement;
+
+    // nsISMILAttr methods
+    virtual nsresult ValueFromString(const nsAString& aStr,
+                                     const nsISMILAnimationElement* aSrcElement,
+                                     nsSMILValue& aValue,
+                                     PRBool& aPreventCachingOfSandwich) const;
+    virtual nsSMILValue GetBaseValue() const;
+    virtual void ClearAnimValue();
+    virtual nsresult SetAnimValue(const nsSMILValue& aValue);
+  };
+#endif // MOZ_SMIL
+};
+
+#endif //__NS_SVGNUMBERPAIR_H__
--- a/content/svg/content/src/nsSVGString.cpp
+++ b/content/svg/content/src/nsSVGString.cpp
@@ -60,16 +60,17 @@ NS_INTERFACE_MAP_END
 void
 nsSVGString::SetBaseValue(const nsAString& aValue,
                           nsSVGElement *aSVGElement,
                           PRBool aDoSetAttr)
 {
   NS_ASSERTION(aSVGElement, "Null element passed to SetBaseValue");
 
   if (aDoSetAttr) {
+    mIsBaseSet = PR_TRUE;
     aSVGElement->SetStringBaseValue(mAttrEnum, aValue);
   }
 #ifdef MOZ_SMIL
   if (mAnimVal) {
     aSVGElement->AnimationNeedsResample();
   }
 #endif
 
--- a/content/svg/content/src/nsSVGString.h
+++ b/content/svg/content/src/nsSVGString.h
@@ -43,38 +43,48 @@
 
 class nsSVGString
 {
 
 public:
   void Init(PRUint8 aAttrEnum) {
     mAnimVal = nsnull;
     mAttrEnum = aAttrEnum;
+    mIsBaseSet = PR_FALSE;
   }
 
   void SetBaseValue(const nsAString& aValue,
                     nsSVGElement *aSVGElement,
                     PRBool aDoSetAttr);
   void GetBaseValue(nsAString& aValue, nsSVGElement *aSVGElement) const
     { aSVGElement->GetStringBaseValue(mAttrEnum, aValue); }
 
   void SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement);
   void GetAnimValue(nsAString& aValue, const nsSVGElement *aSVGElement) const;
 
+  // Returns PR_TRUE if the animated value of this string has been explicitly
+  // set (either by animation, or by taking on the base value which has been
+  // explicitly set by markup or a DOM call), PR_FALSE otherwise.
+  // If this returns PR_FALSE, the animated value is still valid, that is,
+  // useable, and represents the default base value of the attribute.
+  PRBool IsExplicitlySet() const
+    { return !!mAnimVal || mIsBaseSet; }
+
   nsresult ToDOMAnimatedString(nsIDOMSVGAnimatedString **aResult,
                                nsSVGElement *aSVGElement);
 #ifdef MOZ_SMIL
   // Returns a new nsISMILAttr object that the caller must delete
   nsISMILAttr* ToSMILAttr(nsSVGElement *aSVGElement);
 #endif // MOZ_SMIL
 
 private:
 
   nsAutoPtr<nsString> mAnimVal;
   PRUint8 mAttrEnum; // element specified tracking for attribute
+  PRPackedBool mIsBaseSet;
 
 public:
   struct DOMAnimatedString : public nsIDOMSVGAnimatedString
   {
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_CYCLE_COLLECTION_CLASS(DOMAnimatedString)
 
     DOMAnimatedString(nsSVGString *aVal, nsSVGElement *aSVGElement)
--- a/content/svg/content/test/dataTypes-helper.svg
+++ b/content/svg/content/test/dataTypes-helper.svg
@@ -1,17 +1,20 @@
 <?xml version="1.0"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="750">
-	<defs>
-		<filter id="filter">
-			<!-- <boolean> (preserveAlpha) -->
-			<!-- <enum> (edgeMode) -->
-			<!-- <number> (divisor) -->
-			<!-- <integer> (targetX) -->
-			<!-- <string> (result) -->
-			<feConvolveMatrix id="convolve"/>
-		</filter>
-		<!-- <angle> (orient) -->
-		<!-- <length> (markerWidth) -->
-		<!-- <preserveAspectRatio> (preserveAspectRatio) -->
-		<marker id="marker"/>
-	</defs>
+  <defs>
+    <!-- <integer-optional-integer> (filterRes) -->
+    <filter id="filter">
+      <!-- <boolean> (preserveAlpha) -->
+      <!-- <enum> (edgeMode) -->
+      <!-- <number> (divisor) -->
+      <!-- <integer> (targetX) -->
+      <!-- <string> (result) -->
+      <feConvolveMatrix id="convolve"/>
+      <!-- <number-optional-number> (stdDeviation) -->
+      <feGaussianBlur id="blur"/>
+    </filter>
+    <!-- <angle> (orient) -->
+    <!-- <length> (markerWidth) -->
+    <!-- <preserveAspectRatio> (preserveAspectRatio) -->
+    <marker id="marker"/>
+  </defs>
 </svg>
--- a/content/svg/content/test/test_dataTypes.html
+++ b/content/svg/content/test/test_dataTypes.html
@@ -20,16 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script class="testbody" type="application/javascript">
 SimpleTest.waitForExplicitFinish();
 
 function runTests()
 {
   var doc = $("svg").contentWindow.document;
   var filter = doc.getElementById("filter");
   var convolve = doc.getElementById("convolve");
+  var blur = doc.getElementById("blur");
   var marker = doc.getElementById("marker");
 
   // class attribute
   filter.setAttribute("class", "foo");
   is(filter.getAttribute("class"), "foo", "class attribute");
   is(filter.className.baseVal, "foo", "className baseVal");
   is(filter.className.animVal, "foo", "className animVal");
   filter.className.baseVal = "bar";
@@ -62,25 +63,69 @@ function runTests()
   convolve.setAttribute("divisor", "12.5");
   is(convolve.divisor.baseVal, 12.5, "number baseVal");
   is(convolve.divisor.animVal, 12.5, "number animVal");
 
   convolve.divisor.baseVal = 7.5;
   is(convolve.divisor.animVal, 7.5, "number animVal");
   is(convolve.getAttribute("divisor"), "7.5", "number attribute");
 
+  // number-optional-number attribute
+
+  blur.setAttribute("stdDeviation", "20.5");
+  is(blur.stdDeviationX.baseVal, 20.5, "number-optional-number first baseVal");
+  is(blur.stdDeviationX.animVal, 20.5, "number-optional-number first animVal");
+  is(blur.stdDeviationY.baseVal, 20.5, "number-optional-number second baseVal");
+  is(blur.stdDeviationY.animVal, 20.5, "number-optional-number second animVal");
+
+  blur.stdDeviationX.baseVal = 8.5;
+  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");
+
   // integer attribute
 
   convolve.setAttribute("targetX", "12");
   is(convolve.targetX.baseVal, 12, "integer baseVal");
   is(convolve.targetX.animVal, 12, "integer animVal");
   convolve.targetX.baseVal = 7;
   is(convolve.targetX.animVal, 7, "integer animVal");
   is(convolve.getAttribute("targetX"), "7", "integer attribute");
 
+  // integer-optional-integer attribute
+
+  filter.setAttribute("filterRes", "100");
+  is(filter.filterResX.baseVal, 100, "integer-optional-integer first baseVal");
+  is(filter.filterResX.animVal, 100, "integer-optional-integer first animVal");
+  is(filter.filterResY.baseVal, 100, "integer-optional-integer second baseVal");
+  is(filter.filterResY.animVal, 100, "integer-optional-integer second animVal");
+
+  filter.filterResX.baseVal = 50;
+  is(filter.filterResX.animVal, 50, "integer-optional-integer first animVal");
+  is(filter.filterResY.animVal, 100, "integer-optional-integer second animVal");
+  is(filter.getAttribute("filterRes"), "50, 100", "integer-optional-integer attribute");
+
+  filter.filterResY.baseVal = 50;
+  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");
+
   // angle attribute
 
   marker.setAttribute("orient", "90deg");
   is(marker.orientAngle.baseVal.value, 90, "angle baseVal");
   is(marker.orientAngle.animVal.value, 90, "angle animVal");
 
   var baseAngle = marker.orientAngle.baseVal;
   var animAngle = marker.orientAngle.animVal;
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1157,22 +1157,26 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedAngle, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedBoolean, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedEnumeration, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedInteger, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(SVGAnimatedIntegerPair, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedLength, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedLengthList, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedNumber, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)    
+  NS_DEFINE_CLASSINFO_DATA(SVGAnimatedNumberPair, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)    
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedNumberList, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)    
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedPreserveAspectRatio, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedRect, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedString, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -3674,28 +3678,36 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedEnumeration, nsIDOMSVGAnimatedEnumeration)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedEnumeration)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedInteger, nsIDOMSVGAnimatedInteger)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedInteger)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedIntegerPair, nsIDOMSVGAnimatedInteger)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedInteger)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedLength, nsIDOMSVGAnimatedLength)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedLength)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedLengthList, nsIDOMSVGAnimatedLengthList)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedLengthList)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedNumber, nsIDOMSVGAnimatedNumber)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedNumber)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedNumberPair, nsIDOMSVGAnimatedNumber)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedNumber)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedNumberList, nsIDOMSVGAnimatedNumberList)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedNumberList)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedPreserveAspectRatio, nsIDOMSVGAnimatedPreserveAspectRatio)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedPreserveAspectRatio)
   DOM_CLASSINFO_MAP_END
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -300,19 +300,21 @@ DOMCI_CLASS(SVGTSpanElement)
 DOMCI_CLASS(SVGUseElement)
 
 // other SVG classes
 DOMCI_CLASS(SVGAngle)
 DOMCI_CLASS(SVGAnimatedAngle)
 DOMCI_CLASS(SVGAnimatedBoolean)
 DOMCI_CLASS(SVGAnimatedEnumeration)
 DOMCI_CLASS(SVGAnimatedInteger)
+DOMCI_CLASS(SVGAnimatedIntegerPair)
 DOMCI_CLASS(SVGAnimatedLength)
 DOMCI_CLASS(SVGAnimatedLengthList)
 DOMCI_CLASS(SVGAnimatedNumber)
+DOMCI_CLASS(SVGAnimatedNumberPair)
 DOMCI_CLASS(SVGAnimatedNumberList)
 DOMCI_CLASS(SVGAnimatedPreserveAspectRatio)
 DOMCI_CLASS(SVGAnimatedRect)
 DOMCI_CLASS(SVGAnimatedString)
 DOMCI_CLASS(SVGAnimatedTransformList)
 DOMCI_CLASS(SVGEvent)
 DOMCI_CLASS(SVGException)
 DOMCI_CLASS(SVGLength)
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/anim-feGaussianBlur-01.svg
@@ -0,0 +1,25 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="setTimeAndSnapshot(1.9999, true)">
+  <title>Test animation of the "stdDeviation" &lt;number-optional-number&gt; attribute on "feGaussianBlur" elements</title>
+  <script xlink:href="smil-util.js" type="text/javascript"/>
+  <filter id="filter" x="0" y="0" width="1" height="1" filterRes="1000">
+    <feGaussianBlur stdDeviation="50">
+      <animate attributeName="stdDeviation"
+               calcMode="linear"
+               begin="0s" dur="2s"
+               from="100,100" to="0"
+               fill="freeze"/>
+    </feGaussianBlur>
+  </filter>
+  <rect width="100%" height="100%" fill="lime"/>
+  <g transform="translate(50, 0)">
+    <circle fill="red" cx="100" cy="100" r="98" transform="translate(50, 0)" filter="url(#filter)"/>
+  </g>
+  <circle fill="lime" cx="200" cy="100" r="100"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/anim-filter-filterRes-01.svg
@@ -0,0 +1,24 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="setTimeAndSnapshot(1, true)">
+  <title>Test animation of the "filterRes" &lt;integer-optional-integer&gt; attribute on "filter" elements</title>
+  <script xlink:href="smil-util.js" type="text/javascript"/>
+  <filter id="filter" x="0" y="0" width="1" height="1" filterRes="10 10">
+    <animate attributeName="filterRes"
+             calcMode="linear"
+             begin="0s" dur="2s"
+             from="0" to="2000 2000"
+             fill="freeze"/>
+    <feGaussianBlur stdDeviation="0.001"/>
+  </filter>
+  <rect width="100%" height="100%" fill="lime"/>
+  <g transform="translate(50, 0)">
+    <circle fill="red" cx="100" cy="100" r="98" transform="translate(50, 0)" filter="url(#filter)"/>
+  </g>
+  <circle fill="lime" cx="200" cy="100" r="100"/>
+</svg>
--- a/layout/reftests/svg/smil/reftest.list
+++ b/layout/reftests/svg/smil/reftest.list
@@ -93,19 +93,25 @@ fails == anim-fillcolor-1.svg      anim-
 # animate some <number> attributes:
 == anim-feComponentTransfer-01.svg lime.svg
 == anim-feDistantLight-01.svg anim-feDistantLight-01-ref.svg
 == anim-feOffset-01.svg lime.svg
 == anim-feSpotLight-01.svg anim-feSpotLight-01-ref.svg
 == anim-offset-01.svg lime.svg
 == anim-pathLength-01.svg anim-pathLength-01-ref.svg
 
+# animate some <number-optional-number> attributes:
+== anim-feGaussianBlur-01.svg lime.svg
+
 # animate some <integer> attributes:
 == anim-feTurbulence-numOctaves-01.svg anim-feTurbulence-numOctaves-01-ref.svg
 
+# animate some <integer-optional-integer> attributes:
+== anim-filter-filterRes-01.svg lime.svg
+
 # animate some <angle> attributes:
 == anim-marker-orient-01.svg lime.svg
 
 #animate points list:
 == anim-polygon-points-01.svg anim-polygon-points-01-ref.svg
 == anim-polyline-points-01.svg anim-polyline-points-01-ref.svg
 
 # animate path data:
--- a/layout/svg/base/src/nsSVGFilterFrame.cpp
+++ b/layout/svg/base/src/nsSVGFilterFrame.cpp
@@ -144,19 +144,21 @@ nsAutoFilterInstance::nsAutoFilterInstan
   }
 
   gfxMatrix userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
   
   // Calculate filterRes (the width and height of the pixel buffer of the
   // temporary offscreen surface that we'll paint into):
 
   gfxIntSize filterRes;
-  if (filter->HasAttr(kNameSpaceID_None, nsGkAtoms::filterRes)) {
-    PRInt32 filterResX, filterResY;
-    filter->GetAnimatedIntegerValues(&filterResX, &filterResY, nsnull);
+  if (filter->mIntegerPairAttributes[nsSVGFilterElement::FILTERRES].IsExplicitlySet()) {
+    PRInt32 filterResX =
+      filter->mIntegerPairAttributes[nsSVGFilterElement::FILTERRES].GetAnimValue(nsSVGIntegerPair::eFirst);
+    PRInt32 filterResY =
+      filter->mIntegerPairAttributes[nsSVGFilterElement::FILTERRES].GetAnimValue(nsSVGIntegerPair::eSecond);
     // XXX what if the 'filterRes' attribute has a bad value? error console warning?
 
     // We don't care if this overflows, because we can handle upscaling/
     // downscaling to filterRes
     PRBool overflow;
     filterRes =
       nsSVGUtils::ConvertToSurfaceSize(gfxSize(filterResX, filterResY),
                                        &overflow);
--- a/layout/svg/base/src/nsSVGFilterInstance.cpp
+++ b/layout/svg/base/src/nsSVGFilterInstance.cpp
@@ -134,23 +134,23 @@ nsSVGFilterInstance::ComputeFilterPrimit
     defaultFilterSubregion =
       gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
   }
 
   gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
     &fE->mLengthAttributes[nsSVGFE::X], mTargetBBox, mTargetFrame);
   gfxRect region = UserSpaceToFilterSpace(feArea);
 
-  if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::x))
+  if (!fE->mLengthAttributes[nsSVGFE::X].IsExplicitlySet())
     region.x = defaultFilterSubregion.X();
-  if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::y))
+  if (!fE->mLengthAttributes[nsSVGFE::Y].IsExplicitlySet())
     region.y = defaultFilterSubregion.Y();
-  if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::width))
+  if (!fE->mLengthAttributes[nsSVGFE::WIDTH].IsExplicitlySet())
     region.width = defaultFilterSubregion.Width();
-  if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::height))
+  if (!fE->mLengthAttributes[nsSVGFE::HEIGHT].IsExplicitlySet())
     region.height = defaultFilterSubregion.Height();
 
   // We currently require filter primitive subregions to be pixel-aligned.
   // Following the spec, any pixel partially in the region is included
   // in the region.
   region.RoundOut();
   aPrimitive->mImage.mFilterPrimitiveSubregion = region;
 }