Bug 515116. DeCOMify SVG length-list, and implement SMIL animation of length-list. r=longsonr, r=dholbert, sr=roc
authorJonathan Watt <jwatt@jwatt.org>
Fri, 16 Jul 2010 22:42:12 +0100
changeset 47840 9712aa5f77e17cbe24fd906ccadb87689af761eb
parent 47839 cc53b4e4e27797ef6aa0e05c3d765a69bd4ea2cf
child 47841 f6c93f02146c55b55209c6ae622e15a88cb28543
push idunknown
push userunknown
push dateunknown
reviewerslongsonr, dholbert, roc
bugs515116
milestone2.0b2pre
first release with
nightly win64
9712aa5f77e1 / 4.0b2pre / 20100716150536 / files
nightly linux32
nightly linux64
nightly mac
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly win64
Bug 515116. DeCOMify SVG length-list, and implement SMIL animation of length-list. r=longsonr, r=dholbert, sr=roc
content/smil/nsISMILType.h
content/smil/nsSMILValue.h
content/svg/content/src/DOMSVGAnimatedLengthList.cpp
content/svg/content/src/DOMSVGAnimatedLengthList.h
content/svg/content/src/DOMSVGLength.cpp
content/svg/content/src/DOMSVGLength.h
content/svg/content/src/DOMSVGLengthList.cpp
content/svg/content/src/DOMSVGLengthList.h
content/svg/content/src/Makefile.in
content/svg/content/src/SVGAnimatedLengthList.cpp
content/svg/content/src/SVGAnimatedLengthList.h
content/svg/content/src/SVGLength.cpp
content/svg/content/src/SVGLength.h
content/svg/content/src/SVGLengthList.cpp
content/svg/content/src/SVGLengthList.h
content/svg/content/src/SVGLengthListSMILType.cpp
content/svg/content/src/SVGLengthListSMILType.h
content/svg/content/src/nsSVGAnimatedLengthList.cpp
content/svg/content/src/nsSVGAnimatedLengthList.h
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGElement.h
content/svg/content/src/nsSVGFilters.cpp
content/svg/content/src/nsSVGLength.cpp
content/svg/content/src/nsSVGLength.h
content/svg/content/src/nsSVGLengthList.cpp
content/svg/content/src/nsSVGLengthList.h
content/svg/content/src/nsSVGSVGElement.cpp
content/svg/content/src/nsSVGStopElement.cpp
content/svg/content/src/nsSVGTextElement.cpp
content/svg/content/src/nsSVGTextPositioningElement.cpp
content/svg/content/src/nsSVGTextPositioningElement.h
content/svg/content/test/Makefile.in
content/svg/content/test/test_SVGxxxList.xhtml
layout/reftests/svg/smil/anim-text-x-y-dx-dy-01-ref.svg
layout/reftests/svg/smil/anim-text-x-y-dx-dy-01.svg
layout/reftests/svg/smil/reftest.list
layout/svg/base/src/nsISVGGlyphFragmentLeaf.h
layout/svg/base/src/nsSVGAFrame.cpp
layout/svg/base/src/nsSVGGlyphFrame.cpp
layout/svg/base/src/nsSVGGlyphFrame.h
layout/svg/base/src/nsSVGTextContainerFrame.cpp
layout/svg/base/src/nsSVGTextContainerFrame.h
layout/svg/base/src/nsSVGTextFrame.cpp
layout/svg/base/src/nsSVGTextPathFrame.cpp
layout/svg/base/src/nsSVGTextPathFrame.h
layout/svg/base/src/nsSVGUtils.cpp
layout/svg/base/src/nsSVGUtils.h
--- a/content/smil/nsISMILType.h
+++ b/content/smil/nsISMILType.h
@@ -47,19 +47,22 @@ class nsSMILValue;
 // nsISMILType: Interface for defining the basic operations needed for animating
 // a particular kind of data (e.g. lengths, colors, transformation matrices).
 //
 // This interface is never used directly but always through an nsSMILValue that
 // bundles together a pointer to a concrete implementation of this interface and
 // the data upon which it should operate.
 //
 // We keep the data and type separate rather than just providing different
-// subclasses of nsSMILValue as this allows nsSMILValues to be allocated on the
-// stack and directly assigned to one another provided performance benefits for
-// the animation code.
+// subclasses of nsSMILValue. This is so that sizeof(nsSMILValue) is the same
+// for all value types, allowing us to have a type-agnostic nsTArray of
+// nsSMILValue objects (actual objects, not pointers). It also allows most
+// nsSMILValues (except those that need to allocate extra memory for their
+// data) to be allocated on the stack and directly assigned to one another
+// provided performance benefits for the animation code.
 //
 // Note that different types have different capabilities. Roughly speaking there
 // are probably three main types:
 //
 // +---------------------+---------------+-------------+------------------+
 // | CATEGORY:           | DISCRETE      | LINEAR      | ADDITIVE         |
 // +---------------------+---------------+-------------+------------------+
 // | Example:            | strings,      | path data?  | lengths,         |
--- a/content/smil/nsSMILValue.h
+++ b/content/smil/nsSMILValue.h
@@ -37,16 +37,25 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef NS_SMILVALUE_H_
 #define NS_SMILVALUE_H_
 
 #include "nsISMILType.h"
 #include "nsSMILNullType.h"
 
+/**
+ * Although objects of this type are generally only created on the stack and
+ * only exist during the taking of a new time sample, that's not always the
+ * case. The nsSMILValue objects obtained from attributes' base values are
+ * cached so that the SMIL engine can make certain optimizations during a
+ * sample if the base value has not changed since the last sample (potentially
+ * avoiding recomposing). These nsSMILValue objects typically live much longer
+ * than a single sample.
+ */
 class nsSMILValue
 {
 public:
   nsSMILValue() : mU(), mType(&nsSMILNullType::sSingleton) { }
   explicit nsSMILValue(const nsISMILType* aType);
   nsSMILValue(const nsSMILValue& aVal);
 
   ~nsSMILValue()
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGAnimatedLengthList.cpp
@@ -0,0 +1,165 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 "DOMSVGAnimatedLengthList.h"
+#include "DOMSVGLengthList.h"
+#include "SVGAnimatedLengthList.h"
+#include "nsSVGElement.h"
+#include "nsCOMPtr.h"
+#include "nsSVGAttrTearoffTable.h"
+
+// See the architecture comment in this file's header.
+
+namespace mozilla {
+
+static nsSVGAttrTearoffTable<SVGAnimatedLengthList, DOMSVGAnimatedLengthList>
+  sSVGAnimatedLengthListTearoffTable;
+
+NS_SVG_VAL_IMPL_CYCLE_COLLECTION(DOMSVGAnimatedLengthList, mElement)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGAnimatedLengthList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGAnimatedLengthList)
+
+}
+DOMCI_DATA(SVGAnimatedLengthList, DOMSVGAnimatedLengthList)
+namespace mozilla {
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGAnimatedLengthList)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedLengthList)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGAnimatedLengthList)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+DOMSVGAnimatedLengthList::GetBaseVal(nsIDOMSVGLengthList **_retval)
+{
+  if (!mBaseVal) {
+    mBaseVal = new DOMSVGLengthList(this);
+  }
+  NS_ADDREF(*_retval = mBaseVal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGAnimatedLengthList::GetAnimVal(nsIDOMSVGLengthList **_retval)
+{
+  if (!mAnimVal) {
+    mAnimVal = new DOMSVGLengthList(this);
+  }
+  NS_ADDREF(*_retval = mAnimVal);
+  return NS_OK;
+}
+
+/* static */ already_AddRefed<DOMSVGAnimatedLengthList>
+DOMSVGAnimatedLengthList::GetDOMWrapper(SVGAnimatedLengthList *aList,
+                                        nsSVGElement *aElement,
+                                        PRUint8 aAttrEnum,
+                                        PRUint8 aAxis)
+{
+  DOMSVGAnimatedLengthList *wrapper =
+    sSVGAnimatedLengthListTearoffTable.GetTearoff(aList);
+  if (!wrapper) {
+    wrapper = new DOMSVGAnimatedLengthList(aElement, aAttrEnum, aAxis);
+    sSVGAnimatedLengthListTearoffTable.AddTearoff(aList, wrapper);
+  }
+  NS_ADDREF(wrapper);
+  return wrapper;
+}
+
+/* static */ DOMSVGAnimatedLengthList*
+DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(SVGAnimatedLengthList *aList)
+{
+  return sSVGAnimatedLengthListTearoffTable.GetTearoff(aList);
+}
+
+DOMSVGAnimatedLengthList::~DOMSVGAnimatedLengthList()
+{
+  // Script no longer has any references to us, to our base/animVal objects, or
+  // to any of their list items.
+  sSVGAnimatedLengthListTearoffTable.RemoveTearoff(&InternalAList());
+}
+
+void
+DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo(const SVGLengthList& aNewValue)
+{
+  // When the number of items in our internal counterpart's baseVal changes,
+  // we MUST keep our baseVal in sync. If we don't, script will either see a
+  // list that is too short and be unable to access indexes that should be
+  // valid, or else, MUCH WORSE, script will see a list that is too long and be
+  // able to access "items" at indexes that are out of bounds (read/write to
+  // bad memory)!!
+
+  if (mBaseVal) {
+    mBaseVal->InternalListLengthWillChange(aNewValue.Length());
+  }
+
+  // If our attribute is not animating, then our animVal mirrors our baseVal
+  // and we must sync its length too. (If our attribute is animating, then the
+  // SMIL engine takes care of calling InternalAnimValListWillChangeTo() if
+  // necessary.)
+
+  if (!IsAnimating()) {
+    InternalAnimValListWillChangeTo(aNewValue);
+  }
+}
+
+void
+DOMSVGAnimatedLengthList::InternalAnimValListWillChangeTo(const SVGLengthList& aNewValue)
+{
+  if (mAnimVal) {
+    mAnimVal->InternalListLengthWillChange(aNewValue.Length());
+  }
+}
+
+PRBool
+DOMSVGAnimatedLengthList::IsAnimating() const
+{
+  return InternalAList().IsAnimating();
+}
+
+SVGAnimatedLengthList&
+DOMSVGAnimatedLengthList::InternalAList()
+{
+  return *mElement->GetAnimatedLengthList(mAttrEnum);
+}
+
+const SVGAnimatedLengthList&
+DOMSVGAnimatedLengthList::InternalAList() const
+{
+  return *mElement->GetAnimatedLengthList(mAttrEnum);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGAnimatedLengthList.h
@@ -0,0 +1,229 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 ***** */
+
+#ifndef MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__
+#define MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__
+
+#include "nsIDOMSVGAnimatedLengthList.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+
+class nsSVGElement;
+
+namespace mozilla {
+
+class SVGAnimatedLengthList;
+class SVGLengthList;
+class DOMSVGLengthList;
+
+/**
+ * Class DOMSVGAnimatedLengthList
+ *
+ * This class is used to create the DOM tearoff objects that wrap internal
+ * SVGAnimatedLengthList objects. We have this internal-DOM split because DOM
+ * classes are relatively heavy-weight objects with non-optimal interfaces for
+ * internal code, and they're relatively infrequently used. Having separate
+ * internal and DOM classes does add complexity - especially for lists where
+ * the internal list and DOM lists (and their items) need to be kept in sync -
+ * but it keeps the internal classes light and fast, and in 99% of cases
+ * they're all that's used. DOM wrappers are only instantiated when script
+ * demands it.
+ *
+ * Ownership model:
+ *
+ * The diagram below shows the ownership model between the various DOM objects
+ * in the tree of DOM objects that correspond to an SVG length list attribute.
+ * The angled brackets ">" and "<" denote a reference from one object to
+ * another, where the "!" character denotes a strong reference, and the "~"
+ * character denotes a weak reference.
+ *
+ *      .----<!----.                .----<!----.        .----<!----.
+ *      |          |                |          |        |          |
+ *   element ~> DOMSVGAnimatedLengthList ~> DOMSVGLengthList ~> DOMSVGLength
+ *
+ * Rational:
+ *
+ * The following three paragraphs explain the main three requirements that must
+ * be met by any design. These are followed by an explanation of the rational
+ * behind our particular design.
+ *
+ * 1: DOMSVGAnimatedLengthList, DOMSVGLengthLists and DOMSVGLength get to their
+ * internal counterparts via their element, and they use their element to send
+ * out appropriate notifications when they change. Because of this, having
+ * their element disappear out from under them would be very bad. To keep their
+ * element alive at least as long as themselves, each of these classes must
+ * contain a _strong_ reference (directly or indirectly) to their element.
+ *
+ * 2: Another central requirement of any design is the SVG specification's
+ * requirement that script must always be given the exact same objects each
+ * time it accesses a given object in a DOM object tree corresponding to an SVG
+ * length list attribute. In practice "always" actually means "whenever script
+ * has kept a references to a DOM object it previously accessed", since a
+ * script will only be able to detect any difference in object identity if it
+ * has a previous reference to compare against.
+ *
+ * 3: The wiggle room in the "same object" requirement leads us to a third
+ * (self imposed) requirement: if script no longer has a reference to a given
+ * DOM object from an object tree corresponding to an SVG length list
+ * attribute, and if that object doesn't currently have any descendants, then
+ * that object should be released to free up memory.
+ *
+ * To help in understanding our current design, consider this BROKEN design:
+ *
+ *      .-------------------------------<!-------------------------.
+ *      |--------------------<!----------------.                   |
+ *      |----<!----.                           |                   |
+ *      |          |                           |                   |
+ *   element ~> DOMSVGAnimatedLengthList !> DOMSVGLengthList !> DOMSVGLength
+ *
+ * Having all the objects keep a reference directly to their element like this
+ * would reduce the number of dereferences that they need to make to get their
+ * internal counterpart. Hovewer, this design does not meet the "same object"
+ * requirement of the SVG specification. If script keeps a reference to a
+ * DOMSVGLength or DOMSVGLengthList object, but not to that object's
+ * DOMSVGAnimatedLengthList, then the DOMSVGAnimatedLengthList may be garbage
+ * collected. We'd then have no way to return the same DOMSVGLength /
+ * DOMSVGLengthList object that the script has a reference to if the script
+ * went looking for it via the DOMSVGAnimatedLengthList property on the
+ * element - we'd end up creating a fresh DOMSVGAnimatedLengthList, with no
+ * knowlegde of the existing DOMSVGLengthList or DOMSVGLength object.
+ *
+ * The way we solve this problem is by making sure that parent objects cannot
+ * die until all their children are dead by having child objects hold a strong
+ * reference to their parent object. Note that this design means that the child
+ * objects hold a strong reference to their element too, albeit indirectly via
+ * the strong reference to their parent object:
+ *
+ *      .----<!----.                .----<!----.        .----<!----.
+ *      |          |                |          |        |          |
+ *   element ~> DOMSVGAnimatedLengthList ~> DOMSVGLengthList ~> DOMSVGLength
+ *
+ * One drawback of this design is that objects must look up their parent
+ * chain to find their element, but that overhead is relatively small.
+ */
+class DOMSVGAnimatedLengthList : public nsIDOMSVGAnimatedLengthList
+{
+  friend class DOMSVGLengthList;
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGAnimatedLengthList)
+  NS_DECL_NSIDOMSVGANIMATEDLENGTHLIST
+
+  /**
+   * Factory method to create and return a DOMSVGAnimatedLengthList wrapper
+   * for a given internal SVGAnimatedLengthList object. The factory takes care
+   * of caching the object that it returns so that the same object can be
+   * returned for the given SVGAnimatedLengthList each time it is requested.
+   * The cached object is only removed from the cache when it is destroyed due
+   * to there being no more references to it or to any of its descendant
+   * objects. If that happens, any subsequent call requesting the DOM wrapper
+   * for the SVGAnimatedLengthList will naturally result in a new
+   * DOMSVGAnimatedLengthList being returned.
+   */
+  static already_AddRefed<DOMSVGAnimatedLengthList>
+    GetDOMWrapper(SVGAnimatedLengthList *aList,
+                  nsSVGElement *aElement,
+                  PRUint8 aAttrEnum,
+                  PRUint8 aAxis);
+
+  /**
+   * This method returns the DOMSVGAnimatedLengthList wrapper for an internal
+   * SVGAnimatedLengthList object if it currently has a wrapper. If it does
+   * not, then nsnull is returned.
+   */
+  static DOMSVGAnimatedLengthList*
+    GetDOMWrapperIfExists(SVGAnimatedLengthList *aList);
+
+  /**
+   * Called by internal code to notify us when we need to sync the length of
+   * our baseVal DOM list with its internal list. This is called just prior to
+   * the length of the internal baseVal list being changed so that any DOM list
+   * items that need to be removed from the DOM list can first get their values
+   * from their internal counterpart.
+   *
+   * The only time this method could fail is on OOM when trying to increase the
+   * length of the DOM list. If that happens then this method simply clears the
+   * list and returns. Callers just proceed as normal, and we simply accept
+   * that the DOM list will be empty (until successfully set to a new value).
+   */
+  void InternalBaseValListWillChangeTo(const SVGLengthList& aNewValue);
+  void InternalAnimValListWillChangeTo(const SVGLengthList& aNewValue);
+
+  /**
+   * Returns true if our attribute is animating (in which case our animVal is
+   * not simply a mirror of our baseVal).
+   */
+  PRBool IsAnimating() const;
+
+private:
+
+  /**
+   * Only our static GetDOMWrapper() factory method may create objects of our
+   * type.
+   */
+  DOMSVGAnimatedLengthList(nsSVGElement *aElement, PRUint8 aAttrEnum, PRUint8 aAxis)
+    : mBaseVal(nsnull)
+    , mAnimVal(nsnull)
+    , mElement(aElement)
+    , mAttrEnum(aAttrEnum)
+    , mAxis(aAxis)
+  {}
+
+  ~DOMSVGAnimatedLengthList();
+
+  /// Get a reference to this DOM wrapper object's internal counterpart.
+  SVGAnimatedLengthList& InternalAList();
+  const SVGAnimatedLengthList& InternalAList() const;
+
+  // Weak refs to our DOMSVGLengthList baseVal/animVal objects. These objects
+  // are friends and take care of clearing these pointers when they die, making
+  // these true weak references.
+  DOMSVGLengthList *mBaseVal;
+  DOMSVGLengthList *mAnimVal;
+
+  // Strong ref to our element to keep it alive. We hold this not only for
+  // ourself, but also for our base/animVal and all of their items.
+  nsRefPtr<nsSVGElement> mElement;
+
+  PRUint8 mAttrEnum;
+  PRUint8 mAxis;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGLength.cpp
@@ -0,0 +1,364 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 "DOMSVGLength.h"
+#include "DOMSVGLengthList.h"
+#include "DOMSVGAnimatedLengthList.h"
+#include "SVGLength.h"
+#include "SVGAnimatedLengthList.h"
+#include "nsSVGElement.h"
+#include "nsIDOMSVGLength.h"
+#include "nsDOMError.h"
+
+// See the architecture comment in DOMSVGAnimatedLengthList.h.
+
+namespace mozilla {
+
+// We could use NS_IMPL_CYCLE_COLLECTION_1, except that in Unlink() we need to
+// clear our list's weak ref to us to be safe. (The other option would be to
+// not unlink and rely on the breaking of the other edges in the cycle, as
+// NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
+NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLength)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLength)
+  // We may not belong to a list, so we must null check tmp->mList.
+  if (tmp->mList) {
+    tmp->mList->mItems[tmp->mListIndex] = nsnull;
+  }
+NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLength)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLength)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLength)
+
+}
+DOMCI_DATA(SVGLength, DOMSVGLength)
+namespace mozilla {
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLength)
+  NS_INTERFACE_MAP_ENTRY(DOMSVGLength) // pseudo-interface
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLength)
+NS_INTERFACE_MAP_END
+
+DOMSVGLength::DOMSVGLength(DOMSVGLengthList *aList,
+                           PRUint32 aAttrEnum,
+                           PRUint8 aListIndex,
+                           PRUint8 aIsAnimValItem)
+  : mList(aList)
+  , mListIndex(aListIndex)
+  , mAttrEnum(aAttrEnum)
+  , mIsAnimValItem(aIsAnimValItem)
+  , mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)
+  , mValue(0.0f)
+{
+#ifdef DEBUG
+  // These shifts are in sync with the flag member's in the header.
+  NS_ABORT_IF_FALSE(aList &&
+                    aAttrEnum < (1 << 22) &&
+                    aListIndex < (1 << 4) &&
+                    aIsAnimValItem < (1 << 1), "bad arg");
+  if (aIsAnimValItem &&
+      mListIndex >= Element()->GetAnimatedLengthList(mAttrEnum)->GetAnimValue().Length() ||
+      !aIsAnimValItem &&
+      mListIndex >= Element()->GetAnimatedLengthList(mAttrEnum)->GetBaseValue().Length()) {
+    NS_ABORT_IF_FALSE(0, "Bad aListIndex!");
+    mList = nsnull;
+  }
+#endif
+}
+
+DOMSVGLength::DOMSVGLength()
+  : mList(nsnull)
+  , mListIndex(0)
+  , mAttrEnum(0)
+  , mIsAnimValItem(0)
+  , mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)
+  , mValue(0.0f)
+{
+}
+
+NS_IMETHODIMP
+DOMSVGLength::GetUnitType(PRUint16* aUnit)
+{
+#ifdef MOZ_SMIL
+  if (mIsAnimValItem && HasOwner()) {
+    Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
+  }
+#endif
+  *aUnit = HasOwner() ? InternalItem().GetUnit() : mUnit;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLength::GetValue(float* aValue)
+{
+#ifdef MOZ_SMIL
+  if (mIsAnimValItem && HasOwner()) {
+    Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
+  }
+#endif
+  if (HasOwner()) {
+    *aValue = InternalItem().GetValueInUserUnits(Element(), Axis());
+    if (NS_FloatIsFinite(*aValue)) {
+      return NS_OK;
+    }
+  } else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER ||
+             mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) {
+    *aValue = mValue;
+    return NS_OK;
+  }
+  // else [SVGWG issue] Can't convert this length's value to user units
+  // ReportToConsole
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+DOMSVGLength::SetValue(float aUserUnitValue)
+{
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  NS_ENSURE_FINITE(aUserUnitValue, NS_ERROR_ILLEGAL_VALUE);
+
+  // Although the value passed in is in user units, this method does not turn
+  // this length into a user unit length. Instead it converts the user unit
+  // value to this length's current unit and sets that, leaving this length's
+  // unit as it is.
+
+  if (HasOwner()) {
+    if (InternalItem().SetFromUserUnitValue(aUserUnitValue, Element(), Axis())) {
+      Element()->DidChangeLengthList(mAttrEnum, PR_TRUE);
+#ifdef MOZ_SMIL
+      if (mList->mAList->IsAnimating()) {
+        Element()->AnimationNeedsResample();
+      }
+#endif
+      return NS_OK;
+    }
+  } else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER ||
+             mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) {
+    mValue = aUserUnitValue;
+    return NS_OK;
+  }
+  // else [SVGWG issue] Can't convert user unit value to this length's unit
+  // ReportToConsole
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+DOMSVGLength::GetValueInSpecifiedUnits(float* aValue)
+{
+#ifdef MOZ_SMIL
+  if (mIsAnimValItem && HasOwner()) {
+    Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
+  }
+#endif
+  *aValue = HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLength::SetValueInSpecifiedUnits(float aValue)
+{
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
+
+  if (HasOwner()) {
+    InternalItem().SetValueInCurrentUnits(aValue);
+    Element()->DidChangeLengthList(mAttrEnum, PR_TRUE);
+#ifdef MOZ_SMIL
+    if (mList->mAList->IsAnimating()) {
+      Element()->AnimationNeedsResample();
+    }
+#endif
+    return NS_OK;
+  }
+  mValue = aValue;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLength::SetValueAsString(const nsAString& aValue)
+{
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  SVGLength value;
+  if (!value.SetValueFromString(aValue)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+  if (HasOwner()) {
+    InternalItem() = value;
+    Element()->DidChangeLengthList(mAttrEnum, PR_TRUE);
+#ifdef MOZ_SMIL
+    if (mList->mAList->IsAnimating()) {
+      Element()->AnimationNeedsResample();
+    }
+#endif
+    return NS_OK;
+  }
+  mValue = value.GetValueInCurrentUnits();
+  mUnit = value.GetUnit();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLength::GetValueAsString(nsAString& aValue)
+{
+#ifdef MOZ_SMIL
+  if (mIsAnimValItem && HasOwner()) {
+    Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
+  }
+#endif
+  if (HasOwner()) {
+    InternalItem().GetValueAsString(aValue);
+    return NS_OK;
+  }
+  SVGLength(mValue, mUnit).GetValueAsString(aValue);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLength::NewValueSpecifiedUnits(PRUint16 aUnit, float aValue)
+{
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
+
+  if (!SVGLength::IsValidUnitType(aUnit)) {
+    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  }
+  if (HasOwner()) {
+    InternalItem().SetValueAndUnit(aValue, aUnit);
+    Element()->DidChangeLengthList(mAttrEnum, PR_TRUE);
+#ifdef MOZ_SMIL
+    if (mList->mAList->IsAnimating()) {
+      Element()->AnimationNeedsResample();
+    }
+#endif
+    return NS_OK;
+  }
+  mUnit = PRUint8(aUnit);
+  mValue = aValue;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLength::ConvertToSpecifiedUnits(PRUint16 aUnit)
+{
+  if (mIsAnimValItem) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  if (!SVGLength::IsValidUnitType(aUnit)) {
+    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  }
+  if (HasOwner()) {
+    if (InternalItem().ConvertToUnit(PRUint8(aUnit), Element(), Axis())) {
+      return NS_OK;
+    }
+  } else {
+    SVGLength len(mValue, mUnit);
+    if (len.ConvertToUnit(PRUint8(aUnit), nsnull, 0)) {
+      mValue = len.GetValueInCurrentUnits();
+      mUnit = aUnit;
+      return NS_OK;
+    }
+  }
+  // else [SVGWG issue] Can't convert unit
+  // ReportToConsole
+  return NS_ERROR_FAILURE;
+}
+
+void
+DOMSVGLength::InsertingIntoList(DOMSVGLengthList *aList,
+                                PRUint32 aAttrEnum,
+                                PRUint8 aListIndex,
+                                PRUint8 aIsAnimValItem)
+{
+  NS_ASSERTION(!HasOwner(), "Inserting item that is already in a list");
+  NS_ASSERTION(mIsAnimValItem &&
+               aListIndex < aList->Element()->GetAnimatedLengthList(aAttrEnum)->GetAnimValue().Length() ||
+               !aIsAnimValItem &&
+               aListIndex < aList->Element()->GetAnimatedLengthList(aAttrEnum)->GetBaseValue().Length(),
+               "mListIndex too big");
+
+  mList = aList;
+  mAttrEnum = aAttrEnum;
+  mListIndex = aListIndex;
+  mIsAnimValItem = aIsAnimValItem;
+}
+
+void
+DOMSVGLength::RemovingFromList()
+{
+  mValue = InternalItem().GetValueInCurrentUnits();
+  mUnit  = InternalItem().GetUnit();
+  mList = nsnull;
+  mIsAnimValItem = 0;
+}
+
+SVGLength
+DOMSVGLength::ToSVGLength()
+{
+  if (HasOwner()) {
+    return SVGLength(InternalItem().GetValueInCurrentUnits(),
+                     InternalItem().GetUnit());
+  }
+  return SVGLength(mValue, mUnit);
+}
+
+SVGLength&
+DOMSVGLength::InternalItem()
+{
+  SVGAnimatedLengthList *alist = Element()->GetAnimatedLengthList(mAttrEnum);
+  return mIsAnimValItem && alist->mAnimVal ?
+    (*alist->mAnimVal)[mListIndex] :
+    alist->mBaseVal[mListIndex];
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGLength.h
@@ -0,0 +1,226 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 ***** */
+
+#ifndef MOZILLA_DOMSVGLENGTH_H__
+#define MOZILLA_DOMSVGLENGTH_H__
+
+#include "nsIDOMSVGLength.h"
+#include "DOMSVGLengthList.h"
+#include "SVGLength.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsAutoPtr.h"
+
+class nsSVGElement;
+
+// We make DOMSVGLength a pseudo-interface to allow us to QI to it in order to
+// check that the objects that scripts pass to DOMSVGLengthList methods are our
+// *native* length objects.
+//
+// {A8468350-7F7B-4976-9A7E-3765A1DADF9A}
+#define MOZILLA_DOMSVGLENGTH_IID \
+  { 0xA8468350, 0x7F7B, 0x4976, { 0x9A, 0x7E, 0x37, 0x65, 0xA1, 0xDA, 0xDF, 0x9A } }
+
+namespace mozilla {
+
+/**
+ * Class DOMSVGLength
+ *
+ * This class creates the DOM objects that wrap internal SVGLength objects that
+ * are in an SVGLengthList. It is also used to create the objects returned by
+ * SVGSVGElement.createSVGLength().
+ *
+ * For the DOM wrapper classes for non-list SVGLength, see nsSVGLength2.h.
+ *
+ * See the architecture comment in DOMSVGAnimatedLengthList.h.
+ *
+ * This class is strongly intertwined with DOMSVGAnimatedLengthList and
+ * DOMSVGLengthList. We are a friend of DOMSVGLengthList, and are responsible
+ * for nulling out our DOMSVGLengthList's pointer to us when we die, making it
+ * a real weak pointer.
+ *
+ * When objects of this type are in a DOMSVGLengthList they belong to an
+ * attribute. While they belong to an attribute, the objects' values come from
+ * their corresponding internal SVGLength objects in the internal SVGLengthList
+ * objects for the attribute. Getting and setting values of a DOMSVGLength
+ * requires reading and writing to its internal SVGLength. However, if the
+ * DOMSVGLength is detached from its DOMSVGLengthList then it first makes a
+ * copy of its internal SVGLength's value and unit so that it doesn't appear to
+ * "lose" its value from script's perspective on being removed from the list.
+ * This means that these DOM tearoffs have space to store these values, even
+ * though they're not used in the common case.
+ *
+ * This class also stores its current list index, attribute enum, and whether
+ * it belongs to a baseVal or animVal list. This is so that objects of this
+ * type can find their corresponding internal SVGLength.
+ *
+ * To use these classes for <length> attributes as well as <list-of-length>
+ * attributes, we would need to take a bit from mListIndex and use that to
+ * indicate whether the object belongs to a list or non-list attribute, then
+ * if-else as appropriate. The bug for doing that work is:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=571734
+ */
+class DOMSVGLength : public nsIDOMSVGLength
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGLENGTH_IID)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGLength)
+  NS_DECL_NSIDOMSVGLENGTH
+
+  /**
+   * Generic ctor for DOMSVGLength objects that are created for an attribute.
+   */
+  DOMSVGLength(DOMSVGLengthList *aList,
+               PRUint32 aAttrEnum,
+               PRUint8 aListIndex,
+               PRUint8 aIsAnimValItem);
+
+  /**
+   * Ctor for creating the objects returned by SVGSVGElement.createSVGLength(),
+   * which do not initially belong to an attribute.
+   */
+  DOMSVGLength();
+
+  ~DOMSVGLength() {
+    // Our mList's weak ref to us must be nulled out when we die. If GC has
+    // unlinked us using the cycle collector code, then that has already
+    // happened, and mList is null.
+    if (mList) {
+      mList->mItems[mListIndex] = nsnull;
+    }
+  };
+
+  /**
+   * Create an unowned copy of an owned length. The caller is responsible for
+   * the first AddRef().
+   */
+  DOMSVGLength* Copy() {
+    NS_ASSERTION(mList, "unexpected caller");
+    DOMSVGLength *copy = new DOMSVGLength();
+    SVGLength &length = InternalItem();
+    copy->NewValueSpecifiedUnits(length.GetUnit(), length.GetValueInCurrentUnits());
+    return copy;
+  }
+
+  PRBool IsInList() const {
+    return !!mList;
+  }
+
+  /**
+   * In future, if this class is used for non-list lengths, this will be
+   * different to IsInList().
+   */
+  PRBool HasOwner() const {
+    return !!mList;
+  }
+
+  /**
+   * This method is called to notify this DOM object that it is being inserted
+   * into a list, and give it the information it needs as a result.
+   *
+   * This object MUST NOT already belong to a list when this method is called.
+   * That's not to say that script can't move these DOM objects between
+   * lists - it can - it's just that the logic to handle that (and send out
+   * the necessary notifications) is located elsewhere (in DOMSVGLengthList).)
+   */
+  void InsertingIntoList(DOMSVGLengthList *aList,
+                         PRUint32 aAttrEnum,
+                         PRUint8 aListIndex,
+                         PRUint8 aIsAnimValItem);
+
+  /// This method is called to notify this object that its list index changed.
+  void UpdateListIndex(PRUint8 aListIndex) {
+    mListIndex = aListIndex;
+  }
+
+  /**
+   * This method is called to notify this DOM object that it is about to be
+   * removed from its current DOM list so that it can first make a copy of its
+   * internal counterpart's values. (If it didn't do this, then it would
+   * "loose" its value on being removed.)
+   */
+  void RemovingFromList();
+
+  SVGLength ToSVGLength();
+
+private:
+
+  nsSVGElement* Element() {
+    return mList->Element();
+  }
+
+  PRUint8 AttrEnum() const {
+    return mAttrEnum;
+  }
+
+  /**
+   * Get the axis that this length lies along. This method must only be called
+   * when this object is associated with an element (HasOwner() returns true).
+   */
+  PRUint8 Axis() const {
+    return mList->Axis();
+  }
+
+  /**
+   * Get a reference to the internal SVGLength list item that this DOM wrapper
+   * object currently wraps.
+   *
+   * To simplyfy the code we just have this one method for obtaining both
+   * baseVal and animVal internal items. This means that animVal items don't
+   * get const protection, but then our setter methods guard against changing
+   * animVal items.
+   */
+  SVGLength& InternalItem();
+
+  nsRefPtr<DOMSVGLengthList> mList;
+
+  // Bounds for the following are checked in the ctor, so be sure to update
+  // that if you change the capacity of any of the following.
+
+  PRUint32 mListIndex:22; // supports > 4 million list items
+  PRUint32 mAttrEnum:4; // supports up to 16 attributes
+  PRUint32 mIsAnimValItem:1;
+
+  // The following members are only used when we're not in a list:
+  PRUint32 mUnit:5; // can handle 31 units (the 10 SVG 1.1 units + rem, vw, vh, wm, calc + future additions)
+  float mValue;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGLength, MOZILLA_DOMSVGLENGTH_IID)
+
+} // namespace mozilla
+
+#endif // MOZILLA_DOMSVGLENGTH_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGLengthList.cpp
@@ -0,0 +1,329 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 "nsSVGElement.h"
+#include "DOMSVGLengthList.h"
+#include "DOMSVGLength.h"
+#include "nsDOMError.h"
+#include "SVGAnimatedLengthList.h"
+#include "nsCOMPtr.h"
+
+// See the comment in this file's header.
+
+namespace mozilla {
+
+// We could use NS_IMPL_CYCLE_COLLECTION_1, except that in Unlink() we need to
+// clear our DOMSVGAnimatedLengthList's weak ref to us to be safe. (The other
+// option would be to not unlink and rely on the breaking of the other edges in
+// the cycle, as NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
+NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLengthList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLengthList)
+  // No need to null check tmp - script/SMIL can't detach us from mAList
+  ( tmp->IsAnimValList() ? tmp->mAList->mAnimVal : tmp->mAList->mBaseVal ) = nsnull;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mAList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLengthList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mAList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLengthList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLengthList)
+
+}
+DOMCI_DATA(SVGLengthList, DOMSVGLengthList)
+namespace mozilla {
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLengthList)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLengthList)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLengthList)
+NS_INTERFACE_MAP_END
+
+
+void
+DOMSVGLengthList::InternalListLengthWillChange(PRUint32 aNewLength)
+{
+  PRUint32 oldLength = mItems.Length();
+
+  // If our length will decrease, notify the items that will be removed:
+  for (PRUint32 i = aNewLength; i < oldLength; ++i) {
+    if (mItems[i]) {
+      mItems[i]->RemovingFromList();
+    }
+  }
+
+  if (!mItems.SetLength(aNewLength)) { // OOM
+    mItems.Clear();
+    return;
+  }
+
+  // If our length has increased, null out the new pointers:
+  for (PRUint32 i = oldLength; i < aNewLength; ++i) {
+    mItems[i] = nsnull;
+  }
+}
+
+SVGLengthList&
+DOMSVGLengthList::InternalList()
+{
+  SVGAnimatedLengthList *alist = Element()->GetAnimatedLengthList(AttrEnum());
+  return IsAnimValList() && alist->mAnimVal ? *alist->mAnimVal : alist->mBaseVal;
+}
+
+// ----------------------------------------------------------------------------
+// nsIDOMSVGLengthList implementation:
+
+NS_IMETHODIMP
+DOMSVGLengthList::GetNumberOfItems(PRUint32 *aNumberOfItems)
+{
+#ifdef MOZ_SMIL
+  if (IsAnimValList()) {
+    Element()->FlushAnimations();
+  }
+#endif
+  *aNumberOfItems = Length();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLengthList::Clear()
+{
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  if (Length() > 0) {
+    // Notify any existing DOM items of removal *before* truncating the lists
+    // so that they can find their SVGLength internal counterparts and copy
+    // their values:
+    mAList->InternalBaseValListWillChangeTo(SVGLengthList());
+
+    mItems.Clear();
+    InternalList().Clear();
+    Element()->DidChangeLengthList(AttrEnum(), PR_TRUE);
+#ifdef MOZ_SMIL
+    if (mAList->IsAnimating()) {
+      Element()->AnimationNeedsResample();
+    }
+#endif
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLengthList::Initialize(nsIDOMSVGLength *newItem,
+                             nsIDOMSVGLength **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  // If newItem is already in a list we should insert a clone of newItem, and
+  // for consistency, this should happen even if *this* is the list that
+  // newItem is currently in. Note that in the case of newItem being in this
+  // list, the Clear() call before the InsertItemBefore() call would remove it
+  // from this list, and so the InsertItemBefore() call would not insert a
+  // clone of newItem, it would actually insert newItem. To prevent that from
+  // happening we have to do the clone here, if necessary.
+
+  nsCOMPtr<DOMSVGLength> domItem = do_QueryInterface(newItem);
+  if (!domItem) {
+    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
+  }
+  if (domItem->HasOwner()) {
+    newItem = domItem->Copy();
+  }
+
+  Clear();
+  return InsertItemBefore(newItem, 0, _retval);
+}
+
+NS_IMETHODIMP
+DOMSVGLengthList::GetItem(PRUint32 index,
+                          nsIDOMSVGLength **_retval)
+{
+#ifdef MOZ_SMIL
+  if (IsAnimValList()) {
+    Element()->FlushAnimations();
+  }
+#endif
+  if (index < Length()) {
+    EnsureItemAt(index);
+    NS_ADDREF(*_retval = mItems[index]);
+    return NS_OK;
+  }
+  *_retval = nsnull;
+  return NS_ERROR_DOM_INDEX_SIZE_ERR;
+}
+
+NS_IMETHODIMP
+DOMSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem,
+                                   PRUint32 index,
+                                   nsIDOMSVGLength **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  nsCOMPtr<DOMSVGLength> domItem = do_QueryInterface(newItem);
+  if (!domItem) {
+    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
+  }
+  index = NS_MIN(index, Length());
+  SVGLength length = domItem->ToSVGLength(); // get before setting domItem
+  if (domItem->HasOwner()) {
+    domItem = new DOMSVGLength();
+  }
+  PRBool ok = !!InternalList().InsertItem(index, length);
+  if (!ok) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
+  ok = !!mItems.InsertElementAt(index, domItem.get());
+  if (!ok) {
+    InternalList().RemoveItem(index);
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  for (PRUint32 i = index + 1; i < Length(); ++i) {
+    if (mItems[i]) {
+      mItems[i]->UpdateListIndex(i);
+    }
+  }
+  Element()->DidChangeLengthList(AttrEnum(), PR_TRUE);
+#ifdef MOZ_SMIL
+  if (mAList->IsAnimating()) {
+    Element()->AnimationNeedsResample();
+  }
+#endif
+  *_retval = domItem.forget().get();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem,
+                              PRUint32 index,
+                              nsIDOMSVGLength **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  nsCOMPtr<DOMSVGLength> domItem = do_QueryInterface(newItem);
+  if (!domItem) {
+    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
+  }
+  if (index >= Length()) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
+  SVGLength length = domItem->ToSVGLength(); // get before setting domItem
+  if (domItem->HasOwner()) {
+    domItem = new DOMSVGLength();
+  }
+  if (mItems[index]) {
+    // Notify any existing DOM item of removal *before* modifying the lists so
+    // that the DOM item can copy the *old* value at its index:
+    mItems[index]->RemovingFromList();
+  }
+  InternalList()[index] = length;
+  domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
+  mItems[index] = domItem;
+
+  Element()->DidChangeLengthList(AttrEnum(), PR_TRUE);
+#ifdef MOZ_SMIL
+  if (mAList->IsAnimating()) {
+    Element()->AnimationNeedsResample();
+  }
+#endif
+  NS_ADDREF(*_retval = domItem.get());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLengthList::RemoveItem(PRUint32 index,
+                             nsIDOMSVGLength **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  if (index >= Length()) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
+  // We have to return the removed item, so make sure it exists:
+  EnsureItemAt(index);
+
+  // Notify the DOM item of removal *before* modifying the lists so that the
+  // DOM item can copy its *old* value:
+  mItems[index]->RemovingFromList();
+
+  InternalList().RemoveItem(index);
+
+  NS_ADDREF(*_retval = mItems[index]);
+  mItems.RemoveElementAt(index);
+  for (PRUint32 i = index; i < Length(); ++i) {
+    if (mItems[i]) {
+      mItems[i]->UpdateListIndex(i);
+    }
+  }
+  Element()->DidChangeLengthList(AttrEnum(), PR_TRUE);
+#ifdef MOZ_SMIL
+  if (mAList->IsAnimating()) {
+    Element()->AnimationNeedsResample();
+  }
+#endif
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGLengthList::AppendItem(nsIDOMSVGLength *newItem,
+                             nsIDOMSVGLength **_retval)
+{
+  return InsertItemBefore(newItem, Length(), _retval);
+}
+
+void
+DOMSVGLengthList::EnsureItemAt(PRUint32 aIndex)
+{
+  if (!mItems[aIndex]) {
+    mItems[aIndex] = new DOMSVGLength(this, AttrEnum(), aIndex, IsAnimValList());
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGLengthList.h
@@ -0,0 +1,157 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 ***** */
+
+#ifndef MOZILLA_DOMSVGLENGTHLIST_H__
+#define MOZILLA_DOMSVGLENGTHLIST_H__
+
+#include "nsIDOMSVGLengthList.h"
+#include "SVGLengthList.h"
+#include "SVGLength.h"
+#include "DOMSVGAnimatedLengthList.h"
+#include "nsCOMArray.h"
+#include "nsAutoPtr.h"
+
+class nsSVGElement;
+
+namespace mozilla {
+
+class DOMSVGLength;
+
+/**
+ * Class DOMSVGLengthList
+ *
+ * This class is used to create the DOM tearoff objects that wrap internal
+ * SVGLengthList objects.
+ *
+ * See the architecture comment in DOMSVGAnimatedLengthList.h.
+ *
+ * This class is strongly intertwined with DOMSVGAnimatedLengthList and
+ * DOMSVGLength. We are a friend of DOMSVGAnimatedLengthList, and are
+ * responsible for nulling out our DOMSVGAnimatedLengthList's pointer to us
+ * when we die, essentially making its pointer to us a weak pointer. Similarly,
+ * our DOMSVGLength items are friends of us and responsible for nulling out our
+ * pointers to them.
+ *
+ * Our DOM items are created lazily on demand as and when script requests them.
+ */
+class DOMSVGLengthList : public nsIDOMSVGLengthList
+{
+  friend class DOMSVGLength;
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGLengthList)
+  NS_DECL_NSIDOMSVGLENGTHLIST
+
+  DOMSVGLengthList(DOMSVGAnimatedLengthList *aAList)
+    : mAList(aAList)
+  {
+    // We silently ignore SetLength OOM failure since being out of sync is safe
+    // so long as we have *fewer* items than our internal list.
+
+    mItems.SetLength(InternalList().Length());
+    for (PRUint32 i = 0; i < Length(); ++i) {
+      // null out all the pointers - items are created on-demand
+      mItems[i] = nsnull;
+    }
+  }
+
+  ~DOMSVGLengthList() {
+    // Our mAList's weak ref to us must be nulled out when we die. If GC has
+    // unlinked us using the cycle collector code, then that has already
+    // happened, and mAList is null.
+    if (mAList) {
+      ( IsAnimValList() ? mAList->mAnimVal : mAList->mBaseVal ) = nsnull;
+    }
+  };
+
+  /**
+   * This will normally be the same as InternalList().Length(), except if we've
+   * hit OOM in which case our length will be zero.
+   */
+  PRUint32 Length() const {
+    NS_ABORT_IF_FALSE(mItems.Length() == 0 ||
+                      mItems.Length() ==
+                        const_cast<DOMSVGLengthList*>(this)->InternalList().Length(),
+                      "DOM wrapper's list length is out of sync");
+    return mItems.Length();
+  }
+
+  /// Called to notify us to syncronize our length and detach excess items.
+  void InternalListLengthWillChange(PRUint32 aNewLength);
+
+private:
+
+  nsSVGElement* Element() {
+    return mAList->mElement;
+  }
+
+  PRUint8 AttrEnum() const {
+    return mAList->mAttrEnum;
+  }
+
+  PRUint8 Axis() const {
+    return mAList->mAxis;
+  }
+
+  /// Used to determine if this list is the baseVal or animVal list.
+  PRBool IsAnimValList() const {
+    return this == mAList->mAnimVal;
+  }
+
+  /**
+   * Get a reference to this object's corresponding internal SVGLengthList.
+   *
+   * To simplyfy the code we just have this one method for obtaining both
+   * baseVal and animVal internal lists. This means that animVal lists don't
+   * get const protection, but our setter methods guard against changing
+   * animVal lists.
+   */
+  SVGLengthList& InternalList();
+
+  /// Creates a DOMSVGLength for aIndex, if it doesn't already exist.
+  void EnsureItemAt(PRUint32 aIndex);
+
+  // Weak refs to our DOMSVGLength items. The items are friends and take care
+  // of clearing our pointer to them when they die.
+  nsTArray<DOMSVGLength*> mItems;
+
+  nsRefPtr<DOMSVGAnimatedLengthList> mAList;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_DOMSVGLENGTHLIST_H__
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -44,22 +44,24 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= content
 LIBRARY_NAME	= gkcontentsvg_s
 LIBXUL_LIBRARY	= 1
 
 CPPSRCS		= \
+		DOMSVGAnimatedLengthList.cpp \
+		DOMSVGLength.cpp \
+		DOMSVGLengthList.cpp \
 		nsDOMSVGZoomEvent.cpp \
 		nsDOMSVGEvent.cpp \
 		nsSVGAElement.cpp \
 		nsSVGAltGlyphElement.cpp \
 		nsSVGAngle.cpp \
-		nsSVGAnimatedLengthList.cpp \
 		nsSVGAnimatedNumberList.cpp \
 		nsSVGAnimatedTransformList.cpp \
 		nsSVGBoolean.cpp \
 		nsSVGCircleElement.cpp \
 		nsSVGClipPathElement.cpp \
 		nsSVGDataParser.cpp \
 		nsSVGDefsElement.cpp \
 		nsSVGDescElement.cpp \
@@ -71,19 +73,17 @@ CPPSRCS		= \
 		nsSVGFilterElement.cpp \
 		nsSVGFilters.cpp \
 		nsSVGForeignObjectElement.cpp \
 		nsSVGGElement.cpp \
 		nsSVGGradientElement.cpp \
 		nsSVGGraphicElement.cpp \
 		nsSVGImageElement.cpp \
 		nsSVGInteger.cpp \
-		nsSVGLength.cpp \
 		nsSVGLength2.cpp \
-		nsSVGLengthList.cpp \
 		nsSVGLineElement.cpp \
 		nsSVGMarkerElement.cpp \
 		nsSVGMaskElement.cpp \
 		nsSVGMatrix.cpp \
 		nsSVGMetadataElement.cpp \
 		nsSVGNumber.cpp \
 		nsSVGNumber2.cpp \
 		nsSVGNumberList.cpp \
@@ -117,27 +117,31 @@ CPPSRCS		= \
 		nsSVGTextPositioningElement.cpp \
 		nsSVGTitleElement.cpp \
 		nsSVGTransform.cpp \
 		nsSVGTransformList.cpp \
 		nsSVGTransformListParser.cpp \
 		nsSVGUseElement.cpp \
 		nsSVGValue.cpp \
 		nsSVGViewBox.cpp \
+		SVGAnimatedLengthList.cpp \
+		SVGLength.cpp \
+		SVGLengthList.cpp \
 		$(NULL)
 
 ifdef MOZ_SMIL
 CPPSRCS += nsSVGAnimateElement.cpp \
            nsSVGAnimateTransformElement.cpp \
            nsSVGAnimateMotionElement.cpp \
            nsSVGAnimationElement.cpp \
            nsSVGMpathElement.cpp \
            nsSVGSetElement.cpp \
            nsSVGTransformSMILType.cpp \
            nsSVGTransformSMILAttr.cpp \
+           SVGLengthListSMILType.cpp \
            SVGMotionSMILType.cpp \
            SVGMotionSMILAttr.cpp \
            SVGMotionSMILAnimationFunction.cpp \
            SVGMotionSMILPathUtils.cpp \
            SVGOrientSMILType.cpp \
            SVGViewBoxSMILType.cpp \
            $(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGAnimatedLengthList.cpp
@@ -0,0 +1,244 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 "SVGAnimatedLengthList.h"
+#include "DOMSVGAnimatedLengthList.h"
+#include "nsSVGElement.h"
+#include "nsSVGAttrTearoffTable.h"
+#ifdef MOZ_SMIL
+#include "nsSMILValue.h"
+#include "SVGLengthListSMILType.h"
+#endif // MOZ_SMIL
+
+namespace mozilla {
+
+nsresult
+SVGAnimatedLengthList::SetBaseValueString(const nsAString& aValue)
+{
+  SVGLengthList newBaseValue;
+  nsresult rv = newBaseValue.SetValueFromString(aValue);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  DOMSVGAnimatedLengthList *domWrapper =
+    DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
+  if (domWrapper) {
+    // We must send this notification *before* changing mBaseVal! If the length
+    // of our baseVal is being reduced, our baseVal's DOM wrapper list may have
+    // to remove DOM items from itself, and any removed DOM items need to copy
+    // their internal counterpart values *before* we change them.
+    //
+    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.
+
+  rv = mBaseVal.CopyFrom(newBaseValue);
+  if (NS_FAILED(rv)) {
+    // Attempting to increase mBaseVal's length failed - reduce domWrapper
+    // back to the same length:
+    domWrapper->InternalBaseValListWillChangeTo(mBaseVal);
+  }
+  return rv;
+}
+
+void
+SVGAnimatedLengthList::ClearBaseValue(PRUint32 aAttrEnum)
+{
+  DOMSVGAnimatedLengthList *domWrapper =
+    DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
+  if (domWrapper) {
+    // We must send this notification *before* changing mBaseVal! (See above.)
+    domWrapper->InternalAnimValListWillChangeTo(SVGLengthList());
+  }
+  mBaseVal.Clear();
+  // Caller notifies
+}
+
+nsresult
+SVGAnimatedLengthList::SetAnimValue(const SVGLengthList& aNewAnimValue,
+                                    nsSVGElement *aElement,
+                                    PRUint32 aAttrEnum)
+{
+  DOMSVGAnimatedLengthList *domWrapper =
+    DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
+  if (domWrapper) {
+    // A new animation may totally change the number of items in the animVal
+    // list, replacing what was essentially a mirror of the baseVal list, or
+    // else replacing and overriding an existing animation. When this happens
+    // we must try and keep our animVal's DOM wrapper in sync (see the comment
+    // in DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo).
+    //
+    // It's not possible for us to reliably distinguish between calls to this
+    // method that are setting a new sample for an existing animation, and
+    // calls that are setting the first sample of an animation that will
+    // override an existing animation. Happily it's cheap to just blindly
+    // notify our animVal's DOM wrapper of its internal counterpart's new value
+    // each time this method is called, so that's what we do.
+    //
+    // Note that we must send this notification *before* setting or changing
+    // mAnimVal! (See the comment in SetBaseValueString above.)
+    //
+    domWrapper->InternalAnimValListWillChangeTo(aNewAnimValue);
+  }
+  if (!mAnimVal) {
+    mAnimVal = new SVGLengthList();
+  }
+  nsresult rv = mAnimVal->CopyFrom(aNewAnimValue);
+  if (NS_FAILED(rv)) {
+    // OOM. We clear the animation, and, importantly, ClearAnimValue() ensures
+    // that mAnimVal and its DOM wrapper (if any) will have the same length!
+    ClearAnimValue(aElement, aAttrEnum);
+    return rv;
+  }
+  aElement->DidAnimateLengthList(aAttrEnum);
+  return NS_OK;
+}
+
+void
+SVGAnimatedLengthList::ClearAnimValue(nsSVGElement *aElement,
+                                      PRUint32 aAttrEnum)
+{
+  DOMSVGAnimatedLengthList *domWrapper =
+    DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
+  if (domWrapper) {
+    // When all animation ends, animVal simply mirrors baseVal, which may have
+    // a different number of items to the last active animated value. We must
+    // keep the length of our animVal's DOM wrapper list in sync, and again we
+    // must do that before touching mAnimVal. See comments above.
+    //
+    domWrapper->InternalAnimValListWillChangeTo(mBaseVal);
+  }
+  mAnimVal = nsnull;
+  aElement->DidAnimateLengthList(aAttrEnum);
+}
+
+#ifdef MOZ_SMIL
+nsISMILAttr*
+SVGAnimatedLengthList::ToSMILAttr(nsSVGElement *aSVGElement,
+                                  PRUint8 aAttrEnum,
+                                  PRUint8 aAxis,
+                                  PRBool aCanZeroPadList)
+{
+  return new SMILAnimatedLengthList(this, aSVGElement, aAttrEnum, aAxis, aCanZeroPadList);
+}
+
+nsresult
+SVGAnimatedLengthList::
+  SMILAnimatedLengthList::ValueFromString(const nsAString& aStr,
+                               const nsISMILAnimationElement* /*aSrcElement*/,
+                               nsSMILValue& aValue,
+                               PRBool& aPreventCachingOfSandwich) const
+{
+  nsSMILValue val(&SVGLengthListSMILType::sSingleton);
+  SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(val.mU.mPtr);
+  nsresult rv = llai->SetValueFromString(aStr);
+  if (NS_SUCCEEDED(rv)) {
+    llai->SetInfo(mElement, mAxis, mCanZeroPadList);
+    aValue.Swap(val);
+
+    // If any of the lengths in the list depend on their context, then we must
+    // prevent caching of the entire animation sandwich. This is because the
+    // units of a length at a given index can change from sandwich layer to
+    // layer, and indeed even be different within a single sandwich layer. If
+    // any length in the result of an animation sandwich is the result of the
+    // addition of lengths where one or more of those lengths is context
+    // dependent, then naturally the resultant length is also context
+    // dependent, regardless of whether its actual unit is context dependent or
+    // not. Unfortunately normal invalidation mechanisms won't cause us to
+    // recalculate the result of the sandwich if the context changes, so we
+    // take the (substantial) performance hit of preventing caching of the
+    // sandwich layer, causing the animation sandwich to be recalculated every
+    // single sample.
+
+    aPreventCachingOfSandwich = PR_FALSE;
+    for (PRUint32 i = 0; i < llai->Length(); ++i) {
+      PRUint8 unit = (*llai)[i].GetUnit();
+      if (unit == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE ||
+          unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS ||
+          unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EXS) {
+        aPreventCachingOfSandwich = PR_TRUE;
+        break;
+      }
+    }
+  }
+  return rv;
+}
+
+nsSMILValue
+SVGAnimatedLengthList::SMILAnimatedLengthList::GetBaseValue() const
+{
+  // To benefit from Return Value Optimization and avoid copy constructor calls
+  // due to our use of return-by-value, we must return the exact same object
+  // from ALL return points. This function must only return THIS variable:
+  nsSMILValue val;
+
+  nsSMILValue tmp(&SVGLengthListSMILType::sSingleton);
+  SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(tmp.mU.mPtr);
+  nsresult rv = llai->CopyFrom(mVal->mBaseVal);
+  if (NS_SUCCEEDED(rv)) {
+    llai->SetInfo(mElement, mAxis, mCanZeroPadList);
+    val.Swap(tmp);
+  }
+  return val;
+}
+
+nsresult
+SVGAnimatedLengthList::SMILAnimatedLengthList::SetAnimValue(const nsSMILValue& aValue)
+{
+  NS_ASSERTION(aValue.mType == &SVGLengthListSMILType::sSingleton,
+               "Unexpected type to assign animated value");
+  if (aValue.mType == &SVGLengthListSMILType::sSingleton) {
+    mVal->SetAnimValue(*static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr),
+                       mElement,
+                       mAttrEnum);
+  }
+  return NS_OK;
+}
+
+void
+SVGAnimatedLengthList::SMILAnimatedLengthList::ClearAnimValue()
+{
+  if (mVal->mAnimVal) {
+    mVal->ClearAnimValue(mElement, mAttrEnum);
+  }
+}
+#endif // MOZ_SMIL
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGAnimatedLengthList.h
@@ -0,0 +1,158 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 ***** */
+
+#ifndef MOZILLA_SVGANIMATEDLENGTHLIST_H__
+#define MOZILLA_SVGANIMATEDLENGTHLIST_H__
+
+#include "SVGLengthList.h"
+
+class nsSVGElement;
+
+#ifdef MOZ_SMIL
+#include "nsISMILAttr.h"
+#endif // MOZ_SMIL
+
+namespace mozilla {
+
+/**
+ * Class SVGAnimatedLengthList
+ *
+ * This class is very different to the SVG DOM interface of the same name found
+ * in the SVG specification. This is a lightweight internal class - see
+ * DOMSVGAnimatedLengthList for the heavier DOM class that wraps instances of
+ * this class and implements the SVG specification's SVGAnimatedLengthList DOM
+ * interface.
+ *
+ * Except where noted otherwise, this class' methods take care of keeping the
+ * appropriate DOM wrappers in sync (see the comment in
+ * DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo) so that their
+ * consumers don't need to concern themselves with that.
+ */
+class SVGAnimatedLengthList
+{
+  // friends so that they can get write access to mBaseVal
+  friend class DOMSVGLength;
+  friend class DOMSVGLengthList;
+
+public:
+  SVGAnimatedLengthList() {}
+
+  /**
+   * Because it's so important that mBaseVal and its DOMSVGLengthList wrapper
+   * (if any) be kept in sync (see the comment in
+   * DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo), this method
+   * returns a const reference. Only our friend classes may get mutable
+   * references to mBaseVal.
+   */
+  const SVGLengthList& GetBaseValue() const {
+    return mBaseVal;
+  }
+
+  nsresult SetBaseValueString(const nsAString& aValue);
+
+  void ClearBaseValue(PRUint32 aAttrEnum);
+
+  const SVGLengthList& GetAnimValue() const {
+    return mAnimVal ? *mAnimVal : mBaseVal;
+  }
+
+  nsresult SetAnimValue(const SVGLengthList& aValue,
+                        nsSVGElement *aElement,
+                        PRUint32 aAttrEnum);
+
+  void ClearAnimValue(nsSVGElement *aElement,
+                      PRUint32 aAttrEnum);
+
+  PRBool IsAnimating() const {
+    return !!mAnimVal;
+  }
+
+#ifdef MOZ_SMIL
+  /// Callers own the returned nsISMILAttr
+  nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement, PRUint8 aAttrEnum,
+                          PRUint8 aAxis, PRBool aCanZeroPadList);
+#endif // MOZ_SMIL
+
+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="">).
+
+  SVGLengthList mBaseVal;
+  nsAutoPtr<SVGLengthList> mAnimVal;
+
+#ifdef MOZ_SMIL
+  struct SMILAnimatedLengthList : public nsISMILAttr
+  {
+  public:
+    SMILAnimatedLengthList(SVGAnimatedLengthList* aVal,
+                           nsSVGElement* aSVGElement,
+                           PRUint8 aAttrEnum,
+                           PRUint8 aAxis,
+                           PRBool aCanZeroPadList)
+      : mVal(aVal)
+      , mElement(aSVGElement)
+      , mAttrEnum(aAttrEnum)
+      , mAxis(aAxis)
+      , mCanZeroPadList(aCanZeroPadList)
+    {}
+
+    // 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.
+    SVGAnimatedLengthList* mVal;
+    nsSVGElement* mElement;
+    PRUint8 mAttrEnum;
+    PRUint8 mAxis;
+    PRPackedBool mCanZeroPadList; // See SVGLengthListAndInfo::CanZeroPadList
+
+    // 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
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGANIMATEDLENGTHLIST_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGLength.cpp
@@ -0,0 +1,295 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 "SVGLength.h"
+#include "nsSVGElement.h"
+#include "nsSVGSVGElement.h"
+#include "nsString.h"
+#include "nsSVGUtils.h"
+#include "nsContentUtils.h"
+#include "nsTextFormatter.h"
+#include "prdtoa.h"
+#include <limits>
+
+namespace mozilla {
+
+// Declare some helpers defined below:
+static void GetUnitString(nsAString& unit, PRUint16 unitType);
+static PRUint16 GetUnitTypeForString(const char* unitStr);
+
+void
+SVGLength::GetValueAsString(nsAString &aValue) const
+{
+  PRUnichar buf[24];
+  nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
+                            NS_LITERAL_STRING("%g").get(),
+                            (double)mValue);
+  aValue.Assign(buf);
+
+  nsAutoString unitString;
+  GetUnitString(unitString, mUnit);
+  aValue.Append(unitString);
+}
+
+PRBool
+SVGLength::SetValueFromString(const nsAString &aValue)
+{
+  float tmpValue;
+  PRUint16 tmpUnit;
+
+  NS_ConvertUTF16toUTF8 value(aValue);
+  const char *str = value.get();
+
+  while (*str != '\0' && IsSVGWhitespace(*str)) {
+    ++str;
+  }
+  char *unit;
+  tmpValue = float(PR_strtod(str, &unit));
+  if (unit != str && NS_FloatIsFinite(tmpValue)) {
+    char *theRest = unit;
+    if (*unit != '\0' && !IsSVGWhitespace(*unit)) {
+      while (*theRest != '\0' && !IsSVGWhitespace(*theRest)) {
+        ++theRest;
+      }
+      nsCAutoString unitStr(unit, theRest - unit);
+      tmpUnit = GetUnitTypeForString(unitStr.get());
+    } else {
+      tmpUnit = nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER;
+    }
+    while (*theRest && IsSVGWhitespace(*theRest)) {
+      ++theRest;
+    }
+    if (!*theRest) {
+      mValue = tmpValue;
+      mUnit = tmpUnit;
+      return PR_TRUE;
+    }
+  }
+  return PR_FALSE;
+}
+
+inline static PRBool
+IsAbsoluteUnit(PRUint8 aUnit)
+{
+  return aUnit >= nsIDOMSVGLength::SVG_LENGTHTYPE_CM &&
+         aUnit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC;
+}
+
+/**
+ * Helper to convert between different CSS absolute units without the need for
+ * an element, which provides more flexibility at the DOM level (and without
+ * the need for an intermediary conversion to user units, which avoids
+ * unnecessary overhead and rounding error).
+ *
+ * Example usage: to find out how many centimeters there are per inch:
+ *
+ *   GetAbsUnitsPerAbsUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_CM,
+ *                         nsIDOMSVGLength::SVG_LENGTHTYPE_IN)
+ */
+inline static float GetAbsUnitsPerAbsUnit(PRUint8 aUnits, PRUint8 aPerUnit)
+{
+  NS_ABORT_IF_FALSE(IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
+  NS_ABORT_IF_FALSE(IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
+
+  float CSSAbsoluteUnitConversionFactors[5][5] = { // columns: cm, mm, in, pt, pc
+    // cm per...:
+    { 1.0, 0.1, 2.54, 0.035277777777777778, 0.42333333333333333 },
+    // mm per...:
+    { 10.0, 1.0, 25.4, 0.35277777777777778, 4.2333333333333333 },
+    // in per...:
+    { 0.39370078740157481, 0.039370078740157481, 1.0, 0.013888888888888889, 0.16666666666666667 },
+    // pt per...:
+    { 28.346456692913386, 2.8346456692913386, 72.0, 1.0, 12.0 },
+    // pc per...:
+    { 2.3622047244094489, 0.23622047244094489, 6.0, 0.083333333333333333, 1.0 }
+  };
+
+  // First absolute unit is SVG_LENGTHTYPE_CM = 6
+  return CSSAbsoluteUnitConversionFactors[aUnits - 6][aPerUnit - 6];
+}
+
+float
+SVGLength::GetValueInSpecifiedUnit(PRUint8 aUnit,
+                                   const nsSVGElement *aElement,
+                                   PRUint8 aAxis) const
+{
+  if (aUnit == mUnit) {
+    return mValue;
+  }
+  if ((aUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER &&
+       mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) ||
+      (aUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX &&
+       mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)) {
+    return mValue;
+  }
+  if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
+    return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
+  }
+
+  // Otherwise we do a two step convertion via user units. This can only
+  // succeed if aElement is non-null (although that's not sufficent to
+  // guarantee success).
+
+  float userUnitsPerCurrentUnit = GetUserUnitsPerUnit(aElement, aAxis);
+  float userUnitsPerNewUnit =
+    SVGLength(0.0f, aUnit).GetUserUnitsPerUnit(aElement, aAxis);
+
+  NS_ASSERTION(userUnitsPerCurrentUnit >= 0 ||
+               !NS_FloatIsFinite(userUnitsPerCurrentUnit),
+               "bad userUnitsPerCurrentUnit");
+  NS_ASSERTION(userUnitsPerNewUnit >= 0 ||
+               !NS_FloatIsFinite(userUnitsPerNewUnit),
+               "bad userUnitsPerNewUnit");
+
+  float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
+
+  // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
+  // be zero.
+  if (NS_FloatIsFinite(value)) {
+    return value;
+  }
+  return std::numeric_limits<float>::quiet_NaN();
+}
+
+#define INCHES_PER_MM_FLOAT float(0.0393700787)
+#define INCHES_PER_CM_FLOAT float(0.393700787)
+
+float
+SVGLength::GetUserUnitsPerUnit(const nsSVGElement *aElement, PRUint8 aAxis) const
+{
+  switch (mUnit) {
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER:
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_PX:
+      return 1.0f;
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_MM:
+      return INCHES_PER_MM_FLOAT * GetUserUnitsPerInch(aElement);
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_CM:
+      return INCHES_PER_CM_FLOAT * GetUserUnitsPerInch(aElement);
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_IN:
+      return GetUserUnitsPerInch(aElement);
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_PT:
+      return (1.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch(aElement);
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_PC:
+      return (12.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch(aElement);
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE:
+      return GetUserUnitsPerPercent(aElement, aAxis);
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS:
+      return nsSVGUtils::GetFontSize(const_cast<nsSVGElement*>(aElement));
+    case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS:
+      return nsSVGUtils::GetFontXHeight(const_cast<nsSVGElement*>(aElement));
+    default:
+      NS_NOTREACHED("Unknown unit type");
+      return std::numeric_limits<float>::quiet_NaN();
+  }
+}
+
+/* static */ float
+SVGLength::GetUserUnitsPerInch(const nsIContent *aContent)
+{
+  if (aContent) {
+    nsPresContext *context = nsContentUtils::GetContextForContent(
+                                           const_cast<nsIContent*>(aContent));
+    if (context) {
+      float uuPerInch = float(context->AppUnitsPerInch()) /
+                        float(nsPresContext::AppUnitsPerCSSPixel());
+      NS_ASSERTION(uuPerInch > 0.0f, "Non-positive user units per inch");
+      return uuPerInch;
+    }
+  }
+  return std::numeric_limits<float>::quiet_NaN();
+}
+
+/* static */ float
+SVGLength::GetUserUnitsPerPercent(const nsSVGElement *aElement, PRUint8 aAxis)
+{
+  if (aElement) {
+    nsSVGSVGElement *viewportElement = const_cast<nsSVGElement*>(aElement)->GetCtx();
+    if (viewportElement) {
+      return NS_MAX(viewportElement->GetLength(aAxis) / 100.0f, 0.0f);
+    }
+  }
+  return std::numeric_limits<float>::quiet_NaN();
+}
+
+// Helpers:
+
+// These items must be at the same index as the nsIDOMSVGLength constants!
+static nsIAtom** const unitMap[] =
+{
+  nsnull, /* SVG_LENGTHTYPE_UNKNOWN */
+  nsnull, /* SVG_LENGTHTYPE_NUMBER */
+  &nsGkAtoms::percentage,
+  &nsGkAtoms::em,
+  &nsGkAtoms::ex,
+  &nsGkAtoms::px,
+  &nsGkAtoms::cm,
+  &nsGkAtoms::mm,
+  &nsGkAtoms::in,
+  &nsGkAtoms::pt,
+  &nsGkAtoms::pc
+};
+
+static void
+GetUnitString(nsAString& unit, PRUint16 unitType)
+{
+  if (SVGLength::IsValidUnitType(unitType)) {
+    if (unitMap[unitType]) {
+      (*unitMap[unitType])->ToString(unit);
+    }
+    return;
+  }
+  NS_NOTREACHED("Unknown unit type"); // Someone's using an SVGLength with an invalid unit?
+  return;
+}
+
+static PRUint16
+GetUnitTypeForString(const char* unitStr)
+{
+  if (!unitStr || *unitStr == '\0')
+    return nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER;
+
+  nsCOMPtr<nsIAtom> unitAtom = do_GetAtom(unitStr);
+
+  for (PRUint32 i = 1 ; i < NS_ARRAY_LENGTH(unitMap) ; i++) {
+    if (unitMap[i] && *unitMap[i] == unitAtom) {
+      return i;
+    }
+  }
+  NS_NOTREACHED("Returning unknown unit type");
+  return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGLength.h
@@ -0,0 +1,255 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 ***** */
+
+#ifndef MOZILLA_SVGLENGTH_H__
+#define MOZILLA_SVGLENGTH_H__
+
+#include "nsIDOMSVGLength.h"
+#include "nsIContent.h"
+#include "nsAString.h"
+#include "nsContentUtils.h"
+
+class nsSVGElement;
+
+namespace mozilla {
+
+/**
+ * This SVGLength class is currently used for SVGLength *list* attributes only.
+ * The class that is currently used for <length> attributes is nsSVGLength2.
+ *
+ * The member mUnit should always be valid, but the member mValue may be
+ * numeric_limits<float>::quiet_NaN() under one circumstances (see the comment
+ * in SetValueAndUnit below). Even if mValue is valid, some methods may return
+ * numeric_limits<float>::quiet_NaN() if they involve a unit conversion that
+ * fails - see comments below.
+ *
+ * The DOM wrapper class for this class is DOMSVGLength.
+ */
+class SVGLength
+{
+public:
+
+  SVGLength()
+#ifdef DEBUG
+    : mValue(0.0f)
+    , mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN) // caught by IsValid()
+#endif
+  {}
+
+  SVGLength(float aValue, PRUint8 aUnit)
+    : mValue(aValue)
+    , mUnit(aUnit)
+  {
+    NS_ASSERTION(IsValid(), "Constructed an invalid length");
+  }
+
+  SVGLength(const SVGLength &aOther)
+    : mValue(aOther.mValue)
+    , mUnit(aOther.mUnit)
+  {}
+
+  SVGLength& operator=(const SVGLength &rhs) {
+    mValue = rhs.mValue;
+    mUnit = rhs.mUnit;
+    return *this;
+  }
+
+  PRBool operator==(const SVGLength &rhs) const {
+    return mValue == rhs.mValue && mUnit == rhs.mUnit;
+  }
+
+  void GetValueAsString(nsAString& aValue) const;
+
+  /**
+   * This method returns PR_TRUE, unless there was a parse failure, in which
+   * case it returns PR_FALSE (and the length is left unchanged).
+   */
+  PRBool SetValueFromString(const nsAString& aValue);
+
+  /**
+   * This will usually return a valid, finite number. There is one exception
+   * though - see the comment in SetValueAndUnit().
+   */
+  float GetValueInCurrentUnits() const {
+    return mValue;
+  }
+
+  PRUint8 GetUnit() const {
+    return mUnit;
+  }
+
+  void SetValueInCurrentUnits(float aValue) {
+    mValue = aValue;
+    NS_ASSERTION(IsValid(), "Set invalid SVGLength");
+  }
+
+  void SetValueAndUnit(float aValue, PRUint8 aUnit) {
+    mValue = aValue;
+    mUnit = aUnit;
+
+    // IsValid() should always be true, with one exception: if
+    // SVGLengthListSMILType has to convert between unit types and the unit
+    // conversion is undefined, it will end up passing in and setting
+    // numeric_limits<float>::quiet_NaN(). Because of that we only check the
+    // unit here, and allow mValue to be invalid. The painting code has to be
+    // able to handle NaN anyway, since conversion to user units may fail in
+    // general.
+
+    NS_ASSERTION(IsValidUnitType(mUnit), "Set invalid SVGLength");
+  }
+
+  /**
+   * If it's not possible to convert this length's value to user units, then
+   * this method will return numeric_limits<float>::quiet_NaN().
+   */
+  float GetValueInUserUnits(const nsSVGElement *aElement, PRUint8 aAxis) const {
+    return mValue * GetUserUnitsPerUnit(aElement, aAxis);
+  }
+
+  /**
+   * Sets this length's value, converting the supplied user unit value to this
+   * lengths *current* unit (i.e. leaving the length's unit unchanged).
+   *
+   * This method returns PR_TRUE, unless the user unit value couldn't be
+   * converted to this length's current unit, in which case it returns PR_FALSE
+   * (and the length is left unchanged).
+   */
+  PRBool SetFromUserUnitValue(float aUserUnitValue,
+                              nsSVGElement *aElement,
+                              PRUint8 aAxis) {
+    float uuPerUnit = GetUserUnitsPerUnit(aElement, aAxis);
+    float value = aUserUnitValue / uuPerUnit;
+    if (uuPerUnit > 0 && NS_FloatIsFinite(value)) {
+      mValue = value;
+      NS_ASSERTION(IsValid(), "Set invalid SVGLength");
+      return PR_TRUE;
+    }
+    return PR_FALSE;
+  }
+
+  /**
+   * Get this length's value in the units specified.
+   *
+   * This method returns numeric_limits<float>::quiet_NaN() if it is not
+   * possible to convert the value to the specified unit.
+   */
+  float GetValueInSpecifiedUnit(PRUint8 aUnit,
+                                const nsSVGElement *aElement,
+                                PRUint8 aAxis) const;
+
+  /**
+   * Convert this length's value to the unit specified.
+   *
+   * This method returns PR_TRUE, unless it isn't possible to convert the
+   * length to the specified unit. In that case the length is left unchanged
+   * and this method returns PR_FALSE.
+   */
+  PRBool ConvertToUnit(PRUint32 aUnit, nsSVGElement *aElement, PRUint8 aAxis) {
+    float val = GetValueInSpecifiedUnit(aUnit, aElement, aAxis);
+    if (NS_FloatIsFinite(val)) {
+      mValue = val;
+      mUnit = aUnit;
+      NS_ASSERTION(IsValid(), "Set invalid SVGLength");
+      return PR_TRUE;
+    }
+    return PR_FALSE;
+  }
+
+  PRBool IsPercentage() const {
+    return mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE;
+  }
+
+  static PRBool IsValidUnitType(PRUint16 unit) {
+    return unit > nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN &&
+           unit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC;
+  }
+
+private:
+
+#ifdef DEBUG
+  PRBool IsValid() const {
+    return NS_FloatIsFinite(mValue) && IsValidUnitType(mUnit);
+  }
+#endif
+
+  /**
+   * Returns the number of user units per current unit.
+   *
+   * This method returns numeric_limits<float>::quiet_NaN() if the conversion
+   * factor between the length's current unit and user units is undefined (see
+   * the comments for GetUserUnitsPerInch and GetUserUnitsPerPercent).
+   */
+  float GetUserUnitsPerUnit(const nsSVGElement *aElement, PRUint8 aAxis) const;
+
+  /**
+   * The conversion factor between user units (CSS px) and a CSS absolute unit
+   * (in this case inches) is the same for all elements in a given document,
+   * except for elements for which the factor is undefined. The conversion
+   * factor is undefined for elements that are not in a document tree, and for
+   * elements in a document tree that don't have a pres context (elements under
+   * a display:none iframe, or elements belonging to a data document (an XHR
+   * response document or a document created via createDocument()).
+   *
+   * This helper acts as the basis for conversion between user units and all
+   * CSS absolute units (the conversion factors between CSS absolute units are
+   * fixed). Inches are chosen as the canonical unit because that's what
+   * pres/device contexts store, so it makes sense for this helper to also work
+   * in those terms to eliminate unnecessary multiplications/divisions that
+   * must then be reversed.
+   *
+   * This function returns a positive value if the conversion factor is
+   * defined, otherwise it returns numeric_limits<float>::quiet_NaN().
+   */
+  static float GetUserUnitsPerInch(const nsIContent *aContent);
+
+  /**
+   * The conversion factor between user units and percentage units depends on
+   * aElement being non-null, and on aElement having a viewport element
+   * ancestor with only appropriate SVG elements between aElement and that
+   * ancestor. If that's not the case, then the conversion factor is undefined.
+   *
+   * This function returns a non-negative value if the conversion factor is
+   * defined, otherwise it returns numeric_limits<float>::quiet_NaN().
+   */
+  static float GetUserUnitsPerPercent(const nsSVGElement *aElement, PRUint8 aAxis);
+
+  float mValue;
+  PRUint8 mUnit;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGLENGTH_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGLengthList.cpp
@@ -0,0 +1,132 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 "SVGLengthList.h"
+#include "SVGAnimatedLengthList.h"
+#include "SVGLength.h"
+#include "nsSVGElement.h"
+#include "nsISVGValueUtils.h"
+#include "nsDOMError.h"
+#include "nsContentUtils.h"
+#include "nsString.h"
+#include "nsSVGUtils.h"
+#include "string.h"
+
+namespace mozilla {
+
+nsresult
+SVGLengthList::CopyFrom(const SVGLengthList& rhs)
+{
+  if (!mLengths.SetCapacity(rhs.Length())) {
+    // Yes, we do want fallible alloc here
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  mLengths = rhs.mLengths;
+  return NS_OK;
+}
+
+void
+SVGLengthList::GetValueAsString(nsAString& aValue) const
+{
+  aValue.Truncate();
+  PRUint32 last = mLengths.Length() - 1;
+  for (PRUint32 i = 0; i < mLengths.Length(); ++i) {
+    nsAutoString length;
+    mLengths[i].GetValueAsString(length);
+    // We ignore OOM, since it's not useful for us to return an error.
+    aValue.Append(length);
+    if (i != last) {
+      aValue.Append(' ');
+    }
+  }
+}
+
+static inline char* SkipWhitespace(char* str)
+{
+  while (IsSVGWhitespace(*str))
+    ++str;
+  return str;
+}
+
+nsresult
+SVGLengthList::SetValueFromString(const nsAString& aValue)
+{
+  SVGLengthList temp;
+
+  NS_ConvertUTF16toUTF8 value(aValue);
+  char* start = SkipWhitespace(value.BeginWriting());
+
+  // We can't use strtok with SVG_COMMA_WSP_DELIM because to correctly handle
+  // invalid input in the form of two commas without a value between them, we
+  // would need to know if strtok overwrote a comma or not.
+
+  while (*start != '\0') {
+    int end = strcspn(start, SVG_COMMA_WSP_DELIM);
+    if (end == 0) {
+      // found comma in an invalid location
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+    SVGLength length;
+    if (!length.SetValueFromString(NS_ConvertUTF8toUTF16(start, PRUint32(end)))) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+    if (!temp.AppendItem(length)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    start = SkipWhitespace(start + end);
+    if (*start == ',') {
+      start = SkipWhitespace(start + 1);
+    }
+  }
+
+  return CopyFrom(temp);
+}
+
+PRBool
+SVGLengthList::operator==(const SVGLengthList& rhs) const
+{
+  if (Length() != rhs.Length()) {
+    return PR_FALSE;
+  }
+  for (PRUint32 i = 0; i < Length(); ++i) {
+    if (!(mLengths[i] == rhs.mLengths[i])) {
+      return PR_FALSE;
+    }
+  }
+  return PR_TRUE;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGLengthList.h
@@ -0,0 +1,352 @@
+/* -*- 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 Mozilla SVG Project code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 ***** */
+
+#ifndef MOZILLA_SVGLENGTHLIST_H__
+#define MOZILLA_SVGLENGTHLIST_H__
+
+#include "SVGLength.h"
+#include "nsTArray.h"
+#include "nsSVGElement.h"
+
+namespace mozilla {
+
+/**
+ * ATTENTION! WARNING! WATCH OUT!!
+ *
+ * Consumers that modify objects of this type absolutely MUST keep the DOM
+ * wrappers for those lists (if any) in sync!! That's why this class is so
+ * locked down.
+ *
+ * The DOM wrapper class for this class is DOMSVGLengthList.
+ */
+class SVGLengthList
+{
+  friend class SVGAnimatedLengthList;
+  friend class DOMSVGLengthList;
+  friend class DOMSVGLength;
+
+public:
+
+  SVGLengthList(){}
+  ~SVGLengthList(){}
+
+  // Only methods that don't make/permit modification to this list are public.
+  // Only our friend classes can access methods that may change us.
+
+  /// This may return an incomplete string on OOM, but that's acceptable.
+  void GetValueAsString(nsAString& aValue) const;
+
+  PRBool IsEmpty() const {
+    return mLengths.IsEmpty();
+  }
+
+  PRUint32 Length() const {
+    return mLengths.Length();
+  }
+
+  const SVGLength& operator[](PRUint32 aIndex) const {
+    return mLengths[aIndex];
+  }
+
+  PRBool operator==(const SVGLengthList& rhs) const;
+
+  PRBool SetCapacity(PRUint32 size) {
+    return mLengths.SetCapacity(size);
+  }
+
+  void Compact() {
+    mLengths.Compact();
+  }
+
+  // Access to methods that can modify objects of this type is deliberately
+  // limited. This is to reduce the chances of someone modifying objects of
+  // this type without taking the necessary steps to keep DOM wrappers in sync.
+  // If you need wider access to these methods, consider adding a method to
+  // SVGAnimatedLengthList and having that class act as an intermediary so it
+  // can take care of keeping DOM wrappers in sync.
+
+protected:
+
+  /**
+   * This may fail on OOM if the internal capacity needs to be increased, in
+   * which case the list will be left unmodified.
+   */
+  nsresult CopyFrom(const SVGLengthList& rhs);
+
+  SVGLength& operator[](PRUint32 aIndex) {
+    return mLengths[aIndex];
+  }
+
+  /**
+   * This may fail (return PR_FALSE) on OOM if the internal capacity is being
+   * increased, in which case the list will be left unmodified.
+   */
+  PRBool SetLength(PRUint32 aNumberOfItems) {
+    return mLengths.SetLength(aNumberOfItems);
+  }
+
+private:
+
+  // Marking the following private only serves to show which methods are only
+  // used by our friend classes (as opposed to our subclasses) - it doesn't
+  // really provide additional safety.
+
+  nsresult SetValueFromString(const nsAString& aValue);
+
+  void Clear() {
+    mLengths.Clear();
+  }
+
+  PRBool InsertItem(PRUint32 aIndex, const SVGLength &aLength) {
+    if (aIndex >= mLengths.Length()) aIndex = mLengths.Length();
+    return !!mLengths.InsertElementAt(aIndex, aLength);
+  }
+
+  void ReplaceItem(PRUint32 aIndex, const SVGLength &aLength) {
+    NS_ASSERTION(aIndex < mLengths.Length(),
+                 "DOM wrapper caller should have raised INDEX_SIZE_ERR");
+    mLengths[aIndex] = aLength;
+  }
+
+  void RemoveItem(PRUint32 aIndex) {
+    NS_ASSERTION(aIndex < mLengths.Length(),
+                 "DOM wrapper caller should have raised INDEX_SIZE_ERR");
+    mLengths.RemoveElementAt(aIndex);
+  }
+
+  PRBool AppendItem(SVGLength aLength) {
+    return !!mLengths.AppendElement(aLength);
+  }
+
+protected:
+
+  /* Rationale for using nsTArray<SVGLength> and not nsTArray<SVGLength, 1>:
+   *
+   * It might seem like we should use nsAutoTArray<SVGLength, 1> instead of
+   * nsTArray<SVGLength>. That would preallocate space for one SVGLength and
+   * avoid an extra memory allocation call in the common case of the 'x'
+   * and 'y' attributes each containing a single length (and the 'dx' and 'dy'
+   * attributes being empty). However, consider this:
+   *
+   * An empty nsTArray uses sizeof(Header*). An nsAutoTArray<class E,
+   * PRUint32 N> on the other hand uses sizeof(Header*) +
+   * (2 * sizeof(PRUint32)) + (N * sizeof(E)), which for one SVGLength is
+   * sizeof(Header*) + 16 bytes.
+   *
+   * Now consider that for text elements we have four length list attributes
+   * (x, y, dx, dy), each of which can have a baseVal and an animVal list. If
+   * we were to go the nsAutoTArray<SVGLength, 1> route for each of these, we'd
+   * end up using at least 160 bytes for these four attributes alone, even
+   * though we only need storage for two SVGLengths (16 bytes) in the common
+   * case!!
+   *
+   * A compromise might be to template SVGLengthList to allow
+   * SVGAnimatedLengthList to preallocate space for an SVGLength for the
+   * baseVal lists only, and that would cut the space used by the four
+   * attributes to 96 bytes. Taking that even further and templating
+   * SVGAnimatedLengthList too in order to only use nsTArray for 'dx' and 'dy'
+   * would reduce the storage further to 64 bytes. Having different types makes
+   * things more complicated for code that needs to look at the lists though.
+   * In fact it also makes things more complicated when it comes to storing the
+   * lists.
+   *
+   * It may be worth considering using nsAttrValue for length lists instead of
+   * storing them directly on the element.
+   */
+  nsTArray<SVGLength> mLengths;
+};
+
+
+/**
+ * This SVGLengthList subclass is for SVGLengthListSMILType which needs to know
+ * which element and attribute a length list belongs to so that it can convert
+ * between unit types if necessary.
+ */
+class SVGLengthListAndInfo : public SVGLengthList
+{
+public:
+
+  SVGLengthListAndInfo()
+    : mElement(nsnull)
+    , mAxis(0)
+    , mCanZeroPadList(PR_FALSE)
+  {}
+
+  SVGLengthListAndInfo(nsSVGElement *aElement, PRUint8 aAxis, PRBool aCanZeroPadList)
+    : mElement(aElement)
+    , mAxis(aAxis)
+    , mCanZeroPadList(aCanZeroPadList)
+  {}
+
+  void SetInfo(nsSVGElement *aElement, PRUint8 aAxis, PRBool aCanZeroPadList) {
+    mElement = aElement;
+    mAxis = aAxis;
+    mCanZeroPadList = aCanZeroPadList;
+  }
+
+  nsSVGElement* Element() const {
+    return mElement; // .get();
+  }
+
+  PRUint8 Axis() const {
+    NS_ASSERTION(mElement, "Axis() isn't valid");
+    return mAxis;
+  }
+
+  /**
+   * The value returned by this function depends on which attribute this object
+   * is for. If appending a list of zeros to the attribute's list would have no
+   * affect on rendering (e.g. the attributes 'dx' and 'dy' on <text>), then
+   * this method will return PR_TRUE. If appending a list of zeros to the
+   * attribute's list could *change* rendering (e.g. the attributes 'x' and 'y'
+   * on <text>), then this method will return PR_FALSE.
+   *
+   * The reason that this method exists is because the SMIL code needs to know
+   * what to do when it's asked to animate between lists of different length.
+   * If this method returns PR_TRUE, then it can zero pad the short list before
+   * carrying out its operations. However, in the case of the 'x' and 'y'
+   * attributes on <text>, zero would mean "zero in the current coordinate
+   * system", whereas we would want to pad shorter lists with the coordinates
+   * at which glyphs would otherwise lie, which is almost certainly not zero!
+   * Animating from/to zeros in this case would produce terrible results.
+   *
+   * Currently SVGLengthListSMILType simply disallows (drops) animation between
+   * lists of different length if it can't zero pad a list. This is to avoid
+   * having some authors create content that depends on undesirable behaviour
+   * (which would make it difficult for us to fix the behavior in future). At
+   * some point it would be nice to implement a callback to allow this code to
+   * determine padding values for lists that can't be zero padded. See
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=573431
+   */
+  PRBool CanZeroPadList() const {
+    //NS_ASSERTION(mElement, "CanZeroPadList() isn't valid");
+    return mCanZeroPadList;
+  }
+
+  // For the SMIL code. See comment in SVGLengthListSMILType::Add().
+  void SetCanZeroPadList(PRBool aCanZeroPadList) {
+    mCanZeroPadList = aCanZeroPadList;
+  }
+
+  nsresult CopyFrom(const SVGLengthListAndInfo& rhs) {
+    mElement = rhs.mElement;
+    mAxis = rhs.mAxis;
+    mCanZeroPadList = rhs.mCanZeroPadList;
+    return SVGLengthList::CopyFrom(rhs);
+  }
+
+  // Instances of this special subclass do not have DOM wrappers that we need
+  // to worry about keeping in sync, so it's safe to expose any hidden base
+  // class methods required by the SMIL code, as we do below.
+
+  /**
+   * Exposed so that SVGLengthList baseVals can be copied to
+   * SVGLengthListAndInfo objects. Note that callers should also call
+   * SetInfo() when using this method!
+   */
+  nsresult CopyFrom(const SVGLengthList& rhs) {
+    return SVGLengthList::CopyFrom(rhs);
+  }
+  const SVGLength& operator[](PRUint32 aIndex) const {
+    return SVGLengthList::operator[](aIndex);
+  }
+  SVGLength& operator[](PRUint32 aIndex) {
+    return SVGLengthList::operator[](aIndex);
+  }
+  PRBool SetLength(PRUint32 aNumberOfItems) {
+    return SVGLengthList::SetLength(aNumberOfItems);
+  }
+
+private:
+  // We must keep a strong reference to our element because we may belong to a
+  // cached baseVal nsSMILValue. See the comments starting at:
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15
+  nsRefPtr<nsSVGElement> mElement;
+  PRUint8 mAxis;
+  PRPackedBool mCanZeroPadList;
+};
+
+
+/**
+ * This class wraps SVGLengthList objects to allow frame consumers to process
+ * SVGLengthList objects as if they were simply a list of float values in user
+ * units. When consumers request the value at a given index, this class
+ * dynamically converts the corresponding SVGLength from its actual unit and
+ * returns its value in user units.
+ *
+ * Consumers should check that the user unit values returned are finite. Even
+ * if the consumer can guarantee the list's element has a valid viewport
+ * ancestor to resolve percentage units against, and a valid presContext and
+ * styleContext to resolve absolute and em/ex units against, unit conversions
+ * could still overflow. In that case the value returned will be
+ * numeric_limits<float>::quiet_NaN().
+ */
+class NS_STACK_CLASS SVGUserUnitList
+{
+public:
+
+  SVGUserUnitList()
+    : mList(nsnull)
+  {}
+
+  void Init(const SVGLengthList *aList, nsSVGElement *aElement, PRUint8 aAxis) {
+    mList = aList;
+    mElement = aElement;
+    mAxis = aAxis;
+  }
+
+  void Clear() {
+    mList = nsnull;
+  }
+
+  PRUint32 Length() {
+    return mList ? mList->Length() : 0;
+  }
+
+  /// This may return a non-finite value
+  float operator[](PRUint32 aIndex) {
+    return (*mList)[aIndex].GetValueInUserUnits(mElement, mAxis);
+  }
+
+private:
+  const SVGLengthList *mList;
+  nsSVGElement *mElement;
+  PRUint8 mAxis;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGLENGTHLIST_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGLengthListSMILType.cpp
@@ -0,0 +1,309 @@
+/* -*- 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 the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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 "SVGLengthListSMILType.h"
+#include "nsSMILValue.h"
+#include "SVGLengthList.h"
+#include <math.h>
+
+namespace mozilla {
+
+/*static*/ SVGLengthListSMILType SVGLengthListSMILType::sSingleton;
+
+//----------------------------------------------------------------------
+// nsISMILType implementation
+
+void
+SVGLengthListSMILType::Init(nsSMILValue &aValue) const
+{
+  NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type");
+
+  SVGLengthListAndInfo* lengthList = new SVGLengthListAndInfo();
+
+  // See the comment documenting Init() in our header file:
+  lengthList->SetCanZeroPadList(PR_TRUE);
+
+  aValue.mU.mPtr = lengthList;
+  aValue.mType = this;
+}
+
+void
+SVGLengthListSMILType::Destroy(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
+  delete static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr);
+  aValue.mU.mPtr = nsnull;
+  aValue.mType = &nsSMILNullType::sSingleton;
+}
+
+nsresult
+SVGLengthListSMILType::Assign(nsSMILValue& aDest,
+                              const nsSMILValue& aSrc) const
+{
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
+
+  const SVGLengthListAndInfo* src =
+    static_cast<const SVGLengthListAndInfo*>(aSrc.mU.mPtr);
+  SVGLengthListAndInfo* dest =
+    static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
+
+  return dest->CopyFrom(*src);
+}
+
+PRBool
+SVGLengthListSMILType::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 *static_cast<const SVGLengthListAndInfo*>(aLeft.mU.mPtr) ==
+         *static_cast<const SVGLengthListAndInfo*>(aRight.mU.mPtr);
+}
+
+nsresult
+SVGLengthListSMILType::Add(nsSMILValue& aDest,
+                           const nsSMILValue& aValueToAdd,
+                           PRUint32 aCount) const
+{
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
+  NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type");
+
+  SVGLengthListAndInfo& dest =
+    *static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
+  const SVGLengthListAndInfo& valueToAdd =
+    *static_cast<const SVGLengthListAndInfo*>(aValueToAdd.mU.mPtr);
+
+  // To understand this code, see the comments documenting our Init() method,
+  // and documenting SVGLengthListAndInfo::CanZeroPadList().
+
+  // Note that *this* method actually may safely zero pad a shorter list
+  // regardless of the value returned by CanZeroPadList() for that list,
+  // just so long as the shorter list is being added *to* the longer list
+  // and *not* vice versa! It's okay in the case of adding a shorter list to a
+  // longer list because during the add operation we'll end up adding the
+  // zeros to actual specified values. It's *not* okay in the case of adding a
+  // longer list to a shorter list because then we end up adding to implicit
+  // zeros when we'd actually need to add to whatever the underlying values
+  // should be, not zeros, and those values are not explicit or otherwise
+  // available.
+
+  if (dest.Length() < valueToAdd.Length()) {
+    if (!dest.CanZeroPadList()) {
+      // nsSVGUtils::ReportToConsole
+      return NS_ERROR_FAILURE;
+    }
+
+    NS_ASSERTION(valueToAdd.CanZeroPadList() || dest.Length() == 0,
+                 "Only \"zero\" nsSMILValues from the SMIL engine should "
+                 "return PR_TRUE for CanZeroPadList() when the attribute "
+                 "being animated can't be zero padded");
+
+    PRUint32 i = dest.Length();
+    if (!dest.SetLength(valueToAdd.Length())) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    for (; i < valueToAdd.Length(); ++i) {
+      dest[i].SetValueAndUnit(0.0f, valueToAdd[i].GetUnit());
+    }
+  }
+
+  for (PRUint32 i = 0; i < valueToAdd.Length(); ++i) {
+    float valToAdd;
+    if (dest[i].GetUnit() == valueToAdd[i].GetUnit()) {
+      valToAdd = valueToAdd[i].GetValueInCurrentUnits();
+    } else {
+      // If units differ, we use the unit of the item in 'dest'.
+      // We leave it to the frame code to check that values are finite.
+      valToAdd = valueToAdd[i].GetValueInSpecifiedUnit(dest[i].GetUnit(),
+                                                       dest.Element(),
+                                                       dest.Axis());
+    }
+    dest[i].SetValueAndUnit(dest[i].GetValueInCurrentUnits() + valToAdd,
+                            dest[i].GetUnit());
+  }
+
+  // propagate flag:
+  dest.SetCanZeroPadList(dest.CanZeroPadList() &&
+                         valueToAdd.CanZeroPadList());
+
+  return NS_OK;
+}
+
+nsresult
+SVGLengthListSMILType::ComputeDistance(const nsSMILValue& aFrom,
+                                       const nsSMILValue& aTo,
+                                       double& aDistance) const
+{
+  NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
+  NS_PRECONDITION(aTo.mType == this, "Incompatible SMIL type");
+
+  const SVGLengthListAndInfo& from =
+    *static_cast<const SVGLengthListAndInfo*>(aFrom.mU.mPtr);
+  const SVGLengthListAndInfo& to =
+    *static_cast<const SVGLengthListAndInfo*>(aTo.mU.mPtr);
+
+  // To understand this code, see the comments documenting our Init() method,
+  // and documenting SVGLengthListAndInfo::CanZeroPadList().
+
+  NS_ASSERTION(from.CanZeroPadList() == to.CanZeroPadList() ||
+               from.CanZeroPadList() && from.Length() == 0 ||
+               to.CanZeroPadList() && to.Length() == 0,
+               "Only \"zero\" nsSMILValues from the SMIL engine should "
+               "return PR_TRUE for CanZeroPadList() when the attribute "
+               "being animated can't be zero padded");
+
+  if ((from.Length() < to.Length() && !from.CanZeroPadList()) ||
+      (to.Length() < from.Length() && !to.CanZeroPadList())) {
+    // nsSVGUtils::ReportToConsole
+    return NS_ERROR_FAILURE;
+  }
+
+  // We return the root of the sum of the squares of the deltas between the
+  // user unit values of the lengths at each correspanding index. In the
+  // general case, paced animation is probably not useful, but this strategy at
+  // least does the right thing for paced animation in the face of simple
+  // 'values' lists such as:
+  //
+  //   values="100 200 300; 101 201 301; 110 210 310"
+  //
+  // I.e. half way through the simple duration we'll get "105 205 305".
+
+  double total = 0.0;
+
+  PRUint32 i = 0;
+  for (; i < from.Length() && i < to.Length(); ++i) {
+    double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
+    double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
+    double delta = t - f;
+    total += delta * delta;
+  }
+
+  // In the case that from.Length() != to.Length(), one of the following loops
+  // will run. (OK since CanZeroPadList()==true for the other list.)
+
+  for (; i < from.Length(); ++i) {
+    double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
+    total += f * f;
+  }
+  for (; i < to.Length(); ++i) {
+    double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
+    total += t * t;
+  }
+
+  float distance = sqrt(total);
+  if (!NS_FloatIsFinite(distance)) {
+    return NS_ERROR_FAILURE;
+  }
+  aDistance = distance;
+  return NS_OK;
+}
+
+nsresult
+SVGLengthListSMILType::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");
+
+  const SVGLengthListAndInfo& start =
+    *static_cast<const SVGLengthListAndInfo*>(aStartVal.mU.mPtr);
+  const SVGLengthListAndInfo& end =
+    *static_cast<const SVGLengthListAndInfo*>(aEndVal.mU.mPtr);
+  SVGLengthListAndInfo& result =
+    *static_cast<SVGLengthListAndInfo*>(aResult.mU.mPtr);
+
+  // To understand this code, see the comments documenting our Init() method,
+  // and documenting SVGLengthListAndInfo::CanZeroPadList().
+
+  NS_ASSERTION(start.CanZeroPadList() == end.CanZeroPadList() ||
+               start.CanZeroPadList() && start.Length() == 0 ||
+               end.CanZeroPadList() && end.Length() == 0,
+               "Only \"zero\" nsSMILValues from the SMIL engine should "
+               "return PR_TRUE for CanZeroPadList() when the attribute "
+               "being animated can't be zero padded");
+
+  if ((start.Length() < end.Length() && !start.CanZeroPadList()) ||
+      (end.Length() < start.Length() && !end.CanZeroPadList())) {
+    // nsSVGUtils::ReportToConsole
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!result.SetLength(NS_MAX(start.Length(), end.Length()))) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  PRUint32 i = 0;
+  for (; i < start.Length() && i < end.Length(); ++i) {
+    float s;
+    if (start[i].GetUnit() == end[i].GetUnit()) {
+      s = start[i].GetValueInCurrentUnits();
+    } else {
+      // If units differ, we use the unit of the item in 'end'.
+      // We leave it to the frame code to check that values are finite.
+      s = start[i].GetValueInSpecifiedUnit(end[i].GetUnit(), end.Element(), end.Axis());
+    }
+    float e = end[i].GetValueInCurrentUnits();
+    result[i].SetValueAndUnit(s + (e - s) * aUnitDistance, end[i].GetUnit());
+  }
+
+  // In the case that start.Length() != end.Length(), one of the following
+  // loops will run. (Okay, since CanZeroPadList()==true for the other list.)
+
+  for (; i < start.Length(); ++i) {
+    result[i].SetValueAndUnit(start[i].GetValueInCurrentUnits() -
+                              start[i].GetValueInCurrentUnits() * aUnitDistance,
+                              start[i].GetUnit());
+  }
+  for (; i < end.Length(); ++i) {
+    result[i].SetValueAndUnit(end[i].GetValueInCurrentUnits() * aUnitDistance,
+                              end[i].GetUnit());
+  }
+
+  // propagate flag:
+  result.SetCanZeroPadList(start.CanZeroPadList() &&
+                           end.CanZeroPadList());
+
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGLengthListSMILType.h
@@ -0,0 +1,132 @@
+/* -*- 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 the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * 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_SVGLENGTHLISTSMILTYPE_H_
+#define MOZILLA_SVGLENGTHLISTSMILTYPE_H_
+
+#include "nsISMILType.h"
+
+class nsSMILValue;
+
+namespace mozilla {
+
+////////////////////////////////////////////////////////////////////////
+// SVGLengthListSMILType
+//
+// Operations for animating an SVGLengthList.
+//
+class SVGLengthListSMILType : public nsISMILType
+{
+public:
+  // Singleton for nsSMILValue objects to hold onto.
+  static SVGLengthListSMILType sSingleton;
+
+protected:
+  // nsISMILType Methods
+  // -------------------
+
+  /**
+   * When this method initializes the SVGLengthListAndInfo for its nsSMILValue
+   * argument, it has to blindly set its mCanZeroPadList to PR_TRUE despite
+   * the fact that some attributes can't be zero-padded. (See the explaination
+   * that follows.) SVGAnimatedLengthList::SMILAnimatedLengthList's
+   * GetBaseValue() and ValueFromString() methods then override this for the
+   * nsSMILValue objects that they create to set this flag to the appropriate
+   * value for the attribute in question.
+   *
+   * The reason that we default to setting the mCanZeroPadList to true is
+   * because the SMIL engine creates "zero" valued nsSMILValue objects for
+   * intermediary calculations, and may pass such an nsSMILValue (along with an
+   * nsSMILValue from an animation element - that is an nsSMILValue created by
+   * SVGAnimatedLengthList::SMILAnimatedLengthList's GetBaseValue() or
+   * ValueFromString() methods) into the Add(), ComputeDistance() or
+   * Interpolate() methods below. Even in the case of animation of list
+   * attributes that may *not* be padded with zeros (such as 'x' and 'y' on the
+   * <text> element), we need to allow zero-padding of these "zero" valued
+   * nsSMILValue's lists. One reason for this is illustrated by the following
+   * example:
+   *
+   *   <text x="2 4">foo
+   *      <animate by="2 2" .../>
+   *   </text>
+   *
+   * In this example there are two SMIL animation layers to be sandwiched: the
+   * base layer, and the layer created for the <animate> element. The SMIL
+   * engine calculates the result of each layer *independently*, before
+   * compositing the results together. Thus for the <animate> sandwich layer
+   * the SMIL engine interpolates between a "zero" nsSMILValue that it creates
+   * (since there is no explicit "from") and the "2 2", before the result of
+   * that interpolation is added to the "2 4" from the base layer. Clearly for
+   * the interpolation between the "zero" nsSMILValue and "2 2" to work, the
+   * "zero" nsSMILValue's SVGLengthListAndInfo must be zero paddable - hence
+   * why this method always sets mCanZeroPadList to PR_TRUE.
+   *
+   * (Since the Add(), ComputeDistance() and Interpolate() methods may be
+   * passed two input nsSMILValue objects for which CanZeroPadList() returns
+   * opposite values, these methods must be careful what they set the flag to
+   * on the nsSMILValue that they output. If *either* of the input nsSMILValues
+   * has an SVGLengthListAndInfo for which CanZeroPadList() returns PR_FALSE,
+   * then they must set the flag to PR_FALSE on the output nsSMILValue too. If
+   * the methods failed to do that, then when the result nsSMILValue objects
+   * from each sandwich layer are composited together, we could end up allowing
+   * animation between lists of different length when we should not!)
+   */
+  virtual void     Init(nsSMILValue& aValue) const;
+
+  virtual void     Destroy(nsSMILValue& aValue) 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.
+  SVGLengthListSMILType() {}
+  ~SVGLengthListSMILType() {}
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGLENGTHLISTSMILTYPE_H_
deleted file mode 100644
--- a/content/svg/content/src/nsSVGAnimatedLengthList.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
-/* -*- 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
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2002
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *
- * 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 "nsSVGAnimatedLengthList.h"
-#include "nsSVGLengthList.h"
-#include "nsSVGValue.h"
-#include "nsWeakReference.h"
-#include "nsContentUtils.h"
-
-////////////////////////////////////////////////////////////////////////
-// nsSVGAnimatedTransformList
-
-class nsSVGAnimatedLengthList : public nsIDOMSVGAnimatedLengthList,
-                                public nsSVGValue,
-                                public nsISVGValueObserver
-{  
-protected:
-  friend nsresult
-  NS_NewSVGAnimatedLengthList(nsIDOMSVGAnimatedLengthList** result,
-                              nsIDOMSVGLengthList* baseVal);
-
-  nsSVGAnimatedLengthList();
-  ~nsSVGAnimatedLengthList();
-  void Init(nsIDOMSVGLengthList* baseVal);
-  
-public:
-  // nsISupports interface:
-  NS_DECL_ISUPPORTS
-
-  // nsIDOMSVGAnimatedLengthList interface:
-  NS_DECL_NSIDOMSVGANIMATEDLENGTHLIST
-
-  // remainder of nsISVGValue interface:
-  NS_IMETHOD SetValueString(const nsAString& aValue);
-  NS_IMETHOD GetValueString(nsAString& aValue);
-
-  // nsISVGValueObserver
-  NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
-                                     modificationType aModType);
-  NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
-                                     modificationType aModType);
-  
-  // nsISupportsWeakReference
-  // implementation inherited from nsSupportsWeakReference
-  
-protected:
-  nsCOMPtr<nsIDOMSVGLengthList> mBaseVal;
-};
-
-
-//----------------------------------------------------------------------
-// Implementation
-
-nsSVGAnimatedLengthList::nsSVGAnimatedLengthList()
-{
-}
-
-nsSVGAnimatedLengthList::~nsSVGAnimatedLengthList()
-{
-  if (!mBaseVal) return;
-    nsCOMPtr<nsISVGValue> val = do_QueryInterface(mBaseVal);
-  if (!val) return;
-  val->RemoveObserver(this);
-}
-
-void
-nsSVGAnimatedLengthList::Init(nsIDOMSVGLengthList* baseVal)
-{
-  mBaseVal = baseVal;
-  if (!mBaseVal) return;
-  nsCOMPtr<nsISVGValue> val = do_QueryInterface(mBaseVal);
-  if (!val) return;
-  val->AddObserver(this);
-}
-
-//----------------------------------------------------------------------
-// nsISupports methods:
-
-NS_IMPL_ADDREF(nsSVGAnimatedLengthList)
-NS_IMPL_RELEASE(nsSVGAnimatedLengthList)
-
-DOMCI_DATA(SVGAnimatedLengthList, nsSVGAnimatedLengthList)
-
-NS_INTERFACE_MAP_BEGIN(nsSVGAnimatedLengthList)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValue)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedLengthList)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGAnimatedLengthList)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
-NS_INTERFACE_MAP_END
-
-
-//----------------------------------------------------------------------
-// nsISVGValue methods:
-
-NS_IMETHODIMP
-nsSVGAnimatedLengthList::SetValueString(const nsAString& aValue)
-{
-  nsCOMPtr<nsISVGValue> value = do_QueryInterface(mBaseVal);
-  return value->SetValueString(aValue);
-}
-
-NS_IMETHODIMP
-nsSVGAnimatedLengthList::GetValueString(nsAString& aValue)
-{
-  nsCOMPtr<nsISVGValue> value = do_QueryInterface(mBaseVal);
-  return value->GetValueString(aValue);
-}
-
-//----------------------------------------------------------------------
-// nsIDOMSVGAnimatedLengthList methods:
-
-/* readonly attribute nsIDOMSVGLengthList baseVal; */
-NS_IMETHODIMP
-nsSVGAnimatedLengthList::GetBaseVal(nsIDOMSVGLengthList * *aBaseVal)
-{
-  *aBaseVal = mBaseVal;
-  NS_ADDREF(*aBaseVal);
-  return NS_OK;
-}
-
-/* readonly attribute nsIDOMSVGLengthList animVal; */
-NS_IMETHODIMP
-nsSVGAnimatedLengthList::GetAnimVal(nsIDOMSVGLengthList * *aAnimVal)
-{
-  *aAnimVal = mBaseVal;
-  NS_ADDREF(*aAnimVal);
-  return NS_OK;
-}
-
-//----------------------------------------------------------------------
-// nsISVGValueObserver methods
-
-NS_IMETHODIMP
-nsSVGAnimatedLengthList::WillModifySVGObservable(nsISVGValue* observable,
-                                                 modificationType aModType)
-{
-  WillModify(aModType);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSVGAnimatedLengthList::DidModifySVGObservable (nsISVGValue* observable,
-                                                 modificationType aModType)
-{
-  DidModify(aModType);
-  return NS_OK;
-}
-
-
-////////////////////////////////////////////////////////////////////////
-// Exported creation functions:
-
-nsresult
-NS_NewSVGAnimatedLengthList(nsIDOMSVGAnimatedLengthList** result,
-                            nsIDOMSVGLengthList* baseVal)
-{
-  *result = nsnull;
-  
-  nsSVGAnimatedLengthList* animatedLengthList = new nsSVGAnimatedLengthList();
-  if(!animatedLengthList) return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(animatedLengthList);
-
-  animatedLengthList->Init(baseVal);
-  
-  *result = (nsIDOMSVGAnimatedLengthList*) animatedLengthList;
-  
-  return NS_OK;
-}
-
deleted file mode 100644
--- a/content/svg/content/src/nsSVGAnimatedLengthList.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- 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
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *
- * 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_SVGANIMATEDLENGTHLIST_H__
-#define __NS_SVGANIMATEDLENGTHLIST_H__
-
-#include "nsIDOMSVGAnimatedLengthList.h"
-#include "nsIDOMSVGLengthList.h"
-
-nsresult
-NS_NewSVGAnimatedLengthList(nsIDOMSVGAnimatedLengthList** result,
-                               nsIDOMSVGLengthList* baseVal);
-
-#endif //__NS_SVGANIMATEDLENGTHLIST_H__
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -69,25 +69,22 @@
 #include "nsSVGLength2.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGInteger.h"
 #include "nsSVGAngle.h"
 #include "nsSVGBoolean.h"
 #include "nsSVGEnum.h"
 #include "nsSVGViewBox.h"
 #include "nsSVGString.h"
+#include "SVGAnimatedLengthList.h"
 #include "nsIDOMSVGUnitTypes.h"
-#include "nsIDOMSVGLengthList.h"
-#include "nsIDOMSVGAnimatedLengthList.h"
 #include "nsIDOMSVGNumberList.h"
 #include "nsIDOMSVGAnimatedNumberList.h"
 #include "nsIDOMSVGPointList.h"
 #include "nsIDOMSVGAnimatedPoints.h"
-#include "nsIDOMSVGPresAspectRatio.h"
-#include "nsIDOMSVGAnimPresAspRatio.h"
 #include "nsIDOMSVGTransformList.h"
 #include "nsIDOMSVGAnimTransformList.h"
 #include "nsIDOMSVGAnimatedRect.h"
 #include "nsIDOMSVGGradientElement.h"
 #include "nsIDOMSVGPatternElement.h"
 #include "nsSVGRect.h"
 #include "nsIFrame.h"
 #include "prdtoa.h"
@@ -169,16 +166,22 @@ nsSVGElement::Init()
 
   nsSVGPreserveAspectRatio *preserveAspectRatio =
     GetPreserveAspectRatio();
 
   if (preserveAspectRatio) {
     preserveAspectRatio->Init();
   }
 
+  LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
+
+  for (i = 0; i < lengthListInfo.mLengthListCount; i++) {
+    lengthListInfo.Reset(i);
+  }
+
   StringAttributesInfo stringInfo = GetStringInfo();
 
   for (i = 0; i < stringInfo.mStringCount; i++) {
     stringInfo.Reset(i);
   }
 
   return NS_OK;
 }
@@ -348,16 +351,32 @@ nsSVGElement::ParseAttribute(PRInt32 aNa
           lengthInfo.Reset(i);
         }
         foundMatch = PR_TRUE;
         break;
       }
     }
 
     if (!foundMatch) {
+      // Check for SVGAnimatedLengthList attribute
+      LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
+      for (i = 0; i < lengthListInfo.mLengthListCount; i++) {
+        if (aAttribute == *lengthListInfo.mLengthListInfo[i].mName) {
+          rv = lengthListInfo.mLengthLists[i].SetBaseValueString(aValue);
+          if (NS_FAILED(rv)) {
+            // ReportToConsole
+            lengthListInfo.Reset(i);
+          }
+          foundMatch = PR_TRUE;
+          break;
+        }
+      }
+    }
+
+    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)) {
@@ -529,16 +548,30 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespa
           DidChangeLength(i, PR_FALSE);
           foundMatch = PR_TRUE;
           break;
         }
       }
     }
 
     if (!foundMatch) {
+      // Check if this is a length list attribute going away
+      LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
+
+      for (PRUint32 i = 0; i < lengthListInfo.mLengthListCount; i++) {
+        if (aName == *lengthListInfo.mLengthListInfo[i].mName) {
+          lengthListInfo.Reset(i);
+          DidChangeLengthList(i, PR_FALSE);
+          foundMatch = PR_TRUE;
+          break;
+        }
+      }
+    }
+
+    if (!foundMatch) {
       // 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
@@ -664,22 +697,16 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespa
   }
 
   return rv;
 }
 
 void
 nsSVGElement::ResetOldStyleBaseType(nsISVGValue *svg_value)
 {
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> ll = do_QueryInterface(svg_value);
-  if (ll) {
-    nsCOMPtr<nsIDOMSVGLengthList> lengthlist;
-    ll->GetBaseVal(getter_AddRefs(lengthlist));
-    lengthlist->Clear();
-  }
   nsCOMPtr<nsIDOMSVGAnimatedNumberList> nl = do_QueryInterface(svg_value);
   if (nl) {
     nsCOMPtr<nsIDOMSVGNumberList> numberlist;
     nl->GetBaseVal(getter_AddRefs(numberlist));
     numberlist->Clear();
   }
   nsCOMPtr<nsIDOMSVGAnimatedTransformList> tl = do_QueryInterface(svg_value);
   if (tl) {
@@ -1425,16 +1452,95 @@ nsSVGElement::GetAnimatedLengthValues(fl
     else
       *f = info.mLengths[i++].GetAnimValue(ctx);
     f = va_arg(args, float*);
   }
 
   va_end(args);
 }
 
+nsSVGElement::LengthListAttributesInfo
+nsSVGElement::GetLengthListInfo()
+{
+  return LengthListAttributesInfo(nsnull, nsnull, 0);
+}
+
+void
+nsSVGElement::LengthListAttributesInfo::Reset(PRUint8 aAttrEnum)
+{
+  mLengthLists[aAttrEnum].ClearBaseValue(aAttrEnum);
+  // caller notifies
+}
+
+void
+nsSVGElement::DidChangeLengthList(PRUint8 aAttrEnum, PRBool aDoSetAttr)
+{
+  if (!aDoSetAttr)
+    return;
+
+  LengthListAttributesInfo info = GetLengthListInfo();
+
+  NS_ASSERTION(info.mLengthListCount > 0,
+               "DidChangeLengthList on element with no length list attribs");
+  NS_ASSERTION(aAttrEnum < info.mLengthListCount, "aAttrEnum out of range");
+
+  nsAutoString newStr;
+  info.mLengthLists[aAttrEnum].GetBaseValue().GetValueAsString(newStr);
+
+  SetAttr(kNameSpaceID_None, *info.mLengthListInfo[aAttrEnum].mName,
+          newStr, PR_TRUE);
+}
+
+void
+nsSVGElement::DidAnimateLengthList(PRUint8 aAttrEnum)
+{
+  nsIFrame* frame = GetPrimaryFrame();
+
+  if (frame) {
+    LengthListAttributesInfo info = GetLengthListInfo();
+    frame->AttributeChanged(kNameSpaceID_None,
+                            *info.mLengthListInfo[aAttrEnum].mName,
+                            nsIDOMMutationEvent::MODIFICATION);
+  }
+}
+
+void
+nsSVGElement::GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...)
+{
+  LengthListAttributesInfo info = GetLengthListInfo();
+
+  NS_ASSERTION(info.mLengthListCount > 0,
+               "GetAnimatedLengthListValues on element with no length list attribs");
+
+  SVGUserUnitList *list = aFirst;
+  PRUint32 i = 0;
+
+  va_list args;
+  va_start(args, aFirst);
+
+  while (list && i < info.mLengthListCount) {
+    list->Init(&(info.mLengthLists[i].GetAnimValue()), this, info.mLengthListInfo[i].mAxis);
+    ++i;
+    list = va_arg(args, SVGUserUnitList*);
+  }
+
+  va_end(args);
+}
+
+SVGAnimatedLengthList*
+nsSVGElement::GetAnimatedLengthList(PRUint8 aAttrEnum)
+{
+  LengthListAttributesInfo info = GetLengthListInfo();
+  if (aAttrEnum < info.mLengthListCount) {
+    return &(info.mLengthLists[aAttrEnum]);
+  }
+  NS_NOTREACHED("Bad attrEnum");
+  return nsnull;
+}
+
 nsSVGElement::NumberAttributesInfo
 nsSVGElement::GetNumberInfo()
 {
   return NumberAttributesInfo(nsnull, nsnull, 0);
 }
 
 void nsSVGElement::NumberAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
@@ -2054,16 +2160,30 @@ nsSVGElement::GetAnimatedAttr(nsIAtom* a
   }
 
   // preserveAspectRatio:
   if (aName == nsGkAtoms::preserveAspectRatio) {
     nsSVGPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio();
     return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this) : nsnull;
   }
 
+  // LengthLists:
+  {
+    LengthListAttributesInfo info = GetLengthListInfo();
+    for (PRUint32 i = 0; i < info.mLengthListCount; i++) {
+      if (aName == *info.mLengthListInfo[i].mName) {
+        NS_ABORT_IF_FALSE(i <= UCHAR_MAX, "Too many attributes");
+        return info.mLengthLists[i].ToSMILAttr(this,
+                                               PRUint8(i),
+                                               info.mLengthListInfo[i].mAxis,
+                                               info.mLengthListInfo[i].mCouldZeroPadList);
+      }
+    }
+  }
+
   // Mapped attributes:
   if (IsAttributeMapped(aName)) {
     nsCSSProperty prop = nsCSSProps::LookupProperty(nsAtomString(aName));
     // Check IsPropertyAnimatable to avoid attributes that...
     //  - map to explicitly unanimatable properties (e.g. 'direction')
     //  - map to unsupported attributes (e.g. 'glyph-orientation-horizontal')
     if (nsSMILCSSProperty::IsPropertyAnimatable(prop)) {
       return new nsSMILMappedAttribute(prop, this);
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -66,16 +66,22 @@ class nsSVGInteger;
 class nsSVGAngle;
 class nsSVGBoolean;
 class nsSVGEnum;
 struct nsSVGEnumMapping;
 class nsSVGViewBox;
 class nsSVGPreserveAspectRatio;
 class nsSVGString;
 struct gfxMatrix;
+namespace mozilla {
+class SVGAnimatedLengthList;
+class SVGUserUnitList;
+}
+
+using namespace mozilla;
 
 typedef nsStyledElement nsSVGElementBase;
 
 class nsSVGElement : public nsSVGElementBase,    // nsIContent
                      public nsISVGValueObserver  // :nsISupportsWeakReference
 {
 protected:
   nsSVGElement(nsINodeInfo *aNodeInfo);
@@ -154,31 +160,35 @@ public:
   virtual void DidChangeLength(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeNumber(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeInteger(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 DidChangeLengthList(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeString(PRUint8 aAttrEnum) {}
 
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateNumber(PRUint8 aAttrEnum);
   virtual void DidAnimateInteger(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 DidAnimateLengthList(PRUint8 aAttrEnum);
   virtual void DidAnimateTransform();
 
   void GetAnimatedLengthValues(float *aFirst, ...);
   void GetAnimatedNumberValues(float *aFirst, ...);
   void GetAnimatedIntegerValues(PRInt32 *aFirst, ...);
+  void GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...);
+  SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum);
 
 #ifdef MOZ_SMIL
   virtual nsISMILAttr* GetAnimatedAttr(nsIAtom* aName);
   void AnimationNeedsResample();
   void FlushAnimations();
 #endif
 
   virtual void RecompileScriptEventListeners();
@@ -325,16 +335,46 @@ protected:
                        EnumInfo *aEnumInfo,
                        PRUint32 aEnumCount) :
       mEnums(aEnums), mEnumInfo(aEnumInfo), mEnumCount(aEnumCount)
       {}
 
     void Reset(PRUint8 aAttrEnum);
   };
 
+  struct LengthListInfo {
+    nsIAtom** mName;
+    PRUint8   mAxis;
+    /**
+     * Flag to indicate whether appending zeros to the end of the list would
+     * change the rendering of the SVG for the attribute in question. For x and
+     * y on the <text> element this is true, but for dx and dy on <text> this
+     * is false. This flag is fed down to SVGLengthListSMILType so it can
+     * determine if it can sensibly animate from-to lists of different lengths,
+     * which is desirable in the case of dx and dy.
+     */
+    PRPackedBool mCouldZeroPadList;
+  };
+
+  struct LengthListAttributesInfo {
+    SVGAnimatedLengthList* mLengthLists;
+    LengthListInfo*        mLengthListInfo;
+    PRUint32               mLengthListCount;
+
+    LengthListAttributesInfo(SVGAnimatedLengthList *aLengthLists,
+                             LengthListInfo *aLengthListInfo,
+                             PRUint32 aLengthListCount)
+      : mLengthLists(aLengthLists)
+      , mLengthListInfo(aLengthListInfo)
+      , mLengthListCount(aLengthListCount)
+    {}
+
+    void Reset(PRUint8 aAttrEnum);
+  };
+
   struct StringInfo {
     nsIAtom**    mName;
     PRInt32      mNamespaceID;
   };
 
   struct StringAttributesInfo {
     nsSVGString*  mStrings;
     StringInfo*   mStringInfo;
@@ -354,16 +394,17 @@ protected:
   virtual IntegerAttributesInfo GetIntegerInfo();
   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 nsSVGPreserveAspectRatio *GetPreserveAspectRatio();
+  virtual LengthListAttributesInfo GetLengthListInfo();
   virtual StringAttributesInfo GetStringInfo();
 
   static nsSVGEnumMapping sSVGUnitTypesMap[];
 
 private:
   /* read <number-optional-number> */
   nsresult
   ParseNumberOptionalNumber(const nsAString& aValue,
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -53,17 +53,16 @@
 #include "nsSVGFilters.h"
 #include "nsLayoutUtils.h"
 #include "nsSVGUtils.h"
 #include "nsStyleContext.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "gfxContext.h"
 #include "gfxMatrix.h"
-#include "nsSVGLengthList.h"
 #include "nsIDOMSVGURIReference.h"
 #include "nsImageLoadingContent.h"
 #include "imgIContainer.h"
 #include "nsNetUtil.h"
 #include "nsSVGPreserveAspectRatio.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsSVGFilterElement.h"
 #include "nsSVGString.h"
deleted file mode 100644
--- a/content/svg/content/src/nsSVGLength.cpp
+++ /dev/null
@@ -1,522 +0,0 @@
-/* -*- 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
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *   Jonathan Watt <jonathan.watt@strath.ac.uk>
- *
- * 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 "nsSVGLength.h"
-#include "nsSVGUtils.h"
-#include "nsGkAtoms.h"
-#include "nsSVGValue.h"
-#include "nsTextFormatter.h"
-#include "prdtoa.h"
-#include "nsCRT.h"
-#include "nsSVGSVGElement.h"
-#include "nsIDOMSVGNumber.h"
-#include "nsISVGValueUtils.h"
-#include "nsWeakReference.h"
-#include "nsContentUtils.h"
-
-////////////////////////////////////////////////////////////////////////
-// nsSVGLength class
-
-class nsSVGLength : public nsISVGLength,
-                    public nsSVGValue
-{
-protected:
-  friend nsresult NS_NewSVGLength(nsISVGLength** result,
-                                  float value,
-                                  PRUint16 unit);
-
-  friend nsresult NS_NewSVGLength(nsISVGLength** result,
-                                  const nsAString &value);
-  
-  nsSVGLength(float value, PRUint16 unit);
-  nsSVGLength();
-
-public:
-  // nsISupports interface:
-  NS_DECL_ISUPPORTS
-
-  // nsIDOMSVGLength interface:
-  NS_DECL_NSIDOMSVGLENGTH
-
-  // nsISVGLength interface:
-  NS_IMETHOD SetContext(nsIWeakReference *aContext, PRUint8 aCtxType);
-
-  // nsISVGValue interface:
-  NS_IMETHOD SetValueString(const nsAString& aValue);
-  NS_IMETHOD GetValueString(nsAString& aValue);
-  
-  // nsISupportsWeakReference
-  // implementation inherited from nsSupportsWeakReference
-  
-protected:
-  // implementation helpers:
-  float mmPerPixel();
-  float AxisLength();
-  float EmLength();
-  float ExLength();
-  PRBool IsValidUnitType(PRUint16 unit);
-
-  nsWeakPtr mElement;  // owning element - weakptr to avoid reference loop
-  float mValueInSpecifiedUnits;
-  PRUint16 mSpecifiedUnitType;
-  PRUint8 mCtxType;
-};
-
-
-//----------------------------------------------------------------------
-// Implementation
-
-nsresult
-NS_NewSVGLength(nsISVGLength** result,
-                float value,
-                PRUint16 unit)
-{
-  *result = new nsSVGLength(value, unit);
-  if (!*result)
-    return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(*result);
-  return NS_OK;
-}
-
-nsresult
-NS_NewSVGLength(nsISVGLength** result,
-                const nsAString &value)
-{
-  *result = nsnull;
-  nsSVGLength *pl = new nsSVGLength();
-  if (!pl)
-    return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(pl);
-  nsresult rv = pl->SetValueAsString(value);
-  if (NS_FAILED(rv)) {
-    NS_RELEASE(pl);
-    return rv;
-  }
-  *result = pl;
-  return NS_OK;
-}
-
-
-nsSVGLength::nsSVGLength(float value,
-                         PRUint16 unit)
-    : mValueInSpecifiedUnits(value),
-      mSpecifiedUnitType(unit),
-      mCtxType(0)
-{
-}
-
-nsSVGLength::nsSVGLength()
-{
-}
-
-//----------------------------------------------------------------------
-// nsISupports methods:
-
-NS_IMPL_ADDREF(nsSVGLength)
-NS_IMPL_RELEASE(nsSVGLength)
-
-DOMCI_DATA(SVGLength, nsSVGLength)
-
-NS_INTERFACE_MAP_BEGIN(nsSVGLength)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValue)
-  NS_INTERFACE_MAP_ENTRY(nsISVGLength)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLength)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
-NS_INTERFACE_MAP_END
-
-//----------------------------------------------------------------------
-// nsISVGValue methods:
-
-NS_IMETHODIMP
-nsSVGLength::SetValueString(const nsAString& aValue)
-{
-  return SetValueAsString(aValue);
-}
-
-NS_IMETHODIMP
-nsSVGLength::GetValueString(nsAString& aValue)
-{
-  return GetValueAsString(aValue);
-}
-
-//----------------------------------------------------------------------
-// nsIDOMSVGLength methods:
-
-/* readonly attribute unsigned short unitType; */
-NS_IMETHODIMP
-nsSVGLength::GetUnitType(PRUint16 *aUnitType)
-{
-  *aUnitType = mSpecifiedUnitType;
-  return NS_OK;
-}
-
-/* attribute float value; */
-NS_IMETHODIMP
-nsSVGLength::GetValue(float *aValue)
-{
-  switch (mSpecifiedUnitType) {
-    case SVG_LENGTHTYPE_NUMBER:
-    case SVG_LENGTHTYPE_PX:
-      *aValue = mValueInSpecifiedUnits;
-      break;
-    case SVG_LENGTHTYPE_MM:
-      *aValue = mValueInSpecifiedUnits / mmPerPixel();
-      break;
-    case SVG_LENGTHTYPE_CM:
-      *aValue = mValueInSpecifiedUnits * 10.0f / mmPerPixel();
-      break;
-    case SVG_LENGTHTYPE_IN:
-      *aValue = mValueInSpecifiedUnits * 25.4f / mmPerPixel();
-      break;
-    case SVG_LENGTHTYPE_PT:
-      *aValue = mValueInSpecifiedUnits * 25.4f / POINTS_PER_INCH_FLOAT / 
-        mmPerPixel();
-      break;
-    case SVG_LENGTHTYPE_PC:
-      *aValue = mValueInSpecifiedUnits * 25.4f * 12.0f / POINTS_PER_INCH_FLOAT /
-        mmPerPixel();
-      break;
-    case SVG_LENGTHTYPE_PERCENTAGE:
-      *aValue = mValueInSpecifiedUnits * AxisLength() / 100.0f;
-      break;
-    case SVG_LENGTHTYPE_EMS:
-      *aValue = mValueInSpecifiedUnits * EmLength();
-      break;
-    case SVG_LENGTHTYPE_EXS:
-      *aValue = mValueInSpecifiedUnits * ExLength();
-      break;
-    default:
-      NS_NOTREACHED("Unknown unit type");
-      *aValue = 0;
-      return NS_ERROR_UNEXPECTED;
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSVGLength::SetValue(float aValue)
-{
-  NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
-
-  nsresult rv = NS_OK;
-
-  WillModify();
-
-  switch (mSpecifiedUnitType) {
-    case SVG_LENGTHTYPE_NUMBER:
-    case SVG_LENGTHTYPE_PX:
-      mValueInSpecifiedUnits = aValue;
-      break;
-    case SVG_LENGTHTYPE_MM:
-      mValueInSpecifiedUnits = aValue * mmPerPixel();
-      break;
-    case SVG_LENGTHTYPE_CM:
-      mValueInSpecifiedUnits = aValue * mmPerPixel() / 10.0f;
-      break;
-    case SVG_LENGTHTYPE_IN:
-      mValueInSpecifiedUnits = aValue * mmPerPixel() / 25.4f;
-      break;
-    case SVG_LENGTHTYPE_PT:
-      mValueInSpecifiedUnits = aValue * mmPerPixel() * POINTS_PER_INCH_FLOAT /
-        25.4f;
-      break;
-    case SVG_LENGTHTYPE_PC:
-      mValueInSpecifiedUnits = aValue * mmPerPixel() * POINTS_PER_INCH_FLOAT /
-        24.4f / 12.0f;
-      break;
-    case SVG_LENGTHTYPE_PERCENTAGE:
-      mValueInSpecifiedUnits = aValue * 100.0f / AxisLength();
-      break;
-    case SVG_LENGTHTYPE_EMS:
-      mValueInSpecifiedUnits = aValue / EmLength();
-      break;
-    case SVG_LENGTHTYPE_EXS:
-      mValueInSpecifiedUnits = aValue / ExLength();
-      break;
-    default:
-      NS_NOTREACHED("Unknown unit type");
-      mValueInSpecifiedUnits = 0;
-      rv = NS_ERROR_UNEXPECTED;
-      break;
-  }
-
-  DidModify();
-  return rv;
-}
-
-/* attribute float valueInSpecifiedUnits; */
-NS_IMETHODIMP
-nsSVGLength::GetValueInSpecifiedUnits(float *aValueInSpecifiedUnits)
-{
-  *aValueInSpecifiedUnits = mValueInSpecifiedUnits;
-  return NS_OK;
-}
-NS_IMETHODIMP
-nsSVGLength::SetValueInSpecifiedUnits(float aValueInSpecifiedUnits)
-{
-  NS_ENSURE_FINITE(aValueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
-  WillModify();
-  mValueInSpecifiedUnits = aValueInSpecifiedUnits;
-  DidModify();
-  return NS_OK;
-}
-
-/* attribute DOMString valueAsString; */
-NS_IMETHODIMP
-nsSVGLength::GetValueAsString(nsAString & aValueAsString)
-{
-  PRUnichar buf[24];
-  nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
-                            NS_LITERAL_STRING("%g").get(),
-                            (double)mValueInSpecifiedUnits);
-  aValueAsString.Assign(buf);
-
-  nsIAtom* UnitAtom = nsnull;
-
-  switch (mSpecifiedUnitType) {
-    case SVG_LENGTHTYPE_NUMBER:
-      return NS_OK;
-    case SVG_LENGTHTYPE_PX:
-      UnitAtom = nsGkAtoms::px;
-      break;
-    case SVG_LENGTHTYPE_MM:
-      UnitAtom = nsGkAtoms::mm;
-      break;
-    case SVG_LENGTHTYPE_CM:
-      UnitAtom = nsGkAtoms::cm;
-      break;
-    case SVG_LENGTHTYPE_IN:
-      UnitAtom = nsGkAtoms::in;
-      break;
-    case SVG_LENGTHTYPE_PT:
-      UnitAtom = nsGkAtoms::pt;
-      break;
-    case SVG_LENGTHTYPE_PC:
-      UnitAtom = nsGkAtoms::pc;
-      break;
-    case SVG_LENGTHTYPE_EMS:
-      UnitAtom = nsGkAtoms::em;
-      break;
-    case SVG_LENGTHTYPE_EXS:
-      UnitAtom = nsGkAtoms::ex;
-      break;
-    case SVG_LENGTHTYPE_PERCENTAGE:
-      UnitAtom = nsGkAtoms::percentage;
-      break;
-    default:
-      NS_NOTREACHED("Unknown unit");
-      return NS_ERROR_UNEXPECTED;
-  }
-
-  aValueAsString.Append(nsDependentAtomString(UnitAtom));
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSVGLength::SetValueAsString(const nsAString & aValueAsString)
-{
-  nsresult rv = NS_ERROR_DOM_SYNTAX_ERR;
-
-  char *str = ToNewCString(aValueAsString);
-
-  char* number = str;
-  while (*number && IsSVGWhitespace(*number))
-    ++number;
-
-  if (*number) {
-    char *rest;
-    float value = float(PR_strtod(number, &rest));
-    if (rest != number) {
-      const char* unitStr = nsCRT::strtok(rest, SVG_WSP_DELIM, &rest);
-      PRUint16 unitType = SVG_LENGTHTYPE_UNKNOWN;
-      if (!unitStr || *unitStr=='\0') {
-        unitType = SVG_LENGTHTYPE_NUMBER;
-      } else {
-        nsCOMPtr<nsIAtom> unitAtom = do_GetAtom(unitStr);
-        if (unitAtom == nsGkAtoms::px)
-          unitType = SVG_LENGTHTYPE_PX;
-        else if (unitAtom == nsGkAtoms::mm)
-          unitType = SVG_LENGTHTYPE_MM;
-        else if (unitAtom == nsGkAtoms::cm)
-          unitType = SVG_LENGTHTYPE_CM;
-        else if (unitAtom == nsGkAtoms::in)
-          unitType = SVG_LENGTHTYPE_IN;
-        else if (unitAtom == nsGkAtoms::pt)
-          unitType = SVG_LENGTHTYPE_PT;
-        else if (unitAtom == nsGkAtoms::pc)
-          unitType = SVG_LENGTHTYPE_PC;
-        else if (unitAtom == nsGkAtoms::em)
-          unitType = SVG_LENGTHTYPE_EMS;
-        else if (unitAtom == nsGkAtoms::ex)
-          unitType = SVG_LENGTHTYPE_EXS;
-        else if (unitAtom == nsGkAtoms::percentage)
-          unitType = SVG_LENGTHTYPE_PERCENTAGE;
-      }
-      if (IsValidUnitType(unitType) && NS_FloatIsFinite(value)) {
-        WillModify();
-        mValueInSpecifiedUnits = value;
-        mSpecifiedUnitType     = unitType;
-        DidModify();
-        rv = NS_OK;
-      }
-    }
-  }
-
-  nsMemory::Free(str);
-
-  return rv;
-}
-
-/* void newValueSpecifiedUnits (in unsigned short unitType, in float valueInSpecifiedUnits); */
-NS_IMETHODIMP
-nsSVGLength::NewValueSpecifiedUnits(PRUint16 unitType, float valueInSpecifiedUnits)
-{
-  NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
-
-  if (!IsValidUnitType(unitType))
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-
-  WillModify();
-  mValueInSpecifiedUnits = valueInSpecifiedUnits;
-  mSpecifiedUnitType     = unitType;
-  DidModify();
-
-  return NS_OK;
-}
-
-/* void convertToSpecifiedUnits (in unsigned short unitType); */
-NS_IMETHODIMP
-nsSVGLength::ConvertToSpecifiedUnits(PRUint16 unitType)
-{
-  if (!IsValidUnitType(unitType))
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-
-  WillModify();
-  float valueInUserUnits;
-  GetValue(&valueInUserUnits);
-  mSpecifiedUnitType = unitType;
-  SetValue(valueInUserUnits);
-  DidModify();
-
-  return NS_OK;
-}
-
-//----------------------------------------------------------------------
-// nsISVGLength methods:
-NS_IMETHODIMP
-nsSVGLength::SetContext(nsIWeakReference *aContext, PRUint8 aCtxType)
-{
-  mElement = aContext;
-  mCtxType = aCtxType;
-
-  return NS_OK;
-}
-
-
-//----------------------------------------------------------------------
-// Implementation helpers:
-
-float nsSVGLength::mmPerPixel()
-{
-  nsCOMPtr<nsIContent> element = do_QueryReferent(mElement);
-  if (!element) {
-    NS_WARNING("no context in mmPerPixel()");
-    return 1.0f;
-  }
-
-  nsSVGSVGElement *ctx = static_cast<nsSVGElement*>(element.get())->GetCtx();
-  if (!ctx) {
-    return 1e-4f; // some small value
-  }
-
-  float mmPerPx = ctx->GetMMPerPx(mCtxType);
-
-  if (mmPerPx == 0.0f) {
-    NS_ASSERTION(mmPerPx != 0.0f, "invalid mm/pixels");
-    mmPerPx = 1e-4f; // some small value
-  }
-
-  return mmPerPx;
-}
-
-float nsSVGLength::AxisLength()
-{
-  nsCOMPtr<nsIContent> element = do_QueryReferent(mElement);
-  if (!element) {
-    NS_WARNING("no context in AxisLength()");
-    return 1.0f;
-  }
-
-  nsSVGSVGElement *ctx = static_cast<nsSVGElement*>(element.get())->GetCtx();
-  if (!ctx) {
-    return 1e-20f; // some small value
-  }
-
-  float d = ctx->GetLength(mCtxType);
-
-  if (d == 0.0f) {
-    NS_WARNING("zero axis length");
-    d = 1e-20f;
-  }
-
-  return d;
-}
-
-float nsSVGLength::EmLength()
-{
-  nsCOMPtr<nsIContent> element = do_QueryReferent(mElement);
-  return nsSVGUtils::GetFontSize(element->AsElement());
-}
-
-float nsSVGLength::ExLength()
-{
-  nsCOMPtr<nsIContent> element = do_QueryReferent(mElement);
-  return nsSVGUtils::GetFontXHeight(element->AsElement());
-}
-
-PRBool nsSVGLength::IsValidUnitType(PRUint16 unit)
-{
-  if (unit > SVG_LENGTHTYPE_UNKNOWN && unit <= SVG_LENGTHTYPE_PC)
-    return PR_TRUE;
-
-  return PR_FALSE;
-}
-
deleted file mode 100644
--- a/content/svg/content/src/nsSVGLength.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- 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
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *
- * 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_SVGLENGTH_H__
-#define __NS_SVGLENGTH_H__
-
-#include "nsISVGLength.h"
-#include "nsAString.h"
-
-nsresult
-NS_NewSVGLength(nsISVGLength** result,
-                float value=0.0f,
-                PRUint16 unit=nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
-
-nsresult
-NS_NewSVGLength(nsISVGLength** result,
-                const nsAString &value);
-
-// XXX we'll need this prototype-based stuff to support unsetting:
-//nsresult NS_NewSVGLength(nsIDOMSVGLength** result,
-//                         nsIDOMSVGLength*  prototype);
-
-
-#endif //__NS_SVGLENGTH_H__
deleted file mode 100644
--- a/content/svg/content/src/nsSVGLengthList.cpp
+++ /dev/null
@@ -1,407 +0,0 @@
-/* -*- 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
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2002
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *
- * 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 "nsSVGLengthList.h"
-#include "nsSVGLength.h"
-#include "nsSVGValue.h"
-#include "nsWeakReference.h"
-#include "nsTArray.h"
-#include "nsDOMError.h"
-#include "nsReadableUtils.h"
-#include "nsSVGSVGElement.h"
-#include "nsCRT.h"
-#include "nsISVGValueUtils.h"
-#include "nsContentUtils.h"
-
-////////////////////////////////////////////////////////////////////////
-// nsSVGLengthList
-
-class nsSVGLengthList : public nsIDOMSVGLengthList,
-                        public nsSVGValue,
-                        public nsISVGValueObserver
-{  
-protected:
-  friend nsresult NS_NewSVGLengthList(nsIDOMSVGLengthList** result,
-                                      nsSVGElement *aContext,
-                                      PRUint8 aCtxType);
-
-  nsSVGLengthList(nsSVGElement *aContext, PRUint8 aCtxType);
-  ~nsSVGLengthList();
-//  void Init();
-  
-public:
-  // nsISupports interface:
-  NS_DECL_ISUPPORTS
-
-  // nsIDOMSVGLengthList interface:
-  NS_DECL_NSIDOMSVGLENGTHLIST
-
-  // remainder of nsISVGValue interface:
-  NS_IMETHOD SetValueString(const nsAString& aValue);
-  NS_IMETHOD GetValueString(nsAString& aValue);
-
-  // nsISVGValueObserver interface:
-  NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
-                                     modificationType aModType);
-  NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
-                                     modificationType aModType);
-  
-  // nsISupportsWeakReference
-  // implementation inherited from nsSupportsWeakReference
-
-protected:
-  // implementation helpers:
-  nsISVGLength* ElementAt(PRInt32 index);
-  void AppendElement(nsISVGLength* aElement);
-  void RemoveElementAt(PRInt32 index);
-  void InsertElementAt(nsISVGLength* aElement, PRInt32 index);
-  
-  void ReleaseLengths();
-  
-  nsAutoTArray<nsISVGLength*, 1> mLengths;
-  nsWeakPtr mContext;  // needs to be weak to avoid reference loop
-  PRUint8 mCtxType;
-};
-
-
-//----------------------------------------------------------------------
-// Implementation
-
-nsSVGLengthList::nsSVGLengthList(nsSVGElement *aContext, PRUint8 aCtxType)
-  : mCtxType(aCtxType)
-{
-  mContext = do_GetWeakReference(static_cast<nsGenericElement*>(aContext));
-}
-
-nsSVGLengthList::~nsSVGLengthList()
-{
-  ReleaseLengths();
-}
-
-//----------------------------------------------------------------------
-// nsISupports methods:
-
-NS_IMPL_ADDREF(nsSVGLengthList)
-NS_IMPL_RELEASE(nsSVGLengthList)
-
-DOMCI_DATA(SVGLengthList, nsSVGLengthList)
-
-NS_INTERFACE_MAP_BEGIN(nsSVGLengthList)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValue)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLengthList)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLengthList)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
-NS_INTERFACE_MAP_END
-
-//----------------------------------------------------------------------
-// nsISVGValue methods:
-
-NS_IMETHODIMP
-nsSVGLengthList::SetValueString(const nsAString& aValue)
-{
-  WillModify();
-  
-  ReleaseLengths();
-
-  nsresult rv = NS_OK;
-
-  char* str;
-  str = ToNewCString(aValue);
-
-  char* rest = str;
-  char* token;
-  const char* delimiters = ",\x20\x9\xD\xA";
-
-  while ((token = nsCRT::strtok(rest, delimiters, &rest))) {
-    nsCOMPtr<nsISVGLength> length;
-    NS_NewSVGLength(getter_AddRefs(length), NS_ConvertASCIItoUTF16(token));
-    if (!length) {
-      rv = NS_ERROR_DOM_SYNTAX_ERR;
-      break;
-    }
-    AppendElement(length);
-  }
-  
-  nsMemory::Free(str);
-  
-  DidModify();
-  return rv;
-}
-
-NS_IMETHODIMP
-nsSVGLengthList::GetValueString(nsAString& aValue)
-{
-  aValue.Truncate();
-
-  PRUint32 count = mLengths.Length();
-
-  if (count == 0) return NS_OK;
-
-  PRUint32 i = 0;
-  
-  while (1) {
-    nsISVGLength* length = ElementAt(i);
-    nsCOMPtr<nsISVGValue> val = do_QueryInterface(length);
-    NS_ASSERTION(val, "length doesn't implement required interface");
-    if (!val) continue;
-    nsAutoString str;
-    val->GetValueString(str);
-    aValue.Append(str);
-
-    if (++i >= count) break;
-
-    aValue.AppendLiteral(" ");
-  }
-  
-  return NS_OK;
-}
-
-//----------------------------------------------------------------------
-// nsIDOMSVGLengthList methods:
-
-/* readonly attribute unsigned long numberOfItems; */
-NS_IMETHODIMP nsSVGLengthList::GetNumberOfItems(PRUint32 *aNumberOfItems)
-{
-  *aNumberOfItems = mLengths.Length();
-  return NS_OK;
-}
-
-/* void clear (); */
-NS_IMETHODIMP nsSVGLengthList::Clear()
-{
-  WillModify();
-  ReleaseLengths();
-  DidModify();
-  return NS_OK;
-}
-
-/* nsIDOMSVGLength initialize (in nsIDOMSVGLength newItem); */
-NS_IMETHODIMP nsSVGLengthList::Initialize(nsIDOMSVGLength *newItem,
-                                          nsIDOMSVGLength **_retval)
-{
-  if (!newItem) {
-    *_retval = nsnull;
-    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
-  }
-  Clear();
-  return AppendItem(newItem, _retval);
-}
-
-/* nsIDOMSVGLength getItem (in unsigned long index); */
-NS_IMETHODIMP nsSVGLengthList::GetItem(PRUint32 index, nsIDOMSVGLength **_retval)
-{
-  if (index >= mLengths.Length()) {
-    *_retval = nsnull;
-    return NS_ERROR_DOM_INDEX_SIZE_ERR;
-  }
-
-  *_retval  = ElementAt(index);
-  NS_ADDREF(*_retval);
-  return NS_OK;
-}
-
-/* nsIDOMSVGLength insertItemBefore (in nsIDOMSVGLength newItem, in unsigned long index); */
-NS_IMETHODIMP
-nsSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem,
-                                  PRUint32 index,
-                                  nsIDOMSVGLength **_retval)
-{
-  // null check when implementing - this method can be used by scripts!
-  // if (!newItem)
-  //   return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
-
-  NS_NOTYETIMPLEMENTED("nsSVGLengthList::InsertItemBefore");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-/* nsIDOMSVGLength replaceItem (in nsIDOMSVGLength newItem, in unsigned long index); */
-NS_IMETHODIMP
-nsSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem,
-                             PRUint32 index,
-                             nsIDOMSVGLength **_retval)
-{
-  // null check when implementing - this method can be used by scripts!
-  // if (!newItem)
-  //   return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
-
-  NS_NOTYETIMPLEMENTED("nsSVGLengthList::ReplaceItem");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-/* nsIDOMSVGLengthList removeItem (in unsigned long index); */
-NS_IMETHODIMP nsSVGLengthList::RemoveItem(PRUint32 index, nsIDOMSVGLength **_retval)
-{
-  if (index >= mLengths.Length()) {
-    *_retval = nsnull;
-    return NS_ERROR_DOM_INDEX_SIZE_ERR;
-  }
-
-  *_retval = ElementAt(index);
-  NS_ADDREF(*_retval);
-  WillModify();
-  RemoveElementAt(index);
-  DidModify();
-  return NS_OK;
-}
-
-/* nsIDOMSVGLengthList appendItem (in nsIDOMSVGLengthList newItem); */
-NS_IMETHODIMP
-nsSVGLengthList::AppendItem(nsIDOMSVGLength *newItem, nsIDOMSVGLength **_retval)
-{
-  nsCOMPtr<nsISVGLength> length = do_QueryInterface(newItem);
-  if (!length) {
-    *_retval = nsnull;
-    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
-  }
-  AppendElement(length);
-
-  *_retval = newItem;
-  NS_ADDREF(*_retval);
-  return NS_OK;
-}
-
-//----------------------------------------------------------------------
-// nsISVGValueObserver methods
-
-NS_IMETHODIMP
-nsSVGLengthList::WillModifySVGObservable(nsISVGValue* observable,
-                                         modificationType aModType)
-{
-  WillModify(aModType);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSVGLengthList::DidModifySVGObservable(nsISVGValue* observable,
-                                        modificationType aModType)
-{
-  DidModify(aModType);
-  return NS_OK;
-}
-
-//----------------------------------------------------------------------
-// Implementation helpers
-
-void
-nsSVGLengthList::ReleaseLengths()
-{
-  WillModify();
-  PRUint32 count = mLengths.Length();
-  for (PRUint32 i = 0; i < count; ++i) {
-    nsISVGLength* length = ElementAt(i);
-    length->SetContext(nsnull, 0);
-    NS_REMOVE_SVGVALUE_OBSERVER(length);
-    NS_RELEASE(length);
-  }
-  mLengths.Clear();
-  DidModify();
-}
-
-nsISVGLength*
-nsSVGLengthList::ElementAt(PRInt32 index)
-{
-  return mLengths.ElementAt(index);
-}
-
-void
-nsSVGLengthList::AppendElement(nsISVGLength* aElement)
-{
-  WillModify();
-  NS_ADDREF(aElement);
-  
-  // The SVG specs state that 'if newItem is already in a list, it
-  // is removed from its previous list before it is inserted into this
-  // list':
-  //  aElement->SetListOwner(this);
-  
-  aElement->SetContext(mContext, mCtxType);
-  mLengths.AppendElement(aElement);
-  NS_ADD_SVGVALUE_OBSERVER(aElement);
-  DidModify();
-}
-
-void
-nsSVGLengthList::RemoveElementAt(PRInt32 index)
-{
-  WillModify();
-  nsISVGLength* length = ElementAt(index);
-  NS_ASSERTION(length, "null length");
-  NS_REMOVE_SVGVALUE_OBSERVER(length);
-  mLengths.RemoveElementAt(index);
-  NS_RELEASE(length);
-  DidModify();
-}
-
-void
-nsSVGLengthList::InsertElementAt(nsISVGLength* aElement, PRInt32 index)
-{
-  WillModify();
-  NS_ADDREF(aElement);
-
-  // The SVG specs state that 'if newItem is already in a list, it
-  // is removed from its previous list before it is inserted into this
-  // list':
-  //  aElement->SetListOwner(this);
-  
-  aElement->SetContext(mContext, mCtxType);
-  
-  mLengths.InsertElementAt(index, aElement);
-  NS_ADD_SVGVALUE_OBSERVER(aElement);
-  DidModify();
-}
-
-
-////////////////////////////////////////////////////////////////////////
-// Exported creation functions:
-
-nsresult
-NS_NewSVGLengthList(nsIDOMSVGLengthList** result, nsSVGElement *aContext, PRUint8 aCtxType)
-{
-  *result = nsnull;
-  
-  nsSVGLengthList* lengthList = new nsSVGLengthList(aContext, aCtxType);
-  if (!lengthList) return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(lengthList);
-
-  *result = lengthList;
-  
-  return NS_OK;
-}
-
deleted file mode 100644
--- a/content/svg/content/src/nsSVGLengthList.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- 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
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2002
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *
- * 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_SVGLENGTHLIST_H__
-#define __NS_SVGLENGTHLIST_H__
-
-#include "nsIDOMSVGLengthList.h"
-
-class nsSVGElement;
-
-nsresult
-NS_NewSVGLengthList(nsIDOMSVGLengthList** result, nsSVGElement *aContext, PRUint8 aCtxType);
-
-#endif //__NS_SVGLENGTHLIST_H__
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -34,17 +34,17 @@
  * 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 "nsGkAtoms.h"
-#include "nsSVGLength.h"
+#include "DOMSVGLength.h"
 #include "nsSVGAngle.h"
 #include "nsCOMPtr.h"
 #include "nsIPresShell.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
 #include "nsSVGMatrix.h"
 #include "nsSVGPoint.h"
@@ -650,17 +650,18 @@ nsSVGSVGElement::CreateSVGNumber(nsIDOMS
 {
   return NS_NewSVGNumber(_retval);
 }
 
 /* nsIDOMSVGLength createSVGLength (); */
 NS_IMETHODIMP
 nsSVGSVGElement::CreateSVGLength(nsIDOMSVGLength **_retval)
 {
-  return NS_NewSVGLength(reinterpret_cast<nsISVGLength**>(_retval));
+  NS_IF_ADDREF(*_retval = new DOMSVGLength());
+  return NS_OK;
 }
 
 /* nsIDOMSVGAngle createSVGAngle (); */
 NS_IMETHODIMP
 nsSVGSVGElement::CreateSVGAngle(nsIDOMSVGAngle **_retval)
 {
   return NS_NewDOMSVGAngle(_retval);
 }
--- a/content/svg/content/src/nsSVGStopElement.cpp
+++ b/content/svg/content/src/nsSVGStopElement.cpp
@@ -110,17 +110,17 @@ nsSVGStopElement::nsSVGStopElement(nsINo
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGStopElement)
 
 //----------------------------------------------------------------------
 // nsIDOMSVGStopElement methods
 
-/* readonly attribute nsIDOMSVGAnimatedLengthList x; */
+/* readonly attribute nsIDOMSVGAnimatedNumber offset; */
 NS_IMETHODIMP nsSVGStopElement::GetOffset(nsIDOMSVGAnimatedNumber * *aOffset)
 {
   return mOffset.ToDOMAnimatedNumber(aOffset,this);
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
--- a/content/svg/content/src/nsSVGTextElement.cpp
+++ b/content/svg/content/src/nsSVGTextElement.cpp
@@ -39,21 +39,24 @@
 #include "nsSVGGraphicElement.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMSVGTextElement.h"
 #include "nsCOMPtr.h"
 #include "nsSVGSVGElement.h"
 #include "nsSVGTextPositioningElement.h"
 #include "nsIFrame.h"
 #include "nsDOMError.h"
-#include "nsSVGLengthList.h"
-#include "nsSVGAnimatedLengthList.h"
+#include "SVGAnimatedLengthList.h"
+#include "DOMSVGAnimatedLengthList.h"
+#include "SVGLengthList.h"
 #include "nsSVGNumberList.h"
 #include "nsSVGAnimatedNumberList.h"
 
+using namespace mozilla;
+
 typedef nsSVGGraphicElement nsSVGTextElementBase;
 
 /**
  * This class does not inherit nsSVGTextPositioningElement - it reimplements it
  * instead.
  *
  * Ideally this class would inherit nsSVGTextPositioningElement in addition to
  * nsSVGGraphicElement, but we don't want two instances of nsSVGStylableElement
@@ -91,21 +94,24 @@ public:
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
 protected:
   nsSVGTextContainerFrame* GetTextContainerFrame() {
     return do_QueryFrame(GetPrimaryFrame(Flush_Layout));
   }
 
+  virtual LengthListAttributesInfo GetLengthListInfo();
+
   // nsIDOMSVGTextPositioning properties:
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> mX;
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> mY;
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> mdX;
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> mdY;
+
+  enum { X, Y, DX, DY };
+  SVGAnimatedLengthList mLengthListAttributes[4];
+  static LengthListInfo sLengthListInfo[4];
+
   nsCOMPtr<nsIDOMSVGAnimatedNumberList> mRotate;
 };
 
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Text)
 
 
 //----------------------------------------------------------------------
@@ -134,64 +140,16 @@ nsSVGTextElement::nsSVGTextElement(nsINo
 }
   
 nsresult
 nsSVGTextElement::Init()
 {
   nsresult rv = nsSVGTextElementBase::Init();
   NS_ENSURE_SUCCESS(rv,rv);
 
-  // DOM property: nsIDOMSVGTextPositioningElement::x, #IMPLIED attrib: x
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> lengthList;
-    rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::X);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mX),
-                                     lengthList);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = AddMappedSVGValue(nsGkAtoms::x, mX);
-    NS_ENSURE_SUCCESS(rv,rv);
-  }
-
-  // DOM property: nsIDOMSVGTextPositioningElement::y, #IMPLIED attrib: y
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> lengthList;
-    rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::Y);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mY),
-                                     lengthList);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = AddMappedSVGValue(nsGkAtoms::y, mY);
-    NS_ENSURE_SUCCESS(rv,rv);
-  }
-
-  // DOM property: nsIDOMSVGTextPositioningElement::dx, #IMPLIED attrib: dx
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> lengthList;
-    rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::X);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mdX),
-                                     lengthList);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = AddMappedSVGValue(nsGkAtoms::dx, mdX);
-    NS_ENSURE_SUCCESS(rv,rv);
-  }
-
-  // DOM property: nsIDOMSVGTextPositioningElement::dy, #IMPLIED attrib: dy
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> lengthList;
-    rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::Y);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mdY),
-                                     lengthList);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = AddMappedSVGValue(nsGkAtoms::dy, mdY);
-    NS_ENSURE_SUCCESS(rv,rv);
-  }
-
   // DOM property: nsIDOMSVGTextPositioningElement::rotate, #IMPLIED attrib: rotate
   {
     nsCOMPtr<nsIDOMSVGNumberList> numberList;
     rv = NS_NewSVGNumberList(getter_AddRefs(numberList));
     NS_ENSURE_SUCCESS(rv,rv);
     rv = NS_NewSVGAnimatedNumberList(getter_AddRefs(mRotate),
                                      numberList);
     NS_ENSURE_SUCCESS(rv,rv);
@@ -217,45 +175,45 @@ NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGTex
 
 //----------------------------------------------------------------------
 // nsIDOMSVGTextPositioningElement methods
 
 /* readonly attribute nsIDOMSVGAnimatedLengthList x; */
 NS_IMETHODIMP
 nsSVGTextElement::GetX(nsIDOMSVGAnimatedLengthList * *aX)
 {
-  *aX = mX;
-  NS_IF_ADDREF(*aX);
+  *aX = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[X],
+                                                this, X, nsSVGUtils::X).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGAnimatedLengthList y; */
 NS_IMETHODIMP
 nsSVGTextElement::GetY(nsIDOMSVGAnimatedLengthList * *aY)
 {
-  *aY = mY;
-  NS_IF_ADDREF(*aY);
+  *aY = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[Y],
+                                                this, Y, nsSVGUtils::Y).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGAnimatedLengthList dx; */
 NS_IMETHODIMP
 nsSVGTextElement::GetDx(nsIDOMSVGAnimatedLengthList * *aDx)
 {
-  *aDx = mdX;
-  NS_IF_ADDREF(*aDx);
+  *aDx = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[DX],
+                                                 this, DX, nsSVGUtils::X).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGAnimatedLengthList dy; */
 NS_IMETHODIMP
 nsSVGTextElement::GetDy(nsIDOMSVGAnimatedLengthList * *aDy)
 {
-  *aDy = mdY;
-  NS_IF_ADDREF(*aDy);
+  *aDy = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[DY],
+                                                 this, DY, nsSVGUtils::Y).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGAnimatedNumberList rotate; */
 NS_IMETHODIMP
 nsSVGTextElement::GetRotate(nsIDOMSVGAnimatedNumberList * *aRotate)
 {
   *aRotate = mRotate;
@@ -412,8 +370,27 @@ nsSVGTextElement::IsAttributeMapped(cons
   static const MappedAttributeEntry* const map[] = {
     sTextContentElementsMap,
     sFontSpecificationMap
   };
 
   return FindAttributeDependence(name, map, NS_ARRAY_LENGTH(map)) ||
     nsSVGTextElementBase::IsAttributeMapped(name);
 }
+
+//----------------------------------------------------------------------
+// nsSVGElement methods
+
+nsSVGElement::LengthListInfo nsSVGTextElement::sLengthListInfo[4] =
+{
+  { &nsGkAtoms::x,  nsSVGUtils::X, PR_FALSE },
+  { &nsGkAtoms::y,  nsSVGUtils::Y, PR_FALSE },
+  { &nsGkAtoms::dx, nsSVGUtils::X, PR_TRUE },
+  { &nsGkAtoms::dy, nsSVGUtils::Y, PR_TRUE }
+};
+
+nsSVGElement::LengthListAttributesInfo
+nsSVGTextElement::GetLengthListInfo()
+{
+  return LengthListAttributesInfo(mLengthListAttributes, sLengthListInfo,
+                                  NS_ARRAY_LENGTH(sLengthListInfo));
+}
+
--- a/content/svg/content/src/nsSVGTextPositioningElement.cpp
+++ b/content/svg/content/src/nsSVGTextPositioningElement.cpp
@@ -29,124 +29,95 @@
  * 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 "nsSVGTextPositioningElement.h"
-#include "nsSVGAnimatedLengthList.h"
+#include "SVGAnimatedLengthList.h"
+#include "DOMSVGAnimatedLengthList.h"
+#include "SVGLengthList.h"
 #include "nsSVGAnimatedNumberList.h"
-#include "nsSVGLengthList.h"
 #include "nsSVGNumberList.h"
 
+using namespace mozilla;
+
 nsresult
 nsSVGTextPositioningElement::Init()
 {
   nsresult rv = nsSVGTextPositioningElementBase::Init();
   NS_ENSURE_SUCCESS(rv,rv);
 
   // Create mapped properties:
 
-  // DOM property: nsIDOMSVGTextPositioningElement::x, #IMPLIED attrib: x
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> lengthList;
-    rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::X);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mX),
-                                     lengthList);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = AddMappedSVGValue(nsGkAtoms::x, mX);
-    NS_ENSURE_SUCCESS(rv,rv);
-  }
-  
-  // DOM property: nsIDOMSVGTextPositioningElement::y, #IMPLIED attrib: y
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> lengthList;
-    rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::Y);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mY),
-                                     lengthList);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = AddMappedSVGValue(nsGkAtoms::y, mY);
-    NS_ENSURE_SUCCESS(rv,rv);
-  }
-
-  // DOM property: nsIDOMSVGTextPositioningElement::dx, #IMPLIED attrib: dx
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> lengthList;
-    rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::X);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mdX),
-                                     lengthList);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = AddMappedSVGValue(nsGkAtoms::dx, mdX);
-    NS_ENSURE_SUCCESS(rv,rv);
-  }
-  
-  // DOM property: nsIDOMSVGTextPositioningElement::dy, #IMPLIED attrib: dy
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> lengthList;
-    rv = NS_NewSVGLengthList(getter_AddRefs(lengthList), this, nsSVGUtils::Y);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = NS_NewSVGAnimatedLengthList(getter_AddRefs(mdY),
-                                     lengthList);
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = AddMappedSVGValue(nsGkAtoms::dy, mdY);
-    NS_ENSURE_SUCCESS(rv,rv);
-  }
-
   // DOM property: nsIDOMSVGTextPositioningElement::rotate, #IMPLIED attrib: rotate
   {
     nsCOMPtr<nsIDOMSVGNumberList> numberList;
     rv = NS_NewSVGNumberList(getter_AddRefs(numberList));
     NS_ENSURE_SUCCESS(rv,rv);
     rv = NS_NewSVGAnimatedNumberList(getter_AddRefs(mRotate),
                                      numberList);
     NS_ENSURE_SUCCESS(rv,rv);
     rv = AddMappedSVGValue(nsGkAtoms::rotate, mRotate);
     NS_ENSURE_SUCCESS(rv,rv);
   }
 
   return rv;
 }
 
+nsSVGElement::LengthListInfo nsSVGTextPositioningElement::sLengthListInfo[4] =
+{
+  { &nsGkAtoms::x,  nsSVGUtils::X, PR_FALSE },
+  { &nsGkAtoms::y,  nsSVGUtils::Y, PR_FALSE },
+  { &nsGkAtoms::dx, nsSVGUtils::X, PR_TRUE },
+  { &nsGkAtoms::dy, nsSVGUtils::Y, PR_TRUE }
+};
+
+nsSVGElement::LengthListAttributesInfo
+nsSVGTextPositioningElement::GetLengthListInfo()
+{
+  return LengthListAttributesInfo(mLengthListAttributes, sLengthListInfo,
+                                  NS_ARRAY_LENGTH(sLengthListInfo));
+}
+
+
 //----------------------------------------------------------------------
 // nsIDOMSVGTextPositioningElement methods
 
 /* readonly attribute nsIDOMSVGAnimatedLengthList x; */
 NS_IMETHODIMP nsSVGTextPositioningElement::GetX(nsIDOMSVGAnimatedLengthList * *aX)
 {
-  *aX = mX;
-  NS_IF_ADDREF(*aX);
+  *aX = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[X],
+                                                this, X, nsSVGUtils::X).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGAnimatedLengthList y; */
 NS_IMETHODIMP nsSVGTextPositioningElement::GetY(nsIDOMSVGAnimatedLengthList * *aY)
 {
-  *aY = mY;
-  NS_IF_ADDREF(*aY);
+  *aY = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[Y],
+                                                this, Y, nsSVGUtils::Y).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGAnimatedLengthList dx; */
 NS_IMETHODIMP nsSVGTextPositioningElement::GetDx(nsIDOMSVGAnimatedLengthList * *aDx)
 {
-  *aDx = mdX;
-  NS_IF_ADDREF(*aDx);
+  *aDx = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[DX],
+                                                 this, DX, nsSVGUtils::X).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGAnimatedLengthList dy; */
 NS_IMETHODIMP nsSVGTextPositioningElement::GetDy(nsIDOMSVGAnimatedLengthList * *aDy)
 {
-  *aDy = mdY;
-  NS_IF_ADDREF(*aDy);
+  *aDy = DOMSVGAnimatedLengthList::GetDOMWrapper(&mLengthListAttributes[DY],
+                                                 this, DY, nsSVGUtils::Y).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGAnimatedNumberList rotate; */
 NS_IMETHODIMP nsSVGTextPositioningElement::GetRotate(nsIDOMSVGAnimatedNumberList * *aRotate)
 {
   *aRotate = mRotate;
   NS_IF_ADDREF(*aRotate);
--- a/content/svg/content/src/nsSVGTextPositioningElement.h
+++ b/content/svg/content/src/nsSVGTextPositioningElement.h
@@ -35,19 +35,24 @@
 
 #ifndef __NS_SVGTEXTPOSITIONINGELEMENTBASE_H__
 #define __NS_SVGTEXTPOSITIONINGELEMENTBASE_H__
 
 #include "nsIDOMSVGTextPositionElem.h"
 #include "nsSVGTextContentElement.h"
 #include "nsIDOMSVGAnimatedLengthList.h"
 #include "nsIDOMSVGAnimatedNumberList.h"
+#include "SVGAnimatedLengthList.h"
 
 class nsSVGElement;
 
+namespace mozilla {
+class SVGAnimatedLengthList;
+}
+
 typedef nsSVGTextContentElement nsSVGTextPositioningElementBase;
 
 /**
  * Note that nsSVGTextElement does not inherit this class - it reimplements it
  * instead (see its documenting comment). The upshot is that any changes to
  * this class also need to be made in nsSVGTextElement.
  */
 class nsSVGTextPositioningElement : public nsSVGTextPositioningElementBase
@@ -58,17 +63,20 @@ public:
 protected:
 
   nsSVGTextPositioningElement(nsINodeInfo *aNodeInfo)
     : nsSVGTextPositioningElementBase(aNodeInfo)
   {}
 
   nsresult Init();
 
+  virtual LengthListAttributesInfo GetLengthListInfo();
+
   // nsIDOMSVGTextPositioning properties:
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> mX;
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> mY;
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> mdX;
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> mdY;
+
+  enum { X, Y, DX, DY };
+  SVGAnimatedLengthList mLengthListAttributes[4];
+  static LengthListInfo sLengthListInfo[4];
+
   nsCOMPtr<nsIDOMSVGAnimatedNumberList> mRotate;
 };
 
 #endif
--- a/content/svg/content/test/Makefile.in
+++ b/content/svg/content/test/Makefile.in
@@ -59,16 +59,17 @@ include $(topsrcdir)/config/rules.mk
 		test_getSubStringLength.xhtml \
 		getSubStringLength-helper.svg \
 		test_isSupported.xhtml \
 		test_pathSeg.xhtml \
 		test_pointer-events.xhtml \
 		test_scientific.html \
 		scientific-helper.svg \
 		test_SVGStyleElement.xhtml \
+		test_SVGxxxList.xhtml \
 		test_switch.xhtml \
 		switch-helper.svg \
 		test_text.html \
 		text-helper.svg \
 		test_transform.xhtml \
 		test_valueAsString.xhtml \
 		test_valueLeaks.xhtml \
 		viewport-helper.svg \
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/test_SVGxxxList.xhtml
@@ -0,0 +1,1162 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=515116
+-->
+<head>
+  <title>Generic tests for SVG animated length lists</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=515116">Mozilla Bug 515116</a>
+<p id="display"></p>
+<div id="content" style="display:none;">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
+  <text id="text">text</text>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+
+SimpleTest.waitForExplicitFinish();
+
+/*
+This file runs a series of type-agnostic tests to check the state of the mini DOM trees that represent various SVG 'list' attributes (including checking the "object identity" of the objects in those trees) in the face of various changes, both with and without the complication of SMIL animation being active.
+
+For additional high level information on the tests that are run, see the comment for 'create_animate_elements' below.
+
+To have the battery of generic tests run for a new list attribute, add an element with that attribute to the document, then add a JavaScript object literal to the following 'tests' array with the following properties:
+
+  target_element_id
+    The ID of the element that has the attribute that is to be tested.
+  attr_name
+    The name of the attribute that is to be tested.
+  prop_name
+    The name of the DOM property that corresponds to the attribute that is to
+    be tested. For some list types the SVGAnimatedXxxList interface is
+    inherited by the element interface rather than the element having a
+    property of that type, and in others the list type is not animatable so
+    there is no SVGAnimatedXxxList interface for that list type. In these
+    cases this property should be set to null.
+  bv_name
+    The name of the DOM base value property for the attribute that is to be
+    tested. This is usually 'baseVal', but not always. In the case of
+    SVGStringList, which is not animatable, this is the name of the
+    SVGStringList property.
+  av_name
+    The name of the DOM anim value property for the attribute that is to be
+    tested. This is usually 'animVal' but not always. In the case of
+    SVGStringList, which is not animatable, this should be set to null.
+  el_type
+    The name of the SVGXxxElement interface on which the property corresponding
+    to the attribute being tested is defined.
+  prop_type
+    The name of the SVGAnimatedXxxList interface (e.g. SVGAnimatedLengthList),
+    if it exists, and if the element has a property is of this type (as
+    opposed to the element interface inheriting it).
+  list_type
+    The name of the SVGXxxList inteface implemented by the baseVal and
+    animVal objects.
+  item_type
+    The name of the SVGXxx interface implemented by the list items.
+  attr_val_3a:
+  attr_val_3b:
+    Two attribute values containing three different items.
+  attr_val_4
+    An attribute value containing four items.
+  attr_val_5a:
+  attr_val_5b:
+    Two attribute values containing five different items.
+*/
+
+var tests = [
+  {
+    // SVGLengthList test:
+    target_element_id: 'text',
+    attr_name: 'x',
+    prop_name: 'x',
+    bv_name: 'baseVal',
+    av_name: 'animVal',
+    el_type: 'SVGTextElement',
+    prop_type: 'SVGAnimatedLengthList',
+    list_type: 'SVGLengthList',
+    item_type: 'SVGLength',
+    attr_val_3a: '10 20ex, 30in',
+    attr_val_3b: '30in 10, 20ex',
+    attr_val_4 : '10 20ex, 30in ,40cm',
+    attr_val_5a: '10 20ex, 30in ,40cm , 50%',
+    attr_val_5b: '50% 10, 20ex ,30in , 40cm',
+    item_constructor: function() {
+      // We need this function literal to avoid "Illegal operation on
+      // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO.
+      return document.getElementById('svg').createSVGLength();
+    }
+  },
+  /*
+  {
+    // SVGNumberList test:
+    target_element_id: 'text',
+    attr_name: 'rotate',
+    prop_name: 'rotate',
+    bv_name: 'baseVal',
+    av_name: 'animVal',
+    el_type: 'SVGTextElement',
+    prop_type: 'SVGAnimatedNumberList',
+    list_type: 'SVGNumberList',
+    item_type: 'SVGNumber',
+    attr_val_3a: '',
+    attr_val_3b: '',
+    attr_val_4 : '',
+    attr_val_5a: '',
+    attr_val_5b: '',
+    item_constructor: document.getElementById('svg').createSVGNumber
+  },
+  {
+    // SVGNumberList test:
+    target_element_id: 'feComponentTransfer',
+    attr_name: 'tableValues',
+    prop_name: 'tableValues',
+    bv_name: 'baseVal',
+    av_name: 'animVal',
+    prop_type: 'SVGAnimatedNumberList',
+    list_type: 'SVGNumberList',
+    item_type: 'SVGNumber',
+    attr_val_3a: '',
+    attr_val_3b: '',
+    attr_val_4 : '',
+    attr_val_5a: '',
+    attr_val_5b: '',
+    item_constructor: document.getElementById('svg').createSVGNumber
+  },
+  {
+    // SVGPointList test:
+    target_element_id: 'polyline',
+    attr_name: 'points',
+    prop_name: null, // SVGAnimatedPoints is an inherited interface!
+    bv_name: 'points',
+    av_name: 'animatedPoints',
+    prop_type: null,
+    list_type: 'SVGPointList',
+    item_type: 'SVGPoint',
+    attr_val_3a: '',
+    attr_val_3b: '',
+    attr_val_4 : '',
+    attr_val_5a: '',
+    attr_val_5b: '',
+    item_constructor: document.getElementById('svg').createSVGPoint
+  },
+  {
+    // SVGPathSegList test:
+    target_element_id: 'path',
+    attr_name: 'd',
+    prop_name: null, // SVGAnimatedPathData is an inherited interface!
+    bv_name: 'pathSegList',
+    av_name: 'animatedPathSegList',
+    prop_type: null,
+    list_type: 'SVGPathSegList',
+    item_type: 'SVGPathSeg',
+    attr_val_3a: '',
+    attr_val_3b: '',
+    attr_val_4 : '',
+    attr_val_5a: '',
+    attr_val_5b: '',
+    item_constructor: function() {
+      // XXX return different values each time
+      return SVGPathElement.createSVGPathSegLinetoAbs(1, 1);
+    }
+  },
+  {
+    // SVGPathSegList test:
+    target_element_id: 'path',
+    attr_name: 'd',
+    prop_name: null, // SVGAnimatedPathData is an inherited interface!
+    bv_name: 'normalizedPathSegList',
+    av_name: 'animatedNormalizedPathSegList',
+    prop_type: null,
+    list_type: 'SVGPathSegList',
+    item_type: 'SVGPathSeg',
+    attr_val_3a: '',
+    attr_val_3b: '',
+    attr_val_4 : '',
+    attr_val_5a: '',
+    attr_val_5b: '',
+    item_constructor: function() {
+      // XXX return different values each time
+      return SVGPathElement.createSVGPathSegLinetoAbs(1, 1);
+    }
+  },
+  {
+    // SVGStringList test:
+    target_element_id: 'g',
+    attr_name: 'requiredFeatures', // requiredExtensions, systemLanguage, viewTarget
+    prop_name: null, // SVGStringList attributes are not animatable
+    bv_name: 'requiredFeatures',
+    av_name: null,
+    prop_type: null,
+    list_type: 'SVGStringList',
+    item_type: 'DOMString',
+    attr_val_3a: '',
+    attr_val_3b: '',
+    attr_val_4 : '',
+    attr_val_5a: '',
+    attr_val_5b: '',
+    item_constructor: function() {
+      // XXX return a meaningful string
+      return ;
+    }
+  },
+  {
+    // SVGTransformList test:
+
+    // XXX SVGTransformList has two extra methods, so add special case code to
+    // the test_xxx_API() functions below.
+
+    target_element_id: 'g',
+    attr_name: 'transform', // gradientTransform, patternTransform
+    prop_name: 'transform',
+    bv_name: 'baseVal',
+    av_name: 'animVal',
+    prop_type: 'SVGAnimatedTransformList',
+    list_type: 'SVGTransformList',
+    item_type: 'SVGTransform',
+    attr_val_3a: '',
+    attr_val_3b: '',
+    attr_val_4 : '',
+    attr_val_5a: '',
+    attr_val_5b: '',
+    item_constructor: function() {
+      // XXX populate the matrix with different values each time
+      return document.getElementById('svg').createSVGTransform();
+    }
+  },
+  */
+];
+
+
+/*
+This function returns a DocumentFragment with three 'animate' element children. The duration of the three animations is as follows:
+
+  animation 1: |  *-----------*-----------*
+  animation 2: |     *--*
+  animation 3: |                    *--*
+               |__________________________________> time (s)
+               |  |  |  |  |  |  |  |  |  |  |  |
+               0  1  2  3  4  5  6  7  8  9 10 11
+
+The first animation repeats once so that we can test state on a repeat animation.
+
+The second animation overrides the first animation for a short time, and has fewer list items than the first animation. This allows us to test object identity and other state on and after an overriding animation. Specifically, it allows us to check whether animVal list items are kept or discarded after the end of an overriding animation that has fewer items.
+
+The third animation has additive="sum", with fewer items than the lower priority animation 1, allowing us to test object identity and other state in that scenario. TODO: some type aware tests to check whether the composite fails or works?
+
+At t=0s and t=1s we test the affect of an attribute value changes in the absence and presence of SMIL animation respectively.
+
+At t=10s we programatically remove the fill="freeze" from animation 1.
+*/
+function create_animate_elements(test)
+{
+  var SVG_NS = 'http://www.w3.org/2000/svg';
+  var df = document.createDocumentFragment();
+
+  if (test.attr_name == 'transform' ||
+      test.attr_name == 'gradientTransform' ||
+      test.attr_name == 'patternTransform') {
+    ok(false, 'Need to update create_animate_element to handle transforms?')
+    throw new Error('Don\'t know how to handle transforms');
+    var animate1 =
+      document.createElementNS(SVG_NS, 'animateTransform');
+    var animate2 =
+      document.createElementNS(SVG_NS, 'animateTransform');
+    var animate3 =
+      document.createElementNS(SVG_NS, 'animateTransform');
+  } else {
+    var animate1 = document.createElementNS(SVG_NS, 'animate');
+    var animate2 = document.createElementNS(SVG_NS, 'animate');
+    var animate3 = document.createElementNS(SVG_NS, 'animate');
+  }
+
+  animate1.setAttribute('attributeName', test.attr_name);
+  animate1.setAttribute('from', test.attr_val_5a);
+  animate1.setAttribute('to', test.attr_val_5b);
+  animate1.setAttribute('begin', '1s');
+  animate1.setAttribute('dur', '4s');
+  animate1.setAttribute('repeatCount', '2');
+  animate1.setAttribute('fill', 'freeze');
+  df.appendChild(animate1);
+
+  animate2.setAttribute('attributeName', test.attr_name);
+  animate2.setAttribute('from', test.attr_val_3a);
+  animate2.setAttribute('to', test.attr_val_3b);
+  animate2.setAttribute('begin', '2s');
+  animate2.setAttribute('dur', '1s');
+  df.appendChild(animate2);
+
+  animate3.setAttribute('attributeName', test.attr_name);
+  animate3.setAttribute('from', test.attr_val_3a);
+  animate3.setAttribute('to', test.attr_val_3b);
+  animate3.setAttribute('begin', '7s');
+  animate3.setAttribute('dur', '1s');
+  animate3.setAttribute('additive', 'sum');
+  df.appendChild(animate3);
+
+  return df;
+}
+
+function get_array_of_list_items(list)
+{
+  array = [];
+  for (var i = 0; i < list.numberOfItems; ++i) {
+    array.push(list.getItem(i));
+  }
+  return array;
+}
+
+
+/**
+ * This function tests the SVGXxxList API for the base val list. This means
+ * running tests for the following property and methods:
+ *
+ *   numberOfItems
+ *   clear()
+ *   SVGLength initialize(in SVGLength newItem)
+ *   SVGLength getItem(in unsigned long index)
+ *   SVGLength insertItemBefore(in SVGLength newItem, in unsigned long index)
+ *   SVGLength replaceItem(in SVGLength newItem, in unsigned long index)
+ *   SVGLength removeItem(in unsigned long index)
+ *   SVGLength appendItem(in SVGLength newItem)
+ *
+ * @param t A test from the 'tests' array.
+ */
+function run_baseVal_API_tests()
+{
+  var res, threw, items;
+
+  for each (var t in tests) {
+
+    // Test .clear():
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+    is(t.baseVal.numberOfItems, 4,
+       'The '+t.list_type+' object should contain four list items.');
+
+    res = t.baseVal.clear();
+
+    is(t.baseVal.numberOfItems, 0,
+       'The method '+t.list_type+'.clear() should clear the '+t.list_type+
+       ' object.');
+    is(res, undefined,
+       'The method '+t.list_type+'.clear() should not return a value.');
+
+    // Test .initialize():
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+    var item = t.item_constructor();
+    var res = t.baseVal.initialize(item);
+
+    is(t.baseVal.numberOfItems, 1,
+       'The '+t.list_type+' object should contain one list item.');
+    ok(res === item,
+       'The list item returned by '+t.list_type+'.initialize() should be the '+
+       'exact same object as the item that was passed to that method, since '+
+       'the item that was passed to that method did not already belong to a '+
+       'list.');
+    ok(t.baseVal.getItem(0) === item,
+       'The list item at index 0 should be the exact same object as the '+
+       'object that was passed to the '+t.list_type+'.initialize() method, '+
+       'since the item that was passed to that method did not already '+
+       'belong to a list.');
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+    var old_items = get_array_of_list_items(t.baseVal);
+    item = t.baseVal.getItem(3);
+    res = t.baseVal.initialize(item);
+
+    ok(res !== item &&
+       t.baseVal.getItem(0) !== item &&
+       t.baseVal.getItem(0) !== old_items[0] &&
+       res === t.baseVal.getItem(0),
+       'The method '+t.list_type+'.initialize() should clone the object that '+
+       'is passed in if that object is already in a list.');
+    // [SVGWG issue] not what the spec currently says
+
+    item = t.baseVal.getItem(0);
+    res = t.baseVal.initialize(item);
+
+    ok(res !== item &&
+       t.baseVal.getItem(0) !== item,
+       'The method '+t.list_type+'.initialize() should clone the object that '+
+       'is passed in, even if that object is the only item in that list.');
+    // [SVGWG issue] not what the spec currently says
+
+    threw = false;
+    try {
+      t.baseVal.initialize({});
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.initialize() should throw if passed an '+
+       'object of the wrong type.');
+
+    // Test .insertItemBefore():
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+    old_items = get_array_of_list_items(t.baseVal);
+    item = t.item_constructor();
+    res = t.baseVal.insertItemBefore(item, 2);
+
+    is(t.baseVal.numberOfItems, 5,
+       'The '+t.list_type+' object should contain five list items.');
+    ok(res === item,
+       'The list item returned by '+t.list_type+'.insertItemBefore() should '+
+       'be the exact same object as the item that was passed to that method, '+
+       'since the item that was passed to that method did not already belong '+
+       'to a list.');
+    ok(t.baseVal.getItem(2) === item,
+       'The list item at index 2 should be the exact same object as the '+
+       'object that was passed to the '+t.list_type+'.insertItemBefore() '+
+       'method, since the item that was passed to that method did not '+
+       'already belong to a list.');
+    ok(t.baseVal.getItem(3) === old_items[2],
+       'The list item that was at index 2 should be at index 3 after '+
+       'inserting a new item at index 2 using the '+t.list_type+
+       '.insertItemBefore() method.');
+
+    item = t.item_constructor();
+    t.baseVal.insertItemBefore(item, 100);
+
+    ok(t.baseVal.getItem(5) === item,
+       'When the index passed to the '+t.list_type+'.insertItemBefore() '+
+       'method is out of bounds, the supplied list item should be appended '+
+       'to the list.');
+
+    item = t.baseVal.getItem(4);
+    res = t.baseVal.insertItemBefore(item, 2);
+
+    is(t.baseVal.numberOfItems, 7,
+       'The '+t.list_type+' object should contain seven list items.');
+    ok(res !== item &&
+       t.baseVal.getItem(2) !== item &&
+       t.baseVal.getItem(2) !== old_items[2] &&
+       res === t.baseVal.getItem(2),
+       'The method '+t.list_type+'.insertItemBefore() should clone the '+
+       'object that is passed in if that object is already in a list.');
+    // [SVGWG issue] not what the spec currently says
+
+    item = t.baseVal.getItem(2);
+    res = t.baseVal.insertItemBefore(item, 2);
+
+    is(t.baseVal.numberOfItems, 8,
+       'The '+t.list_type+' object should contain eight list items.');
+    ok(res !== item &&
+       t.baseVal.getItem(2) !== item,
+       'The method '+t.list_type+'.insertItemBefore() should clone the '+
+       'object that is passed in, even if that object is the item in '+
+       'the list at the index specified.');
+    // [SVGWG issue] not what the spec currently says
+
+    threw = false;
+    try {
+      t.baseVal.insertItemBefore({}, 2);
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.insertItemBefore() should throw if passed '+
+       'an object of the wrong type.');
+
+    // Test .replaceItem():
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+    old_items = get_array_of_list_items(t.baseVal);
+    item = t.item_constructor();
+    res = t.baseVal.replaceItem(item, 2);
+
+    is(t.baseVal.numberOfItems, 4,
+       'The '+t.list_type+' object should contain four list items.');
+    ok(res === item,
+       'The list item returned by '+t.list_type+'.replaceItem() should be '+
+       'the exact same object as the item that was passed to that method, '+
+       'since the item that was passed to that method did not already belong '+
+       'to a list.');
+    ok(t.baseVal.getItem(2) === item,
+       'The list item at index 2 should be the exact same object as the '+
+       'object that was passed to the '+t.list_type+'.replaceItem() method, '+
+       'since the item that was passed to that method did not already belong '+
+       'to a list.');
+    ok(t.baseVal.getItem(3) === old_items[3],
+       'The list item that was at index 3 should still be at index 3 after '+
+       'the item at index 2 was replaced using the '+t.list_type+
+       '.replaceItem() method.');
+
+    item = t.item_constructor();
+
+    threw = false;
+    try {
+      t.baseVal.replaceItem(item, 100);
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.replaceItem() should throw if passed '+
+       'an index that is out of bounds.');
+
+    old_items = get_array_of_list_items(t.baseVal);
+    item = t.baseVal.getItem(3);
+    res = t.baseVal.replaceItem(item, 1);
+
+    is(t.baseVal.numberOfItems, 4,
+       'The '+t.list_type+' object should contain four list items.');
+    ok(res !== item &&
+       t.baseVal.getItem(1) !== item &&
+       t.baseVal.getItem(1) !== old_items[1] &&
+       res === t.baseVal.getItem(1),
+       'The method '+t.list_type+'.replaceItem() should clone the object '+
+       'that is passed in if that object is already in a list.');
+    // [SVGWG issue] not what the spec currently says
+
+    item = t.baseVal.getItem(1);
+    res = t.baseVal.replaceItem(item, 1);
+
+    is(t.baseVal.numberOfItems, 4,
+       'The '+t.list_type+' object should contain four list items.');
+    ok(res !== item &&
+       t.baseVal.getItem(1) !== item,
+       'The method '+t.list_type+'.replaceItem() should clone the object '+
+       'that is passed in, even if the object that object and the object '+
+       'that is being replaced are the exact same objects.');
+    // [SVGWG issue] not what the spec currently says
+
+    threw = false;
+    try {
+      t.baseVal.replaceItem({}, 2);
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.replaceItem() should throw if passed '+
+       'an object of the wrong type.');
+
+    // Test .removeItem():
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+    old_items = get_array_of_list_items(t.baseVal);
+    item = t.baseVal.getItem(2);
+    res = t.baseVal.removeItem(2);
+
+    is(t.baseVal.numberOfItems, 3,
+       'The '+t.list_type+' object should contain three list items.');
+    ok(res === item,
+       'The list item returned by '+t.list_type+'.removeItem() should be the '+
+       'exact same object as the item that was at the specified index.');
+    ok(t.baseVal.getItem(1) === old_items[1],
+       'The list item that was at index 1 should still be at index 1 after '+
+       'the item at index 2 was removed using the '+t.list_type+
+       '.replaceItem() method.');
+    ok(t.baseVal.getItem(2) === old_items[3],
+       'The list item that was at index 3 should still be at index 2 after '+
+       'the item at index 2 was removed using the '+t.list_type+
+       '.replaceItem() method.');
+
+    threw = false;
+    try {
+      t.baseVal.removeItem(100);
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.removeItem() should throw if passed '+
+       'an index that is out of bounds.');
+
+    // Test .appendItem():
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+    old_items = get_array_of_list_items(t.baseVal);
+    item = t.item_constructor();
+    res = t.baseVal.appendItem(item);
+
+    is(t.baseVal.numberOfItems, 5,
+       'The '+t.list_type+' object should contain five list items.');
+    ok(res === item,
+       'The list item returned by '+t.list_type+'.appendItem() should be the '+
+       'exact same object as the item that was passed to that method, since '+
+       'the item that was passed to that method did not already belong '+
+       'to a list.');
+    ok(t.baseVal.getItem(4) === item,
+       'The last list item should be the exact same object as the object '+
+       'that was passed to the '+t.list_type+'.appendItem() method, since '+
+       'the item that was passed to that method did not already belong to '+
+       'a list.');
+    ok(t.baseVal.getItem(3) === old_items[3],
+       'The list item that was at index 4 should still be at index 4 after '+
+       'appending a new item using the '+t.list_type+'.appendItem() '+
+       'method.');
+
+    item = t.baseVal.getItem(2);
+    res = t.baseVal.appendItem(item);
+
+    is(t.baseVal.numberOfItems, 6,
+       'The '+t.list_type+' object should contain six list items.');
+    ok(res !== item &&
+       t.baseVal.getItem(5) !== item &&
+       res === t.baseVal.getItem(5),
+       'The method '+t.list_type+'.appendItem() should clone the object '+
+       'that is passed in if that object is already in a list.');
+    // [SVGWG issue] not what the spec currently says
+
+    item = t.baseVal.getItem(5);
+    res = t.baseVal.appendItem(item);
+
+    is(t.baseVal.numberOfItems, 7,
+       'The '+t.list_type+' object should contain seven list items.');
+    ok(res !== item &&
+       t.baseVal.getItem(6) !== item,
+       'The method '+t.list_type+'.appendItem() should clone the object '+
+       'that is passed in, if that object is already the last item in '+
+       'that list.');
+    // [SVGWG issue] not what the spec currently says
+
+
+
+    threw = false;
+    try {
+      t.baseVal.appendItem({});
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.appendItem() should throw if passed '+
+       'an object of the wrong type.');
+  }
+}
+
+
+/**
+ * This function tests the SVGXxxList API for the anim val list (see also the
+ * comment for test_baseVal_API).
+ */
+function run_animVal_API_tests()
+{
+  var threw, item;
+
+  for each (var t in tests) {
+    if (!t.animVal)
+      continue; // SVGStringList isn't animatable
+
+    item = t.item_constructor();
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+    is(t.animVal.numberOfItems, 4,
+       'The '+t.list_type+' object should contain four list items.');
+
+    // Test .clear():
+
+    threw = false;
+    try {
+      t.animVal.clear();
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.clear() should throw when called on an '+
+       'anim val list, since anim val lists should be readonly.');
+
+    // Test .getItem():
+
+    var item = t.animVal.getItem(2);
+    ok(item != null && item === t.animVal.getItem(2),
+       'The method '+t.list_type+'.getItem() should work when called on an '+
+       'anim val list, and always return the exact same object.');
+
+    // .initialize()
+
+    threw = false;
+    try {
+      t.animVal.initialize(item);
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.initialize() should throw when called on '+
+       'an anim val list, since anim val lists should be readonly.');
+
+    // Test .insertItemBefore():
+
+    threw = false;
+    try {
+      t.animVal.insertItemBefore(item, 2);
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.insertItemBefore() should throw when '+
+       'called on an anim val list, since anim val lists should be readonly.');
+
+    // Test .replaceItem():
+
+    threw = false;
+    try {
+      t.animVal.replaceItem(item, 2);
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.replaceItem() should throw when called '+
+       'on an anim val list, since anim val lists should be readonly.');
+
+    // Test .removeItem():
+
+    threw = false;
+    try {
+      t.animVal.removeItem(2);
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.removeItem() should throw when called '+
+       'on an anim val list, since anim val lists should be readonly.');
+
+    // Test .appendItem():
+
+    threw = false;
+    try {
+      t.animVal.appendItem(item);
+    } catch(e) {
+      threw = true;
+    }
+    ok(threw,
+       'The method '+t.list_type+'.appendItem() should throw when called '+
+       'on an anim val list, since anim val lists should be readonly.');
+  }
+}
+
+
+/**
+ * This function runs some basic tests to check the affect of setAttribute()
+ * calls on object identidy, without taking SMIL animation into consideration.
+ */
+function run_basic_setAttribute_tests()
+{
+  for each (var t in tests) {
+
+    // Since the t.prop, t.baseVal and t.animVal objects should never ever
+    // change, we leave testing of them to our caller so that it can check
+    // them after all the other mutations such as SMIL changes.
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+    ok(t.baseVal.numberOfItems == 4 && t.baseVal.getItem(3) != null,
+       'The length of the '+t.list_type+' object for '+t.bv_path+' should '+
+       'have been set to 4 by the setAttribute() call.');
+
+    if (t.animVal) {
+      ok(t.baseVal.numberOfItems == t.animVal.numberOfItems,
+         'When no animations are active, the '+t.list_type+' objects for '+
+         t.bv_path+' and '+t.av_path+' should be the same length (4).');
+
+      ok(t.baseVal !== t.animVal,
+         'The '+t.list_type+' objects for '+t.bv_path+' and '+t.av_path+
+         ' should be different objects.');
+
+      ok(t.baseVal.getItem(0) !== t.animVal.getItem(0),
+         'The '+t.item_type+' list items in the '+t.list_type+' objects for '+
+         t.bv_path+' and '+t.av_path+' should be different objects.');
+    }
+
+    ok(t.baseVal.getItem(0) === t.baseVal.getItem(0),
+       'The exact same '+t.item_type+' DOM object should be returned each '+
+       'time the item at a given index in the '+t.list_type+' for '+
+       t.bv_path+' is accessed, given that the index was not made invalid '+
+       'by a change in list length between the successive accesses.');
+
+    if (t.animVal) {
+      ok(t.animVal.getItem(0) === t.animVal.getItem(0),
+         'The exact same '+t.item_type+' DOM object should be returned each '+
+         'time the item at a given index in the '+t.list_type+' for '+
+         t.av_path+' is accessed, given that the index was not made invalid '+
+         'by a change in list length between the successive accesses.');
+    }
+
+    // Test the affect of setting the attribute to new values:
+
+    t.old_baseVal_items = get_array_of_list_items(t.baseVal);
+    t.old_animVal_items = get_array_of_list_items(t.animVal);
+
+    t.element.setAttribute(t.attr_name, t.attr_val_3a);
+    t.element.setAttribute(t.attr_name, t.attr_val_5a);
+
+    ok(t.baseVal.numberOfItems == 5 && t.baseVal.getItem(4) != null,
+       'The length of the '+t.list_type+' object for '+t.bv_path+' should '+
+       'have been set to 5 by the setAttribute() call.');
+
+    if (t.animVal) {
+      ok(t.baseVal.numberOfItems == t.animVal.numberOfItems,
+         'Since no animations are active, the length of the '+t.list_type+' '+
+         'objects for '+t.bv_path+' and '+t.av_path+' should be the same '+
+         '(5).');
+    }
+
+    ok(t.baseVal.getItem(2) === t.old_baseVal_items[2],
+       'After its attribute changes, list items in the '+t.list_type+' for '+
+       t.bv_path+' that are at indexes that existed prior to the attribute '+
+       'change should be the exact same objects as the objects that were '+
+       'at those indexes prior to the attribute change.');
+
+    ok(t.baseVal.getItem(3) !== t.old_baseVal_items[3],
+       'After its attribute changes, list items in the '+t.list_type+' for '+
+       t.bv_path+' that are at indexes that did not exist prior to the '+
+       'attribute change should not be the same objects as any objects that '+
+       'were at those indexes at some earlier time.');
+
+    if (t.animVal) {
+      ok(t.animVal.getItem(2) === t.old_animVal_items[2],
+         'After its attribute changes, list items in the '+t.list_type+' for '+
+         t.av_path+' that are at indexes that existed prior to the attribute '+
+         'change should be the exact same objects as the objects that were '+
+         'at those indexes prior to the attribute change.');
+
+      ok(t.animVal.getItem(3) !== t.old_animVal_items[3],
+         'After its attribute changes, list items in the '+t.list_type+' for '+
+         t.av_path+' that are at indexes that did not exist prior to the '+
+         'attribute change should not be the same objects as any objects '+
+         'that were at those indexes at some earlier time.');
+    }
+  }
+}
+
+
+/**
+ * In this function we run a series of tests at various points along the SMIL
+ * animation timeline, using SVGSVGElement.setCurrentTime() to move forward
+ * along the timeline.
+ *
+ * Since Mozilla doesn't currently support moving backwards along the timeline
+ * we run over all the tests before moving on to the next point in the
+ * timeline and running over all the tests again (rather than having one big
+ * 'for' loop).
+ *
+ * See the comment for create_animate_elements() for details of the animations
+ * and their timings.
+ */
+function run_animation_timeline_tests()
+{
+  var svg = document.getElementById('svg');
+
+  for each (var t in tests) {
+    // Reset attributes before moving along the timeline and triggering SMIL:
+    t.element.setAttribute(t.attr_name, t.attr_val_4);
+    t.old_baseVal_items = get_array_of_list_items(t.baseVal);
+    t.old_animVal_items = get_array_of_list_items(t.animVal);
+  }
+
+
+  /********************    t = 1s    ********************/
+
+  svg.setCurrentTime(1); // begin first animation
+
+  for each (var t in tests) {
+    if (!t.animVal)
+      continue;
+
+    ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+       t.baseVal.getItem(3) === t.old_baseVal_items[3],
+       'The start of an animation should never affect the '+t.list_type+
+       ' for '+t.bv_path+', or its list items.');
+
+    ok(t.animVal.numberOfItems == 5 && t.animVal.getItem(4) != null,
+       'The start of the animation should have changed the number of items '+
+       'in the '+t.list_type+' for '+t.bv_path+' to 5.');
+
+    ok(t.animVal.getItem(3) === t.old_animVal_items[3],
+       'When affected by SMIL animation, list items in the '+t.list_type+
+       ' for '+t.bv_path+' that are at indexes that existed prior to the '+
+       'start of the animation should be the exact same objects as the '+
+       'objects that were at those indexes prior to the start of the '+
+       'animation.');
+
+    t.old_animVal_items = get_array_of_list_items(t.animVal);
+
+    t.element.setAttribute(t.attr_name, t.attr_val_3a);
+
+    ok(t.baseVal.numberOfItems == 3 &&
+       t.baseVal.getItem(2) === t.old_baseVal_items[2],
+       'Setting the underlying attribute should change the items in the '+
+       t.list_type+' for '+t.bv_path+', including when an animation is '+
+       'in progress.');
+
+    ok(t.animVal.numberOfItems == 5 &&
+       t.animVal.getItem(4) === t.old_animVal_items[4],
+       'Setting the underlying attribute should not change the '+t.list_type+
+       ' for '+t.bv_path+' when an animation that does not depend on the '+
+       'base val is in progress.');
+
+    t.element.setAttribute(t.attr_name, t.attr_val_4); // reset
+
+    t.old_baseVal_items = get_array_of_list_items(t.baseVal);
+    t.old_animVal_items = get_array_of_list_items(t.animVal);
+  }
+
+
+  /********************    t = 2s    ********************/
+
+  svg.setCurrentTime(2); // begin override animation
+
+  for each (var t in tests) {
+    if (!t.animVal)
+      continue;
+
+    ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+       t.baseVal.getItem(3) === t.old_baseVal_items[3],
+       'The start of an override animation should never affect the '+
+       t.list_type+' for '+t.bv_path+', or its list items.');
+
+    is(t.animVal.numberOfItems, 3,
+       'The start of the override animation should have changed the number '+
+       'of items in the '+t.list_type+' for '+t.bv_path+' to 3.');
+
+    ok(t.animVal.getItem(2) === t.old_animVal_items[2],
+       'When affected by an override SMIL animation, list items in the '+
+       t.list_type+' for '+t.bv_path+' that are at indexes that existed '+
+       'prior to the start of the animation should be the exact same '+
+       'objects as the objects that were at those indexes prior to the '+
+       'start of that animation.');
+
+    t.old_animVal_items = get_array_of_list_items(t.animVal);
+  }
+
+
+  /********************    t = 3s    ********************/
+
+  svg.setCurrentTime(3); // end of override animation
+
+  // [SVGWG issue] what should happen at the exact instant the override
+  // animation is due to end?
+
+
+  /********************    t = 4s    ********************/
+
+  svg.setCurrentTime(4); // override animation definitely finished
+
+  for each (var t in tests) {
+    if (!t.animVal)
+      continue;
+
+    ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+       t.baseVal.getItem(3) === t.old_baseVal_items[3],
+       'The end of an override animation should never affect the '+
+       t.list_type+' for '+t.bv_path+', or its list items.');
+
+    is(t.animVal.numberOfItems, 5,
+       'At the end of the override animation, the number of items in the '+
+       t.list_type+' for '+t.bv_path+' should have reverted to 5.');
+
+    ok(t.animVal.getItem(2) === t.old_animVal_items[2],
+       'At the end of the override animation, list items in the '+
+       t.list_type+' for '+t.bv_path+' that are at indexes that existed '+
+       'prior to the end of the animation should be the exact same '+
+       'objects as the objects that were at those indexes prior to the '+
+       'end of that animation.');
+
+    t.old_animVal_items = get_array_of_list_items(t.animVal);
+  }
+
+
+  /********************    t = 5s    ********************/
+
+  svg.setCurrentTime(5); // animation repeat point
+
+  for each (var t in tests) {
+    if (!t.animVal)
+      continue;
+
+    ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+       t.baseVal.getItem(3) === t.old_baseVal_items[3],
+       'When a SMIL animation repeats, it should never affect the '+
+       t.list_type+' for '+t.bv_path+', or its list items.');
+
+    ok(t.animVal.numberOfItems == t.old_animVal_items.length &&
+       t.animVal.getItem(4) === t.old_animVal_items[4],
+       'When an animation repeats, the list items that are at a given '+
+       'index in the '+t.list_type+' for '+t.av_path+' should be the exact '+
+       'same objects as were at that index before the repeat occured.');
+  }
+
+
+  /********************    t = 6s    ********************/
+
+  svg.setCurrentTime(6); // inside animation repeat
+
+  for each (var t in tests) {
+    if (!t.animVal)
+      continue;
+
+    ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+       t.baseVal.getItem(3) === t.old_baseVal_items[3],
+       'When a SMIL animation repeats, it should never affect the '+
+       t.list_type+' for '+t.bv_path+', or its list items.');
+
+    ok(t.animVal.numberOfItems == t.old_animVal_items.length &&
+       t.animVal.getItem(4) === t.old_animVal_items[4],
+       'When an animation repeats, the list items that are at a given '+
+       'index in the '+t.list_type+' for '+t.av_path+' should be the exact '+
+       'same objects as were at that index before the repeat occured.');
+  }
+
+
+  /********************    t = 7s    ********************/
+
+  svg.setCurrentTime(7); // start of additive="sum" animation
+
+  for each (var t in tests) {
+    if (!t.animVal)
+      continue;
+
+    ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+       t.baseVal.getItem(3) === t.old_baseVal_items[3],
+       'When a new SMIL animation starts and should blend with an '+
+       'underlying animation, it should never affect the '+
+       t.list_type+' for '+t.bv_path+', or its list items.');
+
+    if (t.list_type == 'SVGLengthList') {
+
+      // Length lists are a special case where it makes sense to allow shorter
+      // lists to be composed on top of longer lists (but not necessarily vice
+      // versa - see comment below).
+
+      ok(t.animVal.numberOfItems == t.old_animVal_items.length &&
+         t.animVal.getItem(3) === t.old_animVal_items[3],
+         'When an animation with additive="sum" is added on top of an '+
+         'existing animation that has more list items, the length of the '+
+         t.list_type+' for '+t.av_path+' should not change.');
+
+    } else {
+
+      ok(false,
+         'Decide what to do here - see ' +
+         'https://bugzilla.mozilla.org/show_bug.cgi?id=573716 - we ' +
+         'probably should be discarding any animation sandwich layers from ' +
+         'a layer that fails to add, on up.');
+
+      // In other words, we wouldn't need the if-else check here.
+
+    }
+
+    // XXX what if the higher priority sandwich layer has *more* list items
+    // than the underlying animation? In that case we would need to
+    // distinguish between different SVGLengthList attributes, since although
+    // all SVGLengthList attributes allow a short list to be added to longer
+    // list, they do not all allow a longer list to be added to shorter list.
+    // Specifically that would not be good for 'x' and 'y' on <text> since
+    // lengths there are not naturally zero. See the comment in
+    // SVGLengthListSMILAttr::Add().
+  }
+
+
+  /********************    t = 10s    ********************/
+
+  svg.setCurrentTime(10); // all animations have finished, but one is frozen
+
+  for each (var t in tests) {
+    if (!t.animVal)
+      continue;
+
+    ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+       t.baseVal.getItem(3) === t.old_baseVal_items[3],
+       'When a SMIL animation ends, it should never affect the '+
+       t.list_type+' for '+t.bv_path+', or its list items.');
+
+    is(t.animVal.numberOfItems, 5,
+       'Even though all SMIL animation have finished, the number '+
+       'of items in the '+t.list_type+' for '+t.av_path+
+       ' should still be more than the same as the number of items in '+
+       t.bv_path+' since one of the animations is still frozen.');
+
+    t.element.querySelector('animate').removeAttribute('fill');
+
+    ok(t.animVal.numberOfItems == t.baseVal.numberOfItems,
+       'Once all SMIL animation have finished and been un-frozen, the number '+
+       'of items in the '+t.list_type+' for '+t.av_path+
+       ' should be the same as the number of items in '+t.bv_path+'.');
+
+    ok(t.animVal.getItem(2) === t.old_animVal_items[2],
+       'Even after an animation finishes and is un-frozen, the list items '+
+       'that are at a given index in the '+t.list_type+' for '+t.av_path+
+       ' should be the exact same objects as were at that index before the '+
+       'end and unfreezing of the animation occured.');
+   }
+}
+
+
+function run_tests()
+{
+    document.getElementById('svg').pauseAnimations();
+
+  // Initialize each test object with some useful properties, and create their
+  // 'animate' elements. Note that 'prop' and 'animVal' may be null.
+  for each (var t in tests) {
+    t.element = document.getElementById(t.target_element_id);
+    t.prop = t.prop_name ? t.element[t.prop_name] : null;
+    t.baseVal = ( t.prop || t.element )[t.bv_name];
+    t.animVal = t.av_name ? ( t.prop || t.element )[t.av_name] : null;
+    t.bv_path = t.el_type + '.' +
+                (t.prop ? t.prop_name + '.' : '') +
+                t.bv_name;  // e.g. 'SVGTextElement.x.baseVal'
+    if (t.animVal) {
+      t.av_path = t.el_type + '.' +
+                  (t.prop ? t.prop_name + '.' : '') +
+                  t.av_name;
+    }
+    t.prop_type = t.prop_type || null;
+
+    t.element.appendChild(create_animate_elements(t));
+  }
+
+  // Run the major test groups:
+
+  run_baseVal_API_tests();
+  run_animVal_API_tests();
+  run_basic_setAttribute_tests();
+  run_animation_timeline_tests();
+
+  // After all the other test manipulations, we check that the following
+  // objects have still not changed, since they never should:
+
+  for each (var t in tests) {
+    if (t.prop) {
+      ok(t.prop === t.element[t.prop_name],
+         'The same '+t.prop_type+' object should ALWAYS be returned for '+
+         t.el_type+'.'+t.prop_name+' each time it is accessed.');
+    }
+
+    ok(t.baseVal === ( t.prop || t.element )[t.bv_name],
+       'The same '+t.list_type+' object should ALWAYS be returned for '+
+       t.el_type+'.'+t.prop_name+'.'+t.bv_name+' each time it is accessed.');
+
+    if (t.animVal) {
+      ok(t.animVal === ( t.prop || t.element )[t.av_name],
+         'The same '+t.list_type+' object should ALWAYS be returned for '+
+         t.el_type+'.'+t.prop_name+'.'+t.av_name+' each time it is accessed.');
+    }
+  }
+
+  SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests, false);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/anim-text-x-y-dx-dy-01-ref.svg
@@ -0,0 +1,23 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+
+  <text transform="translate(20, 20)"
+        x="20 10mm 3pc 72pt 2em 3% 1ex"
+        dy="20 10mm 3pc 36pt 2em 3% 1ex 1">ABCDEFGH
+  </text>
+
+  <text transform="translate(120, 20)"
+        x="20 10mm 3pc 72pt 2em 3% 1ex"
+        dy="20 10mm 3pc 36pt 2em 3% 1ex">IJKLMNOP
+  </text>
+
+  <text transform="translate(220, 20)"
+        x="20 10mm 3pc 72pt 2em 3% 1ex"
+        dy="20 10mm 3pc 36pt 2em 3% 1ex 1">QRSTUVWX
+  </text>
+
+  <text transform="translate(320, 20)"
+        x="20 10mm 3pc 72pt 2em 3% 1ex"
+        dy="20 10mm 3pc 36pt 2em 3% 1ex">YZ123456
+  </text>
+
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/anim-text-x-y-dx-dy-01.svg
@@ -0,0 +1,179 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="setTimeAndSnapshot(5, true)">
+  <title>Test animation of the &lt;length-list&gt; attributes on the 'text' element</title>
+  <script xlink:href="smil-util.js" type="text/javascript"/>
+
+  <style type="text/css">
+    :root { font-size: 16px; }  /* see comment below - sets 1em == 16px */
+  </style>
+
+  <!-- If we start getting random orange on this test, see:
+       https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c34
+    -->
+
+  <!-- One of the things that this file tests is animation between lengths
+       of _different unit_. One difficulty this creates is knowing the
+       values to use in the reference file. For example, what length should
+       be used in the reference to comparing against an animation that's mid
+       way between 50px and 10in? The SMIL engine will convert the start
+       length to the unit of the end length and then interpolate, but the
+       number of inches in 50px is not always the same, so we can't fix how
+       many inches is midway between 50px and 10in in the reference file. To
+       get around this problem this test mainly pairs different units with a
+       knows, fixed, relationship. For example, animating between cm and mm,
+       or between 'in' and pt (72 pt/in) or between 'in' and pc (6 pc/in).
+       Note that we can animate between px and em by fixing the relationship
+       between these units by setting the CSS 'font-size' property to a fixed
+       number of px units as we've done above.
+
+       The problem with only testing pairs with a fixed relationship is that
+       implementations may only implement interpolation between those pairs
+       because it's easy. To test interpolation between pairs of units
+       without a fixed relationship we use another strategy: we animate from
+       zero of the start unit. Since zero is zero regardless of the unit, we
+       then know what to use in the reference. In theory implementations might
+       specialize for zero, but that's unlikely (hopefully!).
+
+       (An alternative would be to only test end points of the animation, but
+       implementations may use discrete animation rather than interpolation
+       when lengths of different units are encountered, so that would be a bad
+       approach.)
+
+       (Another better alternative would be to use
+       SVGLength.convertToSpecifiedUnits() in the reference file to figure out
+       the conversion of the start unit and set values in the reference file
+       dynamically.)
+  -->
+
+  <!-- Another thing that this file test is animation between lists
+       of _different length_. One implementation strategy when faced with such
+       an animation is to pad the shorter list with zeros for the purposes of
+       animation. This works exactly as you would hope in the case of the
+       <text> element's 'dx' and 'dy' attributes, since for those attributes
+       lengths are offsets from the coordinate at which respective glyph's
+       would otherwise be positioned. In other words, appending a list of
+       zeros to any 'dx' or 'dy' attribute will never have an affect on
+       rendering. However, in the case of 'x' and 'y' attributes on <text>,
+       any lengths given are distances from zero along the relevant axis of
+       the current coordinate system, and zero is not likely to be the
+       position that all glyphs would otherwise be given. Their position will
+       actually depend on CSS parameters, the implementation's text layout
+       algorithm, and the layout of the characters that came before it. Hence
+       zero padding any 'x' or 'y' attribute will likely drastically alter
+       the rendering of the text, and in the case of animation would cause
+       glyphs to animate from/to zero in their coordinate system instead of
+       from/to their natural position. Clearly this is neither what authors
+       would expect, or want, so Mozilla currently disallows animation of 'x'
+       and 'y' if lists of different length are encountered to prevent content
+       being created that relies on such undesirable behavior.
+
+       Ideally the implementation would provide the SMIL engine with the
+       natural position of each glyph so that it can animate from/to those
+       positions. That's tricky, but we do have a bug open to try and
+       implement that. See:
+
+       https://bugzilla.mozilla.org/show_bug.cgi?id=573431
+    -->
+
+  <!-- Test calcMode="linear". -->
+
+  <text transform="translate(20, 20)"
+        x="10px 0.5cm 0.25in 0.5in 16px 0cm 0%">ABCDEFGH
+
+    <!-- At 5s the animVal should be "20 10mm 3pc 72pt 2em 3% 1ex". -->
+    <animate attributeName="x"
+             calcMode="linear"
+             begin="0s" dur="15s"
+             to="40 20mm 6pc 144pt 4em 9% 3ex"
+             fill="freeze"/>
+
+    <!-- At 5s the animVal should be "20 10mm 3pc 36pt 2em 3% 1ex 1".
+         Note that the 'to' list has one extra list item on purpose! -->
+    <animate attributeName="dy"
+             calcMode="linear"
+             begin="0s" dur="15s"
+             from="10px 0.5cm 0.25in 0.25in 16px 0cm 0%"
+             to="40 20mm 6pc 72pt 4em 9% 3ex 3"
+             fill="freeze"/>
+  </text>
+
+
+  <!-- Test 'by' animation. -->
+
+  <text transform="translate(120, 20)"
+        x="10px 0.5cm 0.25in 0.5in 16px 0cm 0%">IJKLMNOP
+
+    <!-- At 5s the animVal should be "20 10mm 3pc 72pt 2em 3% 1ex". -->
+    <animate attributeName="x"
+             calcMode="linear"
+             begin="0s" dur="15s"
+             by="30 15mm 4.5pc 108pt 3em 9% 3ex"
+             fill="freeze"/>
+
+    <!-- At 5s the animVal should be "20 10mm 3pc 36pt 2em 3% 1ex".
+         Note that the 'from' list is essentially zero length! -->
+    <animate attributeName="dy"
+             calcMode="linear"
+             begin="0s" dur="15s"
+             by="60 30mm 9pc 108pt 6em 9% 3ex"
+             fill="freeze"/>
+  </text>
+
+
+  <!-- Test calcMode="paced". It doesn't make a lot of sense to use paced
+       animation with a length list, but since we support it, we test it.
+  -->
+
+  <text transform="translate(220, 20)">QRSTUVWX
+
+    <!-- At 5s the animVal should be "20 10mm 3pc 72pt 2em 3% 1ex". -->
+    <animate attributeName="x"
+             calcMode="paced"
+             begin="0s" dur="15s"
+             values="10px 0.5cm 0.25in 0.5in 16px 0cm 0%;
+                     30 15mm 4.5pc 108pt 3em 6% 2ex;
+                     40 20mm 6pc 144pt 4em 9% 3ex"
+             fill="freeze"/>
+
+    <!-- At 5s the animVal should be "20 10mm 3pc 36pt 2em 3% 1ex 1".
+         Note that the 'to' lists have one extra list item on purpose! -->
+    <animate attributeName="dy"
+             calcMode="paced"
+             begin="0s" dur="15s"
+             values="10px 0.5cm 0.25in 0.25in 16px 0cm 0%;
+                     30 15mm 4.5pc 54pt 3em 6% 2ex 2;
+                     40 20mm 6pc 72pt 4em 9% 3ex 3"
+             fill="freeze"/>
+  </text>
+
+
+  <!-- Test calcMode="discrete". In this case SMIL treats the 'from' and 'to'
+       as two discrete values to jump between. Some authors may expect
+       discrete animation to jump from integer to integer in the unit of the
+       list item in question (or the unit of the 'to' item if the units of
+       corresponding 'from' and 'to' items differ), but that's not the case.
+  -->
+
+  <text transform="translate(320, 20)">YZ123456
+
+    <!-- At 5s the animVal should be "20 10mm 3pc 72pt 2em 3% 1ex". -->
+    <animate attributeName="x"
+             calcMode="discrete"
+             begin="0s" dur="10s"
+             from="10px 0.5cm 0.25in 0.5in 16px 0cm 0%"
+             to="20 10mm 3pc 72pt 2em 3% 1ex"
+             fill="freeze"/>
+
+    <!-- At 5s the animVal should be "20 10mm 3pc 36pt 2em 3% 1ex".
+         Note that the 'to' list has one extra list item on purpose! -->
+    <animate attributeName="dy"
+             calcMode="discrete"
+             begin="0s" dur="10.1s"
+             from="20 10mm 3pc 36pt 2em 3% 1ex"
+             to="40px 2cm 1in 1in 16px 0cm 0% 3"
+             fill="freeze"/>
+  </text>
+
+</svg>
--- a/layout/reftests/svg/smil/reftest.list
+++ b/layout/reftests/svg/smil/reftest.list
@@ -131,16 +131,18 @@ fails == anim-strokecolor-1.svg anim-sta
 == anim-targethref-3.svg anim-standard-ref.svg
 == anim-targethref-4.svg anim-standard-ref.svg
 == anim-targethref-5.svg anim-standard-ref.svg
 == anim-targethref-6.svg anim-standard-ref.svg
 == anim-targethref-7.svg anim-standard-ref.svg
 == anim-targethref-8.svg anim-standard-ref.svg
 == anim-targethref-9.svg anim-standard-ref.svg
 
+== anim-text-x-y-dx-dy-01.svg anim-text-x-y-dx-dy-01-ref.svg
+
 == anim-width-done-1a.svg anim-standard-ref.svg
 == anim-width-done-1b.svg anim-standard-ref.svg
 
 == anim-x-done-1a.svg anim-standard-ref.svg
 == anim-x-done-1b.svg anim-standard-ref.svg
 == anim-x-interp-1.svg anim-x-interp-1-ref.svg
 == anim-x-interp-2.svg anim-x-interp-2-ref.svg
 == anim-x-interp-3.svg anim-x-interp-3-ref.svg
--- a/layout/svg/base/src/nsISVGGlyphFragmentLeaf.h
+++ b/layout/svg/base/src/nsISVGGlyphFragmentLeaf.h
@@ -35,21 +35,23 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NS_ISVGGLYPHFRAGMENTLEAF_H__
 #define __NS_ISVGGLYPHFRAGMENTLEAF_H__
 
 #include "nsISVGGlyphFragmentNode.h"
-#include "nsIDOMSVGLengthList.h"
 
 class nsIDOMSVGPoint;
 class nsIDOMSVGRect;
 class nsSVGTextPathFrame;
+namespace mozilla {
+class SVGUserUnitList;
+}
 
 class nsISVGGlyphFragmentLeaf : public nsISVGGlyphFragmentNode
 {
 public:
   NS_DECL_QUERYFRAME_TARGET(nsISVGGlyphFragmentLeaf)
 
   NS_IMETHOD GetStartPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)=0;
   NS_IMETHOD GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)=0;
@@ -57,15 +59,14 @@ public:
   NS_IMETHOD GetRotationOfChar(PRUint32 charnum, float *_retval)=0;
 
   NS_IMETHOD_(float) GetAdvance(PRBool aForceGlobalTransform)=0;
 
   NS_IMETHOD_(void) SetGlyphPosition(gfxPoint *aPosition, PRBool aForceGlobalTransform)=0;
   NS_IMETHOD_(nsSVGTextPathFrame*) FindTextPathParent()=0;
   NS_IMETHOD_(PRBool) IsStartOfChunk()=0; // == is new absolutely positioned chunk.
 
-  NS_IMETHOD_(already_AddRefed<nsIDOMSVGLengthList>) GetX()=0;
-  NS_IMETHOD_(already_AddRefed<nsIDOMSVGLengthList>) GetY()=0;
+  NS_IMETHOD_(void) GetXY(mozilla::SVGUserUnitList *aX, mozilla::SVGUserUnitList *aY)=0;
   NS_IMETHOD_(PRUint16) GetTextAnchor()=0;
   NS_IMETHOD_(PRBool) IsAbsolutelyPositioned()=0;
 };
 
 #endif // __NS_ISVGGLYPHFRAGMENTLEAF_H__
--- a/layout/svg/base/src/nsSVGAFrame.cpp
+++ b/layout/svg/base/src/nsSVGAFrame.cpp
@@ -38,22 +38,25 @@
 
 #include "nsSVGTSpanFrame.h"
 #include "nsISVGGlyphFragmentNode.h"
 #include "nsSVGGraphicElement.h"
 #include "nsSVGMatrix.h"
 #include "nsSVGAElement.h"
 #include "nsSVGUtils.h"
 #include "gfxMatrix.h"
+#include "SVGLengthList.h"
 
 // <a> elements can contain text. nsSVGGlyphFrames expect to have
 // a class derived from nsSVGTextContainerFrame as a parent. We
 // also need something that implements nsISVGGlyphFragmentNode to get
 // the text DOM to work.
 
+using namespace mozilla;
+
 typedef nsSVGTSpanFrame nsSVGAFrameBase;
 
 class nsSVGAFrame : public nsSVGAFrameBase
 {
   friend nsIFrame*
   NS_NewSVGAFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 protected:
   nsSVGAFrame(nsStyleContext* aContext) :
@@ -86,17 +89,21 @@ public:
     return MakeFrameName(NS_LITERAL_STRING("SVGA"), aResult);
   }
 #endif
   // nsISVGChildFrame interface:
   virtual void NotifySVGChanged(PRUint32 aFlags);
   
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM();
-  
+
+  // nsSVGTextContainerFrame methods:
+  virtual void GetXY(mozilla::SVGUserUnitList *aX, mozilla::SVGUserUnitList *aY);
+  virtual void GetDxDy(mozilla::SVGUserUnitList *aDx, mozilla::SVGUserUnitList *aDy);
+
 private:
   nsCOMPtr<nsIDOMSVGMatrix> mCanvasTM;
 };
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame*
@@ -178,8 +185,25 @@ nsSVGAFrame::GetCanvasTM()
 
     gfxMatrix tm = content->PrependLocalTransformTo(parent->GetCanvasTM());
 
     mCanvasTM = NS_NewSVGMatrix(tm);
   }
 
   return nsSVGUtils::ConvertSVGMatrixToThebes(mCanvasTM);
 }
+
+//----------------------------------------------------------------------
+// nsSVGTextContainerFrame methods:
+
+void
+nsSVGAFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
+{
+  aX->Clear();
+  aY->Clear();
+}
+
+void
+nsSVGAFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
+{
+  aDx->Clear();
+  aDy->Clear();
+}
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -35,31 +35,33 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsSVGTextFrame.h"
 #include "nsILookAndFeel.h"
 #include "nsTextFragment.h"
 #include "nsSVGUtils.h"
-#include "nsIDOMSVGLengthList.h"
+#include "SVGLengthList.h"
 #include "nsIDOMSVGLength.h"
 #include "nsIDOMSVGRect.h"
 #include "nsIDOMSVGPoint.h"
 #include "nsSVGGlyphFrame.h"
 #include "nsSVGTextPathFrame.h"
 #include "nsSVGPathElement.h"
 #include "nsSVGPoint.h"
 #include "nsSVGRect.h"
 #include "nsDOMError.h"
 #include "gfxContext.h"
 #include "gfxMatrix.h"
 #include "gfxPlatform.h"
 #include "gfxTextRunWordCache.h"
 
+using namespace mozilla;
+
 struct CharacterPosition {
   gfxPoint pos;
   gfxFloat angle;
   PRBool draw;
 };
   
 /**
  * This is a do-it-all helper class. It supports iterating through the
@@ -651,51 +653,26 @@ nsSVGGlyphFrame::GetCharacterData(nsAStr
     }
   }
   aCharacterData = characterData;
 
   return !characterData.IsEmpty();
 }
 
 static PRUint32
-GetNumberOfLengthListItems(nsIDOMSVGLengthList *aList)
-{
-  PRUint32 items = 0;
-  if (aList) {
-    aList->GetNumberOfItems(&items);
-  }
-  return items;
-}
-
-static PRUint32
 GetNumberOfNumberListItems(nsIDOMSVGNumberList *aList)
 {
   PRUint32 items = 0;
   if (aList) {
     aList->GetNumberOfItems(&items);
   }
   return items;
 }
 
 static float
-GetLengthListValue(nsIDOMSVGLengthList *aList, PRUint32 aIndex)
-{
-  if (!aList) {
-    return 0.0f;
-  }
-  nsCOMPtr<nsIDOMSVGLength> length;
-  nsresult rv = aList->GetItem(aIndex, getter_AddRefs(length));
-  float value = 0.0f;
-  if (NS_SUCCEEDED(rv)) {
-    length->GetValue(&value);
-  }
-  return value;
-}
-
-static float
 GetNumberListValue(nsIDOMSVGNumberList *aList, PRUint32 aIndex)
 {
   if (!aList) {
     return 0.0f;
   }
   nsCOMPtr<nsIDOMSVGNumber> number;
   nsresult rv = aList->GetItem(aIndex, getter_AddRefs(number));
   float value = 0.0f;
@@ -709,18 +686,18 @@ PRBool
 nsSVGGlyphFrame::GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPositions,
                                        float aMetricsScale)
 {
   PRUint32 strLength = mTextRun->GetLength();
   NS_ASSERTION(strLength > 0, "no text");
 
   const gfxFloat radPerDeg = M_PI / 180.0;
 
-  nsCOMPtr<nsIDOMSVGLengthList> dxList = GetDx();
-  nsCOMPtr<nsIDOMSVGLengthList> dyList = GetDy();
+  SVGUserUnitList dxList, dyList;
+  GetDxDy(&dxList, &dyList);
   nsCOMPtr<nsIDOMSVGNumberList> rotateList = GetRotate();
 
   PRBool rotateAllGlyphs = (GetNumberOfNumberListItems(rotateList) == 1);
   gfxFloat overallGlyphRotation =
     rotateAllGlyphs ? GetNumberListValue(rotateList, 0) * radPerDeg : 0.0;
 
   gfxPoint pos = mPosition;
 
@@ -741,18 +718,18 @@ nsSVGGlyphFrame::GetCharacterPositions(n
     CharacterPosition *cp = aCharacterPositions->Elements();
 
     gfxFloat length = data->GetLength();
 
     for (PRUint32 i = 0; i < strLength; i++) {
       gfxFloat halfAdvance =
         mTextRun->GetAdvanceWidth(i, 1, nsnull)*aMetricsScale / 2.0;
 
-      pos.x += GetLengthListValue(dxList, i) * pathScale;
-      pos.y += GetLengthListValue(dyList, i) * pathScale;
+      pos.x += i < dxList.Length() ? dxList[i] * pathScale : 0.0;
+      pos.y += i < dyList.Length() ? dyList[i] * pathScale : 0.0;
 
       // check that we're within the path boundaries
       cp[i].draw = (pos.x + halfAdvance >= 0.0 &&
                     pos.x + halfAdvance <= length);
 
       if (cp[i].draw) {
 
         // add y (normal)
@@ -765,26 +742,26 @@ nsSVGGlyphFrame::GetCharacterPositions(n
         cp[i].angle += rotateAllGlyphs ? overallGlyphRotation :
           GetNumberListValue(rotateList, i) * radPerDeg;
       }
       pos.x += 2 * halfAdvance;
     }
     return PR_TRUE;
   }
 
-  nsCOMPtr<nsIDOMSVGLengthList> xList = GetX();
-  nsCOMPtr<nsIDOMSVGLengthList> yList = GetY();
+  SVGUserUnitList xList, yList;
+  GetXY(&xList, &yList);
 
-  PRUint32 xListCount = GetNumberOfLengthListItems(xList);
-  PRUint32 yListCount = GetNumberOfLengthListItems(yList);
+  PRUint32 xListCount = xList.Length();
+  PRUint32 yListCount = yList.Length();
 
   if (xListCount <= 1 &&
       yListCount <= 1 &&
-      GetNumberOfLengthListItems(dxList) == 0 &&
-      GetNumberOfLengthListItems(dyList) == 0 &&
+      dxList.Length() == 0 &&
+      dyList.Length() == 0 &&
       GetNumberOfNumberListItems(rotateList) == 0) {
     // simple text without individual positioning
     return PR_TRUE;
   }
 
   if (!aCharacterPositions->SetLength(strLength))
     return PR_FALSE;
 
@@ -792,28 +769,28 @@ nsSVGGlyphFrame::GetCharacterPositions(n
 
   PRUint16 anchor = GetTextAnchor();
 
   for (PRUint32 i = 0; i < strLength; i++) {
     cp[i].draw = PR_TRUE;
 
     gfxFloat advance = mTextRun->GetAdvanceWidth(i, 1, nsnull)*aMetricsScale;
     if (xListCount > 1 && i < xListCount) {
-      pos.x = GetLengthListValue(xList, i);
+      pos.x = xList[i];
       // apply text-anchor to character
       if (anchor == NS_STYLE_TEXT_ANCHOR_MIDDLE)
         pos.x -= advance/2.0;
       else if (anchor == NS_STYLE_TEXT_ANCHOR_END)
         pos.x -= advance;
     }
     if (yListCount > 1 && i < yListCount) {
-      pos.y = GetLengthListValue(yList, i);
+      pos.y = yList[i];
     }
-    pos.x += GetLengthListValue(dxList, i);
-    pos.y += GetLengthListValue(dyList, i);
+    pos.x += i < dxList.Length() ? dxList[i] : 0.0;
+    pos.y += i < dyList.Length() ? dyList[i] : 0.0;
     cp[i].pos = pos;
     pos.x += advance;
     cp[i].angle = rotateAllGlyphs ? overallGlyphRotation :
       GetNumberListValue(rotateList, i) * radPerDeg;
   }
   return PR_TRUE;
 }
 
@@ -823,27 +800,28 @@ nsSVGGlyphFrame::GetSubStringAdvance(PRU
                                      float aMetricsScale)
 {
   if (aFragmentChars == 0)
     return 0.0f;
  
   gfxFloat advance =
     mTextRun->GetAdvanceWidth(aCharnum, aFragmentChars, nsnull) * aMetricsScale;
 
-  nsCOMPtr<nsIDOMSVGLengthList> dxlist = GetDx();
-  PRUint32 dxcount = GetNumberOfLengthListItems(dxlist);
+  SVGUserUnitList dxlist, notUsed;
+  GetDxDy(&dxlist, &notUsed);
+  PRUint32 dxcount = dxlist.Length();
   if (dxcount) {
     gfxFloat pathScale = 1.0;
     nsSVGTextPathFrame *textPath = FindTextPathParent();
     if (textPath)
       pathScale = textPath->GetPathScale();
     if (dxcount > aFragmentChars) 
       dxcount = aFragmentChars;
     for (PRUint32 i = aCharnum; i < dxcount; i++) {
-      advance += GetLengthListValue(dxlist, i) * pathScale;
+      advance += dxlist[i] * pathScale;
     }
   }
 
   return float(advance);
 }
 
 gfxFloat
 nsSVGGlyphFrame::GetBaselineOffset(float aMetricsScale)
@@ -1039,30 +1017,30 @@ nsSVGGlyphFrame::SetGlyphPosition(gfxPoi
   aPosition->x +=
     mTextRun->GetAdvanceWidth(0, strLength, nsnull) * metricsScale;
 
   gfxFloat pathScale = 1.0;
   nsSVGTextPathFrame *textPath = FindTextPathParent();
   if (textPath)
     pathScale = textPath->GetPathScale();
 
-  nsCOMPtr<nsIDOMSVGLengthList> dxList = GetDx();
-  nsCOMPtr<nsIDOMSVGLengthList> dyList = GetDy();
+  SVGUserUnitList dxList, dyList;
+  GetDxDy(&dxList, &dyList);
 
-  PRUint32 dxcount = GetNumberOfLengthListItems(dxList);
+  PRUint32 dxcount = dxList.Length();
   if (dxcount > strLength) 
     dxcount = strLength;
   for (PRUint32 i = 0; i < dxcount; i++) {
-    aPosition->x += GetLengthListValue(dxList, i) * pathScale;
+    aPosition->x += dxList[i] * pathScale;
   }
-  PRUint32 dycount = GetNumberOfLengthListItems(dyList);
+  PRUint32 dycount = dyList.Length();
   if (dycount > strLength) 
     dycount = strLength;
   for (PRUint32 i = 0; i < dycount; i++) {
-    aPosition->y += GetLengthListValue(dyList, i) * pathScale;
+    aPosition->y += dyList[i] * pathScale;
   }
 }
 
 NS_IMETHODIMP
 nsSVGGlyphFrame::GetStartPositionOfChar(PRUint32 charnum,
                                         nsIDOMSVGPoint **_retval)
 {
   *_retval = nsnull;
@@ -1168,54 +1146,32 @@ nsSVGGlyphFrame::IsStartOfChunk()
 {
   // this fragment is a chunk if it has a corresponding absolute
   // position adjustment in an ancestors' x or y array. (At the moment
   // we don't map the full arrays, but only the first elements.)
 
   return PR_FALSE;
 }
 
-NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
-nsSVGGlyphFrame::GetX()
-{
-  nsSVGTextContainerFrame *containerFrame;
-  containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
-  if (containerFrame)
-    return containerFrame->GetX();
-  return nsnull;
-}
-
-NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
-nsSVGGlyphFrame::GetY()
+NS_IMETHODIMP_(void)
+nsSVGGlyphFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
 {
   nsSVGTextContainerFrame *containerFrame;
   containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
   if (containerFrame)
-    return containerFrame->GetY();
-  return nsnull;
+    containerFrame->GetXY(aX, aY);
 }
 
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGGlyphFrame::GetDx()
+void
+nsSVGGlyphFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
 {
   nsSVGTextContainerFrame *containerFrame;
   containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
   if (containerFrame)
-    return containerFrame->GetDx();
-  return nsnull;
-}
-
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGGlyphFrame::GetDy()
-{
-  nsSVGTextContainerFrame *containerFrame;
-  containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
-  if (containerFrame)
-    return containerFrame->GetDy();
-  return nsnull;
+    containerFrame->GetDxDy(aDx, aDy);
 }
 
 already_AddRefed<nsIDOMSVGNumberList>
 nsSVGGlyphFrame::GetRotate()
 {
   nsSVGTextContainerFrame *containerFrame;
   containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
   if (containerFrame)
--- a/layout/svg/base/src/nsSVGGlyphFrame.h
+++ b/layout/svg/base/src/nsSVGGlyphFrame.h
@@ -144,18 +144,17 @@ public:
    * global transform even when NS_STATE_NONDISPLAY_CHILD
    */
   NS_IMETHOD_(float) GetAdvance(PRBool aForceGlobalTransform);
 
   NS_IMETHOD_(void) SetGlyphPosition(gfxPoint *aPosition, PRBool aForceGlobalTransform);
   NS_IMETHOD_(nsSVGTextPathFrame*) FindTextPathParent();
   NS_IMETHOD_(PRBool) IsStartOfChunk(); // == is new absolutely positioned chunk.
 
-  NS_IMETHOD_(already_AddRefed<nsIDOMSVGLengthList>) GetX();
-  NS_IMETHOD_(already_AddRefed<nsIDOMSVGLengthList>) GetY();
+  NS_IMETHOD_(void) GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY);
   NS_IMETHOD_(PRUint16) GetTextAnchor();
   NS_IMETHOD_(PRBool) IsAbsolutelyPositioned();
 
   // nsISVGGlyphFragmentNode interface:
   // These do not use the global transform if NS_STATE_NONDISPLAY_CHILD
   virtual PRUint32 GetNumberOfChars();
   virtual float GetComputedTextLength();
   virtual float GetSubStringLength(PRUint32 charnum, PRUint32 fragmentChars);
@@ -202,18 +201,17 @@ protected:
   PRBool GetGlobalTransform(gfxMatrix *aMatrix);
   void SetupGlobalTransform(gfxContext *aContext);
   nsresult GetHighlight(PRUint32 *charnum, PRUint32 *nchars,
                         nscolor *foreground, nscolor *background);
   float GetSubStringAdvance(PRUint32 charnum, PRUint32 fragmentChars,
                             float aMetricsScale);
   gfxFloat GetBaselineOffset(float aMetricsScale);
 
-  already_AddRefed<nsIDOMSVGLengthList> GetDx();
-  already_AddRefed<nsIDOMSVGLengthList> GetDy();
+  virtual void GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy);
   already_AddRefed<nsIDOMSVGNumberList> GetRotate();
 
   // Used to support GetBBoxContribution by making GetConvasTM use this as the
   // parent transform instead of the real CanvasTM.
   nsCOMPtr<nsIDOMSVGMatrix> mOverrideCanvasTM;
 
   // Owning pointer, must call gfxTextRunWordCache::RemoveTextRun before deleting
   gfxTextRun *mTextRun;
--- a/layout/svg/base/src/nsSVGTextContainerFrame.cpp
+++ b/layout/svg/base/src/nsSVGTextContainerFrame.cpp
@@ -38,16 +38,19 @@
 #include "nsSVGTextFrame.h"
 #include "nsSVGUtils.h"
 #include "nsSVGOuterSVGFrame.h"
 #include "nsIDOMSVGTextElement.h"
 #include "nsIDOMSVGAnimatedLengthList.h"
 #include "nsIDOMSVGAnimatedNumberList.h"
 #include "nsISVGGlyphFragmentLeaf.h"
 #include "nsDOMError.h"
+#include "SVGLengthList.h"
+
+using namespace mozilla;
 
 //----------------------------------------------------------------------
 // nsQueryFrame methods
 
 NS_QUERYFRAME_HEAD(nsSVGTextContainerFrame)
   NS_QUERYFRAME_ENTRY(nsSVGTextContainerFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsSVGDisplayContainerFrame)
 
@@ -56,78 +59,31 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSVGTextCont
 void
 nsSVGTextContainerFrame::NotifyGlyphMetricsChange()
 {
   nsSVGTextFrame *textFrame = GetTextFrame();
   if (textFrame)
     textFrame->NotifyGlyphMetricsChange();
 }
 
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGTextContainerFrame::GetX()
+void
+nsSVGTextContainerFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
 {
-  nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
-    do_QueryInterface(mContent);
-
-  if (!tpElement)
-    return nsnull;
-
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
-  tpElement->GetX(getter_AddRefs(animLengthList));
-  nsIDOMSVGLengthList *retval;
-  animLengthList->GetAnimVal(&retval);
-  return retval;
+  static_cast<nsSVGElement*>(mContent)->
+    GetAnimatedLengthListValues(aX, aY, nsnull);
 }
 
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGTextContainerFrame::GetY()
-{
-  nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
-    do_QueryInterface(mContent);
-
-  if (!tpElement)
-    return nsnull;
-
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
-  tpElement->GetY(getter_AddRefs(animLengthList));
-  nsIDOMSVGLengthList *retval;
-  animLengthList->GetAnimVal(&retval);
-  return retval;
-}
-
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGTextContainerFrame::GetDx()
+void
+nsSVGTextContainerFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
 {
-  nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
-    do_QueryInterface(mContent);
-
-  if (!tpElement)
-    return nsnull;
-
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
-  tpElement->GetDx(getter_AddRefs(animLengthList));
-  nsIDOMSVGLengthList *retval;
-  animLengthList->GetAnimVal(&retval);
-  return retval;
-}
-
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGTextContainerFrame::GetDy()
-{
-  nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
-    do_QueryInterface(mContent);
-
-  if (!tpElement)
-    return nsnull;
-
-  nsCOMPtr<nsIDOMSVGAnimatedLengthList> animLengthList;
-  tpElement->GetDy(getter_AddRefs(animLengthList));
-  nsIDOMSVGLengthList *retval;
-  animLengthList->GetAnimVal(&retval);
-  return retval;
+  // SVGUserUnitList is lazy, so there's little overhead it getting the x
+  // and y lists even though we ignore them.
+  SVGUserUnitList xLengthList, yLengthList;
+  static_cast<nsSVGElement*>(mContent)->
+    GetAnimatedLengthListValues(&xLengthList, &yLengthList, aDx, aDy, nsnull);
 }
 
 already_AddRefed<nsIDOMSVGNumberList>
 nsSVGTextContainerFrame::GetRotate()
 {
   nsCOMPtr<nsIDOMSVGTextPositioningElement> tpElement =
     do_QueryInterface(mContent);
 
--- a/layout/svg/base/src/nsSVGTextContainerFrame.h
+++ b/layout/svg/base/src/nsSVGTextContainerFrame.h
@@ -33,35 +33,34 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef NS_SVGTEXTCONTAINERFRAME_H
 #define NS_SVGTEXTCONTAINERFRAME_H
 
 #include "nsSVGContainerFrame.h"
-#include "nsIDOMSVGLengthList.h"
 #include "nsIDOMSVGNumberList.h"
 
 class nsISVGGlyphFragmentNode;
 class nsISVGGlyphFragmentLeaf;
-
 class nsSVGTextFrame;
+namespace mozilla {
+class SVGUserUnitList;
+}
 
 class nsSVGTextContainerFrame : public nsSVGDisplayContainerFrame
 {
 public:
   nsSVGTextContainerFrame(nsStyleContext* aContext) :
     nsSVGDisplayContainerFrame(aContext) {}
 
   void NotifyGlyphMetricsChange();
-  virtual already_AddRefed<nsIDOMSVGLengthList> GetX();
-  virtual already_AddRefed<nsIDOMSVGLengthList> GetY();
-  virtual already_AddRefed<nsIDOMSVGLengthList> GetDx();
-  virtual already_AddRefed<nsIDOMSVGLengthList> GetDy();
+  virtual void GetXY(mozilla::SVGUserUnitList *aX, mozilla::SVGUserUnitList *aY);
+  virtual void GetDxDy(mozilla::SVGUserUnitList *aDx, mozilla::SVGUserUnitList *aDy);
   virtual already_AddRefed<nsIDOMSVGNumberList> GetRotate();
   
 public:
   NS_DECL_QUERYFRAME_TARGET(nsSVGTextContainerFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   // nsIFrame
--- a/layout/svg/base/src/nsSVGTextFrame.cpp
+++ b/layout/svg/base/src/nsSVGTextFrame.cpp
@@ -34,31 +34,33 @@
  * 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 "nsIDOMSVGTextElement.h"
 #include "nsSVGTextFrame.h"
 #include "nsWeakReference.h"
-#include "nsIDOMSVGLengthList.h"
+#include "SVGLengthList.h"
 #include "nsIDOMSVGLength.h"
 #include "nsIDOMSVGAnimatedNumber.h"
 #include "nsISVGGlyphFragmentNode.h"
 #include "nsISVGGlyphFragmentLeaf.h"
 #include "nsSVGOuterSVGFrame.h"
 #include "nsIDOMSVGRect.h"
 #include "nsSVGRect.h"
 #include "nsSVGMatrix.h"
 #include "nsGkAtoms.h"
 #include "nsSVGTextPathFrame.h"
 #include "nsSVGPathElement.h"
 #include "nsSVGUtils.h"
 #include "nsSVGGraphicElement.h"
 
+using namespace mozilla;
+
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame*
 NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSVGTextFrame(aContext);
 }
@@ -286,33 +288,16 @@ nsSVGTextFrame::GetCanvasTM()
 
 void
 nsSVGTextFrame::NotifyGlyphMetricsChange()
 {
   mPositioningDirty = PR_TRUE;
   UpdateGlyphPositioning(PR_FALSE);
 }
 
-static void
-GetSingleValue(nsIDOMSVGLengthList *list, gfxFloat *val)
-{
-  if (!list)
-    return;
-
-  PRUint32 count = 0;
-  list->GetNumberOfItems(&count);
-  if (count) {
-    nsCOMPtr<nsIDOMSVGLength> length;
-    list->GetItem(0, getter_AddRefs(length));
-    float value;
-    length->GetValue(&value);
-    *val = value;
-  }
-}
-
 void
 nsSVGTextFrame::UpdateGlyphPositioning(PRBool aForceGlobalTransform)
 {
   if (mMetricsState == suspended || !mPositioningDirty)
     return;
 
   SetWhitespaceHandling();
 
@@ -325,35 +310,26 @@ nsSVGTextFrame::UpdateGlyphPositioning(P
 
   firstFragment = node->GetFirstGlyphFragment();
   if (!firstFragment) {
     return;
   }
 
   gfxPoint ctp(0.0, 0.0);
 
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> list = GetX();
-    GetSingleValue(list, &ctp.x);
-  }
-  {
-    nsCOMPtr<nsIDOMSVGLengthList> list = GetY();
-    GetSingleValue(list, &ctp.y);
-  }
+  SVGUserUnitList xLengthList, yLengthList;
+  GetXY(&xLengthList, &yLengthList);
+  if (xLengthList.Length() > 0) ctp.x = xLengthList[0];
+  if (yLengthList.Length() > 0) ctp.y = yLengthList[0];
 
   // loop over chunks
   while (firstFragment) {
-    {
-      nsCOMPtr<nsIDOMSVGLengthList> list = firstFragment->GetX();
-      GetSingleValue(list, &ctp.x);
-    }
-    {
-      nsCOMPtr<nsIDOMSVGLengthList> list = firstFragment->GetY();
-      GetSingleValue(list, &ctp.y);
-    }
+    firstFragment->GetXY(&xLengthList, &yLengthList);
+    if (xLengthList.Length() > 0) ctp.x = xLengthList[0];
+    if (yLengthList.Length() > 0) ctp.y = yLengthList[0];
 
     // check for startOffset on textPath
     nsSVGTextPathFrame *textPath = firstFragment->FindTextPathParent();
     if (textPath) {
       if (!textPath->GetPathFrame()) {
         // invalid text path, give up
         return;
       }
--- a/layout/svg/base/src/nsSVGTextPathFrame.cpp
+++ b/layout/svg/base/src/nsSVGTextPathFrame.cpp
@@ -38,16 +38,18 @@
 #include "nsIDOMSVGTextPathElement.h"
 #include "nsSVGLength2.h"
 #include "nsIDOMSVGURIReference.h"
 #include "nsSVGEffects.h"
 #include "nsContentUtils.h"
 #include "nsSVGPathElement.h"
 #include "nsSVGTextPathElement.h"
 
+using namespace mozilla;
+
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame*
 NS_NewSVGTextPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSVGTextPathFrame(aContext);
 }
@@ -78,39 +80,30 @@ nsSVGTextPathFrame::Init(nsIContent* aCo
 #endif /* DEBUG */
 
 nsIAtom *
 nsSVGTextPathFrame::GetType() const
 {
   return nsGkAtoms::svgTextPathFrame;
 }
 
-
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGTextPathFrame::GetX()
+void
+nsSVGTextPathFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
 {
-  return nsnull;
+  // 'x' and 'y' don't apply to 'textPath'
+  aX->Clear();
+  aY->Clear();
 }
 
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGTextPathFrame::GetY()
+void
+nsSVGTextPathFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
 {
-  return nsnull;
-}
-
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGTextPathFrame::GetDx()
-{
-  return nsnull;
-}
-
-already_AddRefed<nsIDOMSVGLengthList>
-nsSVGTextPathFrame::GetDy()
-{
-  return nsnull;
+  // 'dx' and 'dy' don't apply to 'textPath'
+  aDx->Clear();
+  aDy->Clear();
 }
 
 already_AddRefed<nsIDOMSVGNumberList>
 nsSVGTextPathFrame::GetRotate()
 {
   return nsnull;
 }
 
--- a/layout/svg/base/src/nsSVGTextPathFrame.h
+++ b/layout/svg/base/src/nsSVGTextPathFrame.h
@@ -33,17 +33,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef NSSVGTEXTPATHFRAME_H
 #define NSSVGTEXTPATHFRAME_H
 
 #include "nsSVGTSpanFrame.h"
-#include "nsSVGLengthList.h"
+#include "SVGLengthList.h"
 #include "nsSVGNumberList.h"
 
 typedef nsSVGTSpanFrame nsSVGTextPathFrameBase;
 
 class nsSVGTextPathFrame : public nsSVGTextPathFrameBase
 {
   friend nsIFrame*
   NS_NewSVGTextPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
@@ -80,19 +80,17 @@ public:
   // nsSVGTextPathFrame methods:
   already_AddRefed<gfxFlattenedPath> GetFlattenedPath();
   nsIFrame *GetPathFrame();
 
   gfxFloat GetStartOffset();
   gfxFloat GetPathScale();
 protected:
 
-  virtual already_AddRefed<nsIDOMSVGLengthList> GetX();
-  virtual already_AddRefed<nsIDOMSVGLengthList> GetY();
-  virtual already_AddRefed<nsIDOMSVGLengthList> GetDx();
-  virtual already_AddRefed<nsIDOMSVGLengthList> GetDy();
+  virtual void GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY);
+  virtual void GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy);
   virtual already_AddRefed<nsIDOMSVGNumberList> GetRotate();
 
 private:
   already_AddRefed<gfxFlattenedPath> GetFlattenedPath(nsIFrame *path);
 };
 
 #endif
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -259,16 +259,17 @@ nsSVGUtils::GetFontSize(Element *aElemen
 {
   if (!aElement)
     return 1.0f;
 
   nsRefPtr<nsStyleContext> styleContext = 
     nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
                                                          nsnull, nsnull);
   if (!styleContext) {
+    // ReportToConsole
     NS_WARNING("Couldn't get style context for content in GetFontStyle");
     return 1.0f;
   }
 
   return GetFontSize(styleContext);
 }
 
 float
@@ -296,16 +297,17 @@ nsSVGUtils::GetFontXHeight(Element *aEle
 {
   if (!aElement)
     return 1.0f;
 
   nsRefPtr<nsStyleContext> styleContext = 
     nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
                                                          nsnull, nsnull);
   if (!styleContext) {
+    // ReportToConsole
     NS_WARNING("Couldn't get style context for content in GetFontStyle");
     return 1.0f;
   }
 
   return GetFontXHeight(styleContext);
 }
   
 float
@@ -323,16 +325,17 @@ nsSVGUtils::GetFontXHeight(nsStyleContex
   nsPresContext *presContext = aStyleContext->PresContext();
   NS_ABORT_IF_FALSE(presContext, "NULL pres context in GetFontXHeight");
 
   nsCOMPtr<nsIFontMetrics> fontMetrics;
   nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext,
                                                getter_AddRefs(fontMetrics));
 
   if (!fontMetrics) {
+    // ReportToConsole
     NS_WARNING("no FontMetrics in GetFontXHeight()");
     return 1.0f;
   }
 
   nscoord xHeight;
   fontMetrics->GetXHeight(xHeight);
   return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) /
          presContext->TextZoom();
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -208,23 +208,31 @@ class nsSVGUtils
 {
 public:
   /*
    * Get the parent element of an nsIContent
    */
   static mozilla::dom::Element *GetParentElement(nsIContent *aContent);
 
   /*
-   * Get a font-size (em) of an nsIContent
+   * Get the number of CSS px (user units) per em (i.e. the em-height in user
+   * units) for an nsIContent
+   *
+   * XXX document the conditions under which these may fail, and what they
+   * return in those cases.
    */
   static float GetFontSize(mozilla::dom::Element *aElement);
   static float GetFontSize(nsIFrame *aFrame);
   static float GetFontSize(nsStyleContext *aStyleContext);
   /*
-   * Get an x-height of of an nsIContent
+   * Get the number of CSS px (user units) per ex (i.e. the x-height in user
+   * units) for an nsIContent
+   *
+   * XXX document the conditions under which these may fail, and what they
+   * return in those cases.
    */
   static float GetFontXHeight(mozilla::dom::Element *aElement);
   static float GetFontXHeight(nsIFrame *aFrame);
   static float GetFontXHeight(nsStyleContext *aStyleContext);
 
   /*
    * Converts image data from premultipled to unpremultiplied alpha
    */