Final merge of bug 596451. a=blocking2.0 - Asynchronous layer-based painting on Windows. Various pieces r=josh/romaxa/karlt/jmathies/roc.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Wed, 10 Nov 2010 10:29:09 -0500
changeset 57236 bdbef533364f1cdecbbefe16d8d8d9f5e0fd8100
parent 57235 30b383d73524f23ce5ea32b176c580077e21c0e9 (current diff)
parent 57201 212a391d3b79caa2e953bb70e4a00460089e844e (diff)
child 57237 68dcdaf45a0f759ad51f8978a1c8d578c6c695df
push id16846
push userbsmedberg@mozilla.com
push dateWed, 10 Nov 2010 15:29:47 +0000
treeherdermozilla-central@bdbef533364f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblocking2.0, josh, romaxa, karlt, jmathies, roc
bugs596451, 583109, 606285
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Final merge of bug 596451. a=blocking2.0 - Asynchronous layer-based painting on Windows. Various pieces r=josh/romaxa/karlt/jmathies/roc. This merge includes the fix for bug 583109 - Add visibility notifications for plugins, so that asynchronous painting can be suspended when plugins are not visible. This collaterally fixes bug 606285, hulu homepage doesn't paint with async layers. This changeset only implements asynchronous painting for out-of-process plugins. In-process painting will be implemented as a followup. Note that it is still possible to disable asynchronous layers on Windows by setting the preference mozilla.plugins.use_layers to false, but my intention is to remove this preference and sync-painting codepaths soon in a followup.
content/svg/content/src/nsSVGPathSeg.cpp
content/svg/content/src/nsSVGPathSeg.h
content/svg/content/src/nsSVGPathSegList.cpp
content/svg/content/src/nsSVGPathSegList.h
dom/plugins/PluginInstanceChild.cpp
dom/plugins/PluginModuleParent.cpp
dom/plugins/PluginModuleParent.h
ipc/ipdl/ipdl/lower.py
modules/plugin/test/crashtests/crashtests.list
modules/plugin/test/testplugin/nptest.cpp
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -2725,64 +2725,77 @@ nsAccessible::InvalidateChildren()
   mEmbeddedObjCollector = nsnull;
   mChildren.Clear();
   mChildrenFlags = eChildrenUninitialized;
 }
 
 PRBool
 nsAccessible::AppendChild(nsAccessible* aChild)
 {
+  if (!aChild)
+    return PR_FALSE;
+
   if (!mChildren.AppendElement(aChild))
     return PR_FALSE;
 
   if (!nsAccUtils::IsEmbeddedObject(aChild))
     mChildrenFlags = eMixedChildren;
 
   aChild->BindToParent(this, mChildren.Length() - 1);
   return PR_TRUE;
 }
 
 PRBool
 nsAccessible::InsertChildAt(PRUint32 aIndex, nsAccessible* aChild)
 {
+  if (!aChild)
+    return PR_FALSE;
+
   if (!mChildren.InsertElementAt(aIndex, aChild))
     return PR_FALSE;
 
-  for (PRUint32 idx = aIndex + 1; idx < mChildren.Length(); idx++)
-    mChildren[idx]->mIndexInParent++;
+  for (PRUint32 idx = aIndex + 1; idx < mChildren.Length(); idx++) {
+    NS_ASSERTION(mChildren[idx]->mIndexInParent == idx - 1, "Accessible child index doesn't match");
+    mChildren[idx]->mIndexInParent = idx;
+  }
 
   if (nsAccUtils::IsText(aChild))
     mChildrenFlags = eMixedChildren;
 
   mEmbeddedObjCollector = nsnull;
 
   aChild->BindToParent(this, aIndex);
   return PR_TRUE;
 }
 
 PRBool
 nsAccessible::RemoveChild(nsAccessible* aChild)
 {
-  if (aChild->mParent != this || aChild->mIndexInParent == -1)
+  if (!aChild)
     return PR_FALSE;
 
-  if (aChild->mIndexInParent >= mChildren.Length() ||
-      mChildren[aChild->mIndexInParent] != aChild) {
+  PRInt32 index = aChild->mIndexInParent;
+  if (aChild->mParent != this || index == -1)
+    return PR_FALSE;
+
+  if (index >= mChildren.Length() || mChildren[index] != aChild) {
     NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
     aChild->UnbindFromParent();
     return PR_FALSE;
   }
 
-  for (PRUint32 idx = aChild->mIndexInParent + 1; idx < mChildren.Length(); idx++)
-    mChildren[idx]->mIndexInParent--;
-
-  mChildren.RemoveElementAt(aChild->mIndexInParent);
-  mEmbeddedObjCollector = nsnull;
+  for (PRUint32 idx = index + 1; idx < mChildren.Length(); idx++) {
+    NS_ASSERTION(mChildren[idx]->mIndexInParent == idx, "Accessible child index doesn't match");
+    mChildren[idx]->mIndexInParent = idx - 1;
+  }
 
   aChild->UnbindFromParent();
+  mChildren.RemoveElementAt(index);
+  mEmbeddedObjCollector = nsnull;
+
   return PR_TRUE;
 }
 
 nsAccessible*
 nsAccessible::GetParent()
 {
   if (mParent)
     return mParent;
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -674,18 +674,20 @@ nsDocAccessible::Shutdown()
   if (mParent) {
     nsDocAccessible* parentDocument = mParent->GetDocAccessible();
     if (parentDocument)
       parentDocument->RemoveChildDocument(this);
 
     mParent->RemoveChild(this);
   }
 
-  PRUint32 childDocCount = mChildDocuments.Length();
-  for (PRUint32 idx = 0; idx < childDocCount; idx++)
+  // Walk the array backwards because child documents remove themselves from the
+  // array as they are shutdown.
+  PRInt32 childDocCount = mChildDocuments.Length();
+  for (PRInt32 idx = childDocCount - 1; idx >= 0; idx--)
     mChildDocuments[idx]->Shutdown();
 
   mChildDocuments.Clear();
 
   mWeakShell = nsnull;  // Avoid reentrancy
 
   mNodeToAccessibleMap.Clear();
   ClearCache(mAccessibleCache);
--- a/accessible/src/base/nsOuterDocAccessible.cpp
+++ b/accessible/src/base/nsOuterDocAccessible.cpp
@@ -191,18 +191,23 @@ nsOuterDocAccessible::InvalidateChildren
   // accessible is created and appended as a child when it's requested.
 
   mChildrenFlags = eChildrenUninitialized;
 }
 
 PRBool
 nsOuterDocAccessible::AppendChild(nsAccessible *aAccessible)
 {
-  NS_ASSERTION(!mChildren.Length(),
-               "Previous child document of outerdoc accessible wasn't removed!");
+  // We keep showing the old document for a bit after creating the new one,
+  // and while building the new DOM and frame tree. That's done on purpose
+  // to avoid weird flashes of default background color.
+  // The old viewer will be destroyed after the new one is created.
+  // For a11y, it should be safe to shut down the old document now.
+  if (mChildren.Length())
+    mChildren[0]->Shutdown();
 
   if (!nsAccessible::AppendChild(aAccessible))
     return PR_FALSE;
 
   NS_LOG_ACCDOCCREATE("append document to outerdoc",
                       aAccessible->GetDocumentNode())
   NS_LOG_ACCDOCCREATE_ACCADDRESS("outerdoc", this)
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -46,17 +46,17 @@ tabbrowser {
 
 .tab-throbber,
 .tab-label,
 .tab-icon-image,
 .tab-close-button {
   -moz-transition: opacity .25s;
 }
 
-.tabbrowser-tab[pinned] {
+.tabbrowser-tabs:not([pinnedonly]) > .tabbrowser-tab[pinned] {
   position: fixed;
   display: block; /* position:fixed already does this (bug 579776), but let's be explicit */
 }
 
 #alltabs-popup {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup");
 }
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -827,41 +827,58 @@
                                           true, false);
             }
 
             // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
             // that might rely upon the other changes suppressed.
             // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
             if (!this._previewMode) {
               // We've selected the new tab, so go ahead and notify listeners.
-              var event = document.createEvent("Events");
+              let event = document.createEvent("Events");
               event.initEvent("TabSelect", true, false);
               this.mCurrentTab.dispatchEvent(event);
 
               this._tabAttrModified(oldTab);
               this._tabAttrModified(this.mCurrentTab);
 
-              // Change focus to the new browser unless the findbar is focused.
-              if (!gFindBarInitialized ||
-                  gFindBar.hidden ||
-                  gFindBar.getElement("findbar-textbox").getAttribute("focused") != "true") {
-
-                var fm = Components.classes["@mozilla.org/focus-manager;1"].
-                           getService(Components.interfaces.nsIFocusManager);
-                var newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});
+              // Adjust focus
+              do {
+                // Focus the location bar if it was previously focused for that tab.
+                // In full screen mode, only bother making the location bar visible
+                // if the tab is a blank one.
+                oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
+                if (newBrowser._urlbarFocused && gURLBar) {
+                  if (!window.fullScreen) {
+                    gURLBar.focus();
+                    break;
+                  } else if (isTabEmpty(this.mCurrentTab)) {
+                    focusAndSelectUrlBar();
+                    break;
+                  }
+                }
+
+                // If the find bar is focused, keep it focused.
+                if (gFindBarInitialized &&
+                    !gFindBar.hidden &&
+                    gFindBar.getElement("findbar-textbox").getAttribute("focused") == "true")
+                  break;
+
+                // Otherwise, focus the content area.
+                let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+                let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});
 
                 // for anchors, use FLAG_SHOWRING so that it is clear what link was
                 // last clicked when switching back to that tab
-                var focusFlags = fm.FLAG_NOSCROLL;
+                let focusFlags = fm.FLAG_NOSCROLL;
                 if (newFocusedElement &&
                     (newFocusedElement instanceof HTMLAnchorElement ||
                      newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
                   focusFlags |= fm.FLAG_SHOWRING;
                 fm.setFocus(newBrowser, focusFlags);
-              }
+              } while (false);
             }
           ]]>
         </body>
       </method>
 
       <method name="_tabAttrModified">
         <parameter name="aTab"/>
         <body><![CDATA[
@@ -1897,25 +1914,21 @@
       </property>
 
       <!-- Moves a tab to a new browser window, unless it's already the only tab
            in the current window, in which case this will do nothing. -->
       <method name="replaceTabWithWindow">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
-            if (this.visibleTabs.length == 1)
+            if (this.tabs.length == 1)
               return null;
 
             // tell a new window to take the "dropped" tab
-            return Services.ww.openWindow(window,
-                                          getBrowserURL(),
-                                          null,
-                                          "chrome,dialog=no,all",
-                                          aTab);
+            return window.openDialog(getBrowserURL(), "_blank", "dialog=no,all", aTab);
           ]]>
         </body>
       </method>
 
       <method name="moveTabTo">
         <parameter name="aTab"/>
         <parameter name="aIndex"/>
         <body>
@@ -2703,21 +2716,28 @@
               tabStrip.scrollByPixels(-1);
           } catch (e) {}
         ]]></body>
       </method>
 
       <method name="_positionPinnedTabs">
         <body><![CDATA[
           var width = 0;
-          var scrollButtonWidth = this.getAttribute("overflow") != "true" ? 0 :
+          var pinnedOnly = (this.tabbrowser._numPinnedTabs == this.tabbrowser.visibleTabs.length);
+
+          if (pinnedOnly)
+            this.tabbrowser.tabContainer.setAttribute("pinnedonly", "true");
+          else
+            this.tabbrowser.tabContainer.removeAttribute("pinnedonly");
+
+          var scrollButtonWidth = (this.getAttribute("overflow") != "true" || pinnedOnly) ? 0 :
                                   this.mTabstrip._scrollButtonDown.scrollWidth;
           for (var i = this.tabbrowser._numPinnedTabs - 1; i >= 0; i--) {
             let tab = this.childNodes[i];
-            width += tab.scrollWidth;
+            width += pinnedOnly ? 0 : tab.scrollWidth;
             tab.style.MozMarginStart = - (width + scrollButtonWidth) + "px";
           }
           this.style.MozMarginStart = width + "px";
           this.mTabstrip.ensureElementIsVisible(this.selectedItem, false);
         ]]></body>
       </method>
 
       <method name="handleEvent">
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -1015,74 +1015,72 @@ GroupItem.prototype = Utils.extend(new I
           opacity: .2,
           top: childBB.top + childBB.height - parentBB.top + padding,
           left: parentBB.width/2 - this.$expander.width()/2
         });
   },
 
   // ----------
   // Function: shouldStack
-  // Returns true if the groupItem, given "count", should stack (instead of grid).
+  // Returns true if the groupItem should stack (instead of grid).
   shouldStack: function GroupItem_shouldStack(count) {
     if (count <= 1)
       return false;
 
     var bb = this.getContentBounds();
     var options = {
-      pretend: true,
-      count: count
+      return: 'widthAndColumns',
+      count: count || this._children.length
     };
+    let {childWidth, columns} = Items.arrange(null, bb, options);
 
-    var rects = Items.arrange(null, bb, options);
-    return (rects[0].width < 55);
+    let shouldStack = childWidth < TabItems.minTabWidth * 1.35;
+    this._columns = shouldStack ? null : columns;
+
+    return shouldStack;
   },
 
   // ----------
   // Function: arrange
   // Lays out all of the children.
   //
   // Parameters:
   //   options - passed to <Items.arrange> or <_stackArrange>
   arrange: function GroupItem_arrange(options) {
     if (this.expanded) {
       this.topChild = null;
       var box = new Rect(this.expanded.bounds);
       box.inset(8, 8);
       Items.arrange(this._children, box, Utils.extend({}, options, {z: 99999}));
     } else {
       var bb = this.getContentBounds();
-      var count = this._children.length;
-      if (!this.shouldStack(count)) {
+      if (!this.shouldStack()) {
         if (!options)
           options = {};
 
-        var animate;
-        if (typeof options.animate == 'undefined')
-          animate = true;
-        else
-          animate = options.animate;
-
         this._children.forEach(function(child) {
             child.removeClass("stacked")
         });
 
         this.topChild = null;
 
-        var arrangeOptions = Utils.copy(options);
-        Utils.extend(arrangeOptions, {
-          pretend: true,
-          count: count
-        });
-
-        if (!count) {
+        if (!this._children.length) {
           this.xDensity = 0;
           this.yDensity = 0;
           return;
         }
 
+        var arrangeOptions = Utils.copy(options);
+        Utils.extend(arrangeOptions, {
+          columns: this._columns
+        });
+
+        // Items.arrange will rearrange the children, but also return an array
+        // of the Rect's used.
+
         var rects = Items.arrange(this._children, bb, arrangeOptions);
 
         // yDensity = (the distance of the bottom of the last tab to the top of the content area)
         // / (the total available content height)
         this.yDensity = (rects[rects.length - 1].bottom - bb.top) / (bb.height);
 
         // xDensity = (the distance from the left of the content area to the right of the rightmost
         // tab) / (the total available content width)
@@ -1093,25 +1091,16 @@ GroupItem.prototype = Utils.extend(new I
         for each (var rect in rects) {
           if (rect.right > rightMostRight)
             rightMostRight = rect.right;
           else
             break;
         }
         this.xDensity = (rightMostRight - bb.left) / (bb.width);
 
-        this._children.forEach(function(child, index) {
-          if (!child.locked.bounds) {
-            child.setBounds(rects[index], !animate);
-            child.setRotation(0);
-            if (options.z)
-              child.setZ(options.z);
-          }
-        });
-
         this._isStacked = false;
       } else
         this._stackArrange(bb, options);
     }
 
     if (this._isStacked && !this.expanded) this.showExpandControl();
     else this.hideExpandControl();
   },
--- a/browser/base/content/tabview/items.js
+++ b/browser/base/content/tabview/items.js
@@ -596,17 +596,17 @@ Item.prototype = {
       var startSent;
       var startEvent;
       var droppables;
       var dropTarget;
 
       // ___ mousemove
       var handleMouseMove = function(e) {
         // positioning
-        var mouse = new Point(e.pageX, e.pageY);		
+        var mouse = new Point(e.pageX, e.pageY);
         if (!startSent) {
           if(Math.abs(mouse.x - startMouse.x) > self.dragOptions.minDragDistance ||
              Math.abs(mouse.y - startMouse.y) > self.dragOptions.minDragDistance) {
             if (typeof self.dragOptions.start == "function")
               self.dragOptions.start.apply(self,
                   [startEvent, {position: {left: startPos.x, top: startPos.y}}]);
             startSent = true;
           }
@@ -887,49 +887,51 @@ let Items = {
   },
 
   // ----------
   // Function: arrange
   // Arranges the given items in a grid within the given bounds,
   // maximizing item size but maintaining standard tab aspect ratio for each
   //
   // Parameters:
-  //   items - an array of <Item>s. Can be null if the pretend and count options are set.
+  //   items - an array of <Item>s. Can be null, in which case we won't
+  //     actually move anything.
   //   bounds - a <Rect> defining the space to arrange within
   //   options - an object with various properites (see below)
   //
   // Possible "options" properties:
   //   animate - whether to animate; default: true.
   //   z - the z index to set all the items; default: don't change z.
-  //   pretend - whether to collect and return the rectangle rather than moving the items; default: false
-  //   count - overrides the item count for layout purposes; default: the actual item count
+  //   return - if set to 'widthAndColumns', it'll return an object with the
+  //     width of children and the columns.
+  //   count - overrides the item count for layout purposes;
+  //     default: the actual item count
   //   padding - pixels between each item
+  //   columns - (int) a preset number of columns to use
   //
   // Returns:
-  //   the list of rectangles if the pretend option is set; otherwise null
+  //   an object with the width value of the child items and the number of columns, 
+  //   if the return option is set to 'widthAndColumns'; otherwise the list of <Rect>s
   arrange: function Items_arrange(items, bounds, options) {
-    var animate;
-    if (!options || typeof options.animate == 'undefined')
-      animate = true;
-    else
-      animate = options.animate;
-
     if (typeof options == 'undefined')
       options = {};
 
-    var rects = null;
-    if (options.pretend)
-      rects = [];
+    var animate = true;
+    if (typeof options.animate != 'undefined')
+      animate = options.animate;
+    var immediately = !animate;
+
+    var rects = [];
 
     var tabAspect = TabItems.tabHeight / TabItems.tabWidth;
     var count = options.count || (items ? items.length : 0);
     if (!count)
       return rects;
 
-    var columns = 1;
+    var columns = options.columns || 1;
     // We'll assume for the time being that all the items have the same styling
     // and that the margin is the same width around.
     var itemMargin = items && items.length ?
                        parseInt(iQ(items[0].container).css('margin-left')) : 0;
     var padding = itemMargin * 2;
     var yScale = 1.1; // to allow for titles
     var rows;
     var tabWidth;
@@ -949,45 +951,41 @@ let Items = {
       columns++;
       figure();
     }
 
     if (rows == 1) {
       tabWidth = Math.min(tabWidth, (bounds.height - 2 * itemMargin) / tabAspect);
       tabHeight = tabWidth * tabAspect;
     }
+    
+    if (options.return == 'widthAndColumns')
+      return {childWidth: tabWidth, columns: columns};
 
     var box = new Rect(bounds.left, bounds.top, tabWidth, tabHeight);
-    var row = 0;
     var column = 0;
-    var immediately;
 
-    var a;
-    for (a = 0; a < count; a++) {
-      immediately = !animate;
-
-      if (rects)
-        rects.push(new Rect(box));
-      else if (items && a < items.length) {
-        var item = items[a];
+    for (let a = 0; a < count; a++) {
+      rects.push(new Rect(box));
+      if (items && a < items.length) {
+        let item = items[a];
         if (!item.locked.bounds) {
           item.setBounds(box, immediately);
           item.setRotation(0);
           if (options.z)
             item.setZ(options.z);
         }
       }
 
       box.left += box.width + padding;
       column++;
       if (column == columns) {
         box.left = bounds.left;
         box.top += (box.height * yScale) + padding;
         column = 0;
-        row++;
       }
     }
 
     return rects;
   },
 
   // ----------
   // Function: unsquish
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -140,30 +140,32 @@ endif
                  browser_bug555224.js \
                  browser_bug555767.js \
                  browser_bug556061.js \
                  browser_bug559991.js \
                  browser_bug561623.js \
                  browser_bug561636.js \
                  browser_bug562649.js \
                  browser_bug563588.js \
+                 browser_bug565575.js \
                  browser_bug575561.js \
                  browser_bug577121.js \
                  browser_bug579872.js \
                  browser_bug580956.js \
                  browser_bug581242.js \
                  browser_bug581253.js \
                  browser_bug581947.js \
                  browser_bug585785.js \
                  browser_bug585830.js \
                  browser_bug592338.js \
                  browser_bug594131.js \
                  browser_bug595507.js \
                  browser_bug596687.js \
                  browser_bug597218.js \
+                 browser_bug609700.js \
                  browser_contextSearchTabPosition.js \
                  browser_ctrlTab.js \
                  browser_discovery.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
                  browser_hide_removing.js \
                  browser_inspector_initialization.js \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug565575.js
@@ -0,0 +1,13 @@
+function test() {
+  gBrowser.selectedBrowser.focus();
+  BrowserOpenTab();
+  ok(gURLBar.focused, "location bar is focused for a new tab");
+
+  gBrowser.selectedTab = gBrowser.tabs[0];
+  ok(!gURLBar.focused, "location bar isn't focused for the previously selected tab");
+
+  gBrowser.selectedTab = gBrowser.tabs[1];
+  ok(gURLBar.focused, "location bar is re-focused when selecting the new tab");
+
+  gBrowser.removeCurrentTab();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug609700.js
@@ -0,0 +1,20 @@
+function test() {
+  waitForExplicitFinish();
+
+  Services.ww.registerNotification(function (aSubject, aTopic, aData) {
+    if (aTopic == "domwindowopened") {
+      Services.ww.unregisterNotification(arguments.callee);
+
+      ok(true, "duplicateTabIn opened a new window");
+
+      aSubject.addEventListener("load", function () {
+        executeSoon(function () {
+          aSubject.close();
+          finish();
+        });
+      }, false);
+    }
+  });
+
+  duplicateTabIn(gBrowser.selectedTab, "window");
+}
--- a/browser/base/content/test/browser_tabfocus.js
+++ b/browser/base/content/test/browser_tabfocus.js
@@ -39,21 +39,18 @@ function test() {
     is(focusedWindow.value, browser1.contentWindow, "initial frame focus in tab 1");
     is(fm.getFocusedElementForWindow(browser2.contentWindow, false, focusedWindow), null, "initial focus in tab 2");
     is(focusedWindow.value, browser2.contentWindow, "initial frame focus in tab 2");
 
     expectFocusShift(function () gBrowser.selectedTab = tab2,
                      browser2.contentWindow, null, true,
                      "focusedElement after tab change, focus in new tab");
 
-    // switching tabs when the urlbar is focused and nothing in the new tab is focused
+    // switching tabs when nothing in the new tab is focused
     // should focus the browser
-    expectFocusShift(function () gURLBar.focus(),
-                     window, gURLBar.inputField, true,
-                     "url field focused");
     expectFocusShift(function () gBrowser.selectedTab = tab1,
                      browser1.contentWindow, null, true,
                      "focusedElement after tab change, focus in new tab");
 
     // focusing a button in the current tab should focus it
     var button1 = browser1.contentDocument.getElementById("button1");
     expectFocusShift(function () button1.focus(),
                      browser1.contentWindow, button1, true,
@@ -84,16 +81,19 @@ function test() {
 
     // focusing the url field should switch active focus away from the tab but
     // not clear what would be the focus in the tab 
     button1.focus();
     expectFocusShift(function () gURLBar.focus(),
                      window, gURLBar.inputField, true,
                      "focusedWindow after url field focused");
     is(fm.getFocusedElementForWindow(browser2.contentWindow, false, {}), button2, "url field focused, button in tab");
+    expectFocusShift(function () gURLBar.blur(),
+                     window, null, true,
+                     "focusedWindow after browser focused");
 
     // when a chrome element is focused, switching tabs to a tab with a button
     // with the current focus should focus the button
     expectFocusShift(function () gBrowser.selectedTab = tab1,
                      browser1.contentWindow, button1, true,
                      "focusedWindow after tab change, focus in url field, button focused in new tab");
     is(fm.getFocusedElementForWindow(browser2.contentWindow, false, {}), button2, "after switch tab, focus in unfocused tab");
 
--- a/browser/components/migration/content/migration.js
+++ b/browser/components/migration/content/migration.js
@@ -32,16 +32,18 @@
 # 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 *****
 
 const kIMig = Components.interfaces.nsIBrowserProfileMigrator;
 const kIPStartup = Components.interfaces.nsIProfileStartup;
 const kProfileMigratorContractIDPrefix = "@mozilla.org/profile/migrator;1?app=browser&type=";
+const Cc = Components.classes;
+const Ci = Components.interfaces;
 
 var MigrationWizard = {
   _source: "",                  // Source Profile Migrator ContractID suffix
   _itemsFlags: kIMig.ALL,       // Selected Import Data Sources (16-bit bitfield)
   _selectedProfile: null,       // Selected Profile name to import from
   _wiz: null,
   _migrator: null,
   _autoMigrate: null,
@@ -49,16 +51,17 @@ var MigrationWizard = {
 
   init: function ()
   {
     var os = Components.classes["@mozilla.org/observer-service;1"]
                        .getService(Components.interfaces.nsIObserverService);
     os.addObserver(this, "Migration:Started", false);
     os.addObserver(this, "Migration:ItemBeforeMigrate", false);
     os.addObserver(this, "Migration:ItemAfterMigrate", false);
+    os.addObserver(this, "Migration:ItemError", false);
     os.addObserver(this, "Migration:Ended", false);
 
     this._wiz = document.documentElement;
 
     if ("arguments" in window && window.arguments.length > 1) {
       this._source = window.arguments[0];
       this._migrator = window.arguments[1].QueryInterface(kIMig);
       this._autoMigrate = window.arguments[2].QueryInterface(kIPStartup);
@@ -76,16 +79,17 @@ var MigrationWizard = {
 
   uninit: function ()
   {
     var os = Components.classes["@mozilla.org/observer-service;1"]
                        .getService(Components.interfaces.nsIObserverService);
     os.removeObserver(this, "Migration:Started");
     os.removeObserver(this, "Migration:ItemBeforeMigrate");
     os.removeObserver(this, "Migration:ItemAfterMigrate");
+    os.removeObserver(this, "Migration:ItemError");
     os.removeObserver(this, "Migration:Ended");
   },
 
   // 1 - Import Source
   onImportSourcePageShow: function ()
   {
     // Reference to the "From File" radio button 
     var fromfile = null;
@@ -484,16 +488,45 @@ var MigrationWizard = {
         setTimeout(close, 5000);
       }
       else {
         this._wiz.canAdvance = true;
         var nextButton = this._wiz.getButton("next");
         nextButton.click();
       }
       break;
+    case "Migration:ItemError":
+      var type = "undefined";
+      switch (parseInt(aData)) {
+      case Ci.nsIBrowserProfileMigrator.SETTINGS:
+        type = "settings";
+        break;
+      case Ci.nsIBrowserProfileMigrator.COOKIES:
+        type = "cookies";
+        break;
+      case Ci.nsIBrowserProfileMigrator.HISTORY:
+        type = "history";
+        break;
+      case Ci.nsIBrowserProfileMigrator.FORMDATA:
+        type = "form data";
+        break;
+      case Ci.nsIBrowserProfileMigrator.PASSWORDS:
+        type = "passwords";
+        break;
+      case Ci.nsIBrowserProfileMigrator.BOOKMARKS:
+        type = "bookmarks";
+        break;
+      case Ci.nsIBrowserProfileMigrator.OTHERDATA:
+        type = "misc. data";
+        break;
+      }
+      Cc["@mozilla.org/consoleservice;1"]
+        .getService(Ci.nsIConsoleService)
+        .logStringMessage("some " + type + " did not successfully migrate.");
+      break;
     }
   },
 
   onDonePageShow: function ()
   {
     this._wiz.getButton("cancel").disabled = true;
     this._wiz.canRewind = false;
     this._listItems("doneItems");
--- a/browser/components/migration/src/nsBrowserProfileMigratorUtils.h
+++ b/browser/components/migration/src/nsBrowserProfileMigratorUtils.h
@@ -34,29 +34,31 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef browserprofilemigratorutils___h___
 #define browserprofilemigratorutils___h___
 
 #define MIGRATION_ITEMBEFOREMIGRATE "Migration:ItemBeforeMigrate"
+#define MIGRATION_ITEMMIGRATEERROR  "Migration:ItemError"
 #define MIGRATION_ITEMAFTERMIGRATE  "Migration:ItemAfterMigrate"
 #define MIGRATION_STARTED           "Migration:Started"
 #define MIGRATION_ENDED             "Migration:Ended"
 
 #define NOTIFY_OBSERVERS(message, item) \
   mObserverService->NotifyObservers(nsnull, message, item)
 
 #define COPY_DATA(func, replace, itemIndex) \
-  if (NS_SUCCEEDED(rv) && (aItems & itemIndex || !aItems)) { \
+  if ((aItems & itemIndex || !aItems)) { \
     nsAutoString index; \
     index.AppendInt(itemIndex); \
     NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get()); \
-    rv = func(replace); \
+    if (NS_FAILED(func(replace))) \
+      NOTIFY_OBSERVERS(MIGRATION_ITEMMIGRATEERROR, index.get()); \
     NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); \
   }
 
 #define NC_URI(property) \
   NS_LITERAL_CSTRING("http://home.netscape.com/NC-rdf#"#property)
 
 #define BATCH_ACTION_HISTORY 0
 #define BATCH_ACTION_HISTORY_REPLACE 1
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp
+++ b/browser/components/migration/src/nsIEProfileMigrator.cpp
@@ -65,16 +65,18 @@
 #include "nsISimpleEnumerator.h"
 #include "nsISupportsArray.h"
 #include "nsIProfileMigrator.h"
 #include "nsIBrowserProfileMigrator.h"
 #include "nsIObserverService.h"
 #include "nsILocalFileWin.h"
 #include "nsAutoPtr.h"
 
+#include "prnetdb.h"
+
 #include <objbase.h>
 #include <shlguid.h>
 #include <urlhist.h>
 #include <comdef.h>
 #include <shlobj.h>
 #include <intshcut.h>
 
 #include "nsIBrowserHistory.h"
@@ -1914,24 +1916,30 @@ nsIEProfileMigrator::CopyCookiesFromBuff
     PL_strncpy(hostCopyConstructor, host, hostLength);
     hostCopyConstructor += hostLength;
 
     *hostCopyConstructor = '\0';
 
     nsDependentCString stringName(name),
                        stringPath(path);
 
-    // delete any possible extant matching host cookie
-    if (hostCopy[0] == '.')
+    // delete any possible extant matching host cookie and
+    // check if we're dealing with an IPv4/IPv6 hostname.
+    PRBool isIPAddress = PR_FALSE;
+    if (hostCopy[0] == '.') {
       aCookieManager->Remove(nsDependentCString(hostCopy+1),
                              stringName, stringPath, PR_FALSE);
+      PRNetAddr addr;
+      if (PR_StringToNetAddr(hostCopy+1, &addr) == PR_SUCCESS)
+        isIPAddress = PR_TRUE;
+    }
 
     nsresult onerv;
     // Add() makes a new domain cookie
-    onerv = aCookieManager->Add(nsDependentCString(hostCopy),
+    onerv = aCookieManager->Add(nsDependentCString(hostCopy + (isIPAddress ? 1 : 0)),
                                 stringPath,
                                 stringName,
                                 nsDependentCString(value),
                                 flagsValue & 0x1, // isSecure
                                 PR_FALSE, // isHttpOnly
                                 PR_FALSE, // isSession
                                 PRInt64(expirationDate));
     if (NS_FAILED(onerv)) {
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -740,34 +740,69 @@ SessionStoreService.prototype = {
       let followUp = this._statesToRestore[aWindow.__SS_restoreID].windows.length == 1;
       this.restoreWindow(aWindow, this._statesToRestore[aWindow.__SS_restoreID], true, followUp);
     }
     else if (this._restoreLastWindow && aWindow.toolbar.visible &&
              this._closedWindows.length &&
              !this._inPrivateBrowsing) {
       // default to the most-recently closed window
       // don't use popup windows
-      let state = null;
-      let newClosedWindows = this._closedWindows.filter(function(aWinState) {
-        if (!state && !aWinState.isPopup) {
-          state = aWinState;
-          return false;
+      let closedWindowState = null;
+      let closedWindowIndex;
+      for (let i = 0; i < this._closedWindows.length; i++) {
+        // Take the first non-popup, point our object at it, and break out.
+        if (!this._closedWindows[i].isPopup) {
+          closedWindowState = this._closedWindows[i];
+          closedWindowIndex = i;
+          break;
         }
-        return true;
-      });
-      if (state) {
-        delete state.hidden;
+      }
+
+      if (closedWindowState) {
+        let newWindowState;
 #ifndef XP_MACOSX
-        if (!this._doResumeSession())
+        if (!this._doResumeSession()) {
 #endif
-          state.tabs = state.tabs.filter(function (tab) tab.pinned);
-        if (state.tabs.length > 0) {
-          this._closedWindows = newClosedWindows;
+          // We want to split the window up into pinned tabs and unpinned tabs.
+          // Pinned tabs should be restored. If there are any remaining tabs,
+          // they should be added back to _closedWindows.
+          // We'll cheat a little bit and reuse _prepDataForDeferredRestore
+          // even though it wasn't built exactly for this.
+          let [appTabsState, normalTabsState] =
+            this._prepDataForDeferredRestore(JSON.stringify({ windows: [closedWindowState] }));
+
+          // These are our pinned tabs, which we should restore
+          if (appTabsState.windows.length) {
+            newWindowState = appTabsState.windows[0];
+            delete newWindowState.__lastSessionWindowID;
+          }
+
+          // In case there were no unpinned tabs, remove the window from _closedWindows
+          if (!normalTabsState.windows.length) {
+            this._closedWindows.splice(closedWindowIndex, 1);
+          }
+          // Or update _closedWindows with the modified state
+          else {
+            delete normalTabsState.windows[0].__lastSessionWindowID;
+            this._closedWindows[closedWindowIndex] = normalTabsState.windows[0];
+          }
+#ifndef XP_MACOSX
+        }
+        else {
+          // If we're just restoring the window, make sure it gets removed from
+          // _closedWindows.
+          this._closedWindows.splice(closedWindowIndex, 1);
+          newWindowState = closedWindowState;
+          delete newWindowState.hidden;
+        }
+#endif
+        if (newWindowState) {
+          // Ensure that the window state isn't hidden
           this._restoreCount = 1;
-          state = { windows: [state] };
+          let state = { windows: [newWindowState] };
           this.restoreWindow(aWindow, state, this._isCmdLineEmpty(aWindow, state));
         }
       }
       // we actually restored the session just now.
       this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
     }
     if (this._restoreLastWindow && aWindow.toolbar.visible) {
       // always reset (if not a popup window)
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -115,16 +115,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_524745.js \
 	browser_528776.js \
 	browser_579868.js \
 	browser_579879.js \
 	browser_580512.js \
 	browser_581593.js \
 	browser_586147.js \
 	browser_586068-cascaded_restore.js \
+	browser_589246.js \
 	browser_590268.js \
 	browser_600545.js \
 	$(NULL)
 
 ifneq ($(OS_ARCH),Darwin)
 _BROWSER_TEST_FILES += \
 	browser_597071.js \
 	$(NULL)
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_589246.js
@@ -0,0 +1,276 @@
+/* ***** 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 sessionstore test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Paul O’Shannessy <paul@oshannessy.com>
+ *
+ * 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 ***** */
+
+// Mirrors WINDOW_ATTRIBUTES IN nsSessionStore.js
+const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"];
+
+Cu.import("resource://gre/modules/Services.jsm");
+let ss = Cc["@mozilla.org/browser/sessionstore;1"].
+         getService(Ci.nsISessionStore);
+
+let stateBackup = ss.getBrowserState();
+
+let originalWarnOnClose = gPrefService.getBoolPref("browser.tabs.warnOnClose");
+let originalStartupPage = gPrefService.getIntPref("browser.startup.page");
+let originalWindowType = document.documentElement.getAttribute("windowtype");
+
+let gotLastWindowClosedTopic = false;
+let shouldPinTab = false;
+let shouldOpenTabs = false;
+let shouldCloseTab = false;
+let testNum = 0;
+let afterTestCallback;
+
+// Set state so we know the closed windows content
+let testState = {
+  windows: [
+    { tabs: [{ entries: [{ url: "http://example.org" }] }] }
+  ],
+  _closedWindows: []
+};
+
+// We'll push a set of conditions and callbacks into this array
+// Ideally we would also test win/linux under a complete set of conditions, but
+// the tests for osx mirror the other set of conditions possible on win/linux.
+let tests = [];
+
+// the third & fourth test share a condition check, keep it DRY
+function checkOSX34Generator(num) {
+  return function(aPreviousState, aCurState) {
+    // In here, we should have restored the pinned tab, so only the unpinned tab
+    // should be in aCurState. So let's shape our expectations.
+    let expectedState = JSON.parse(aPreviousState);
+    expectedState[0].tabs.shift();
+    // size attributes are stripped out in _prepDataForDeferredRestore in nsSessionStore.
+    // This isn't the best approach, but neither is comparing JSON strings
+    WINDOW_ATTRIBUTES.forEach(function (attr) delete expectedState[0][attr]);
+
+    is(aCurState, JSON.stringify(expectedState),
+       "test #" + num + ": closedWindowState is as expected");
+  };
+}
+function checkNoWindowsGenerator(num) {
+  return function(aPreviousState, aCurState) {
+    is(aCurState, "[]", "test #" + num + ": there should be no closedWindowsLeft");
+  };
+}
+
+// The first test has 0 pinned tabs and 1 unpinned tab
+tests.push({
+  pinned: false,
+  extra: false,
+  close: false,
+  checkWinLin: checkNoWindowsGenerator(1),
+  checkOSX: function(aPreviousState, aCurState) {
+    is(aCurState, aPreviousState, "test #1: closed window state is unchanged");
+  }
+});
+
+// The second test has 1 pinned tab and 0 unpinned tabs.
+tests.push({
+  pinned: true,
+  extra: false,
+  close: false,
+  checkWinLin: checkNoWindowsGenerator(2),
+  checkOSX: checkNoWindowsGenerator(2)
+});
+
+// The third test has 1 pinned tab and 2 unpinned tabs.
+tests.push({
+  pinned: true,
+  extra: true,
+  close: false,
+  checkWinLin: checkNoWindowsGenerator(3),
+  checkOSX: checkOSX34Generator(3)
+});
+
+// The fourth test has 1 pinned tab, 2 unpinned tabs, and closes one unpinned tab.
+tests.push({
+  pinned: true,
+  extra: true,
+  close: "one",
+  checkWinLin: checkNoWindowsGenerator(4),
+  checkOSX: checkOSX34Generator(4)
+});
+
+// The fifth test has 1 pinned tab, 2 unpinned tabs, and closes both unpinned tabs.
+tests.push({
+  pinned: true,
+  extra: true,
+  close: "both",
+  checkWinLin: checkNoWindowsGenerator(5),
+  checkOSX: checkNoWindowsGenerator(5)
+});
+
+
+function test() {
+  /** Test for Bug 589246 - Closed window state getting corrupted when closing
+      and reopening last browser window without exiting browser **/
+  waitForExplicitFinish();
+  // windows opening & closing, so extending the timeout
+  requestLongerTimeout(2);
+
+  // We don't want the quit dialog pref
+  gPrefService.setBoolPref("browser.tabs.warnOnClose", false);
+  // Ensure that we would restore the session (important for Windows)
+  gPrefService.setIntPref("browser.startup.page", 3);
+
+  runNextTestOrFinish();
+}
+
+function runNextTestOrFinish() {
+  if (tests.length) {
+    setupForTest(tests.shift())
+  }
+  else {
+    // some state is cleaned up at the end of each test, but not all
+    ["browser.tabs.warnOnClose", "browser.startup.page"].forEach(function(p) {
+      if (gPrefService.prefHasUserValue(p))
+        gPrefService.clearUserPref(p);
+    });
+
+    ss.setBrowserState(stateBackup);
+    executeSoon(finish);
+  }
+}
+
+function setupForTest(aConditions) {
+  // reset some checks
+  gotLastWindowClosedTopic = false;
+  shouldPinTab = aConditions.pinned;
+  shouldOpenTabs = aConditions.extra;
+  shouldCloseTab = aConditions.close;
+  testNum++;
+
+  // set our test callback
+  afterTestCallback = /Mac/.test(navigator.platform) ? aConditions.checkOSX
+                                                     : aConditions.checkWinLin;
+
+  // Add observers
+  Services.obs.addObserver(onLastWindowClosed, "browser-lastwindow-close-granted", false);
+
+  // Set the state
+  Services.obs.addObserver(onStateRestored, "sessionstore-browser-state-restored", false);
+  ss.setBrowserState(JSON.stringify(testState));
+}
+
+function onStateRestored(aSubject, aTopic, aData) {
+  info("test #" + testNum + ": onStateRestored");
+  Services.obs.removeObserver(onStateRestored, "sessionstore-browser-state-restored", false);
+
+  // change this window's windowtype so that closing a new window will trigger
+  // browser-lastwindow-close-granted.
+  document.documentElement.setAttribute("windowtype", "navigator:testrunner");
+
+  let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "http://example.com");
+  newWin.addEventListener("load", function(aEvent) {
+    newWin.removeEventListener("load", arguments.callee, false);
+
+    newWin.gBrowser.selectedBrowser.addEventListener("load", function() {
+      newWin.gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+      // pin this tab
+      if (shouldPinTab)
+        newWin.gBrowser.pinTab(newWin.gBrowser.selectedTab);
+
+      newWin.addEventListener("unload", onWindowUnloaded, false);
+      // Open a new tab as well. On Windows/Linux this will be restored when the
+      // new window is opened below (in onWindowUnloaded). On OS X we'll just
+      // restore the pinned tabs, leaving the unpinned tab in the closedWindowsData.
+      if (shouldOpenTabs) {
+        let newTab = newWin.gBrowser.addTab("about:config");
+        let newTab2 = newWin.gBrowser.addTab("about:buildconfig");
+
+        newTab.linkedBrowser.addEventListener("load", function() {
+          newTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+          if (shouldCloseTab == "one") {
+            newWin.gBrowser.removeTab(newTab2);
+          }
+          else if (shouldCloseTab == "both") {
+            newWin.gBrowser.removeTab(newTab);
+            newWin.gBrowser.removeTab(newTab2);
+          }
+          newWin.BrowserTryToCloseWindow();
+        }, true);
+      }
+      else {
+        newWin.BrowserTryToCloseWindow();
+      }
+    }, true);
+  }, false);
+}
+
+// This will be called before the window is actually closed
+function onLastWindowClosed(aSubject, aTopic, aData) {
+  info("test #" + testNum + ": onLastWindowClosed");
+  Services.obs.removeObserver(onLastWindowClosed, "browser-lastwindow-close-granted", false);
+  gotLastWindowClosedTopic = true;
+}
+
+// This is the unload event listener on the new window (from onStateRestored).
+// Unload is fired after the window is closed, so sessionstore has already
+// updated _closedWindows (which is important). We'll open a new window here
+// which should actually trigger the bug.
+function onWindowUnloaded() {
+  info("test #" + testNum + ": onWindowClosed");
+  ok(gotLastWindowClosedTopic, "test #" + testNum + ": browser-lastwindow-close-granted was notified prior");
+
+  let previousClosedWindowData = ss.getClosedWindowData();
+
+  // Now we want to open a new window
+  let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:robots");
+  newWin.addEventListener("load", function(aEvent) {
+    newWin.removeEventListener("load", arguments.callee, false);
+
+    newWin.gBrowser.selectedBrowser.addEventListener("load", function () {
+      // Good enough for checking the state
+      afterTestCallback(previousClosedWindowData, ss.getClosedWindowData());
+      afterTestCleanup(newWin);
+    }, true);
+
+  }, false);
+}
+
+function afterTestCleanup(aNewWin) {
+  executeSoon(function() {
+    aNewWin.close();
+    document.documentElement.setAttribute("windowtype", originalWindowType);
+    runNextTestOrFinish();
+  });
+}
+
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -380,18 +380,16 @@
 #endif
 @BINPATH@/components/nsINIProcessor.manifest
 @BINPATH@/components/nsINIProcessor.js
 @BINPATH@/components/nsPrompter.manifest
 @BINPATH@/components/nsPrompter.js
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/components/SyncComponents.manifest
 @BINPATH@/components/Weave.js
-@BINPATH@/components/WeaveCrypto.manifest
-@BINPATH@/components/WeaveCrypto.js
 #endif
 
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsSafebrowsingApplication.manifest
 @BINPATH@/components/nsSafebrowsingApplication.js
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -62,16 +62,18 @@ components/nsCloseAllWindows.js
 components/nsDictionary.js
 components/nsExtensionManager.js
 components/nsInterfaceInfoToIDL.js
 components/nsScriptableIO.js
 components/nsUrlClassifierTable.js
 components/nsXmlRpcClient.js
 components/pluginGlue.js
 components/sidebar.xpt
+components/WeaveCrypto.js
+components/WeaveCrypto.manifest
 components/xmlextras.xpt
 components/xpcom.xpt
 components/xpti.dat
 components/xptitemp.dat
 defaults/pref/all.js
 defaults/pref/bug259708.js
 defaults/pref/bug307259.js
 defaults/pref/reporter.js
@@ -616,17 +618,16 @@ xpicleanup@BIN_SUFFIX@
   components/nsUrlClassifierLib.js
   components/nsUrlClassifierListManager.js
   components/nsURLFormatter.js
   components/nsWebHandlerApp.js
   components/PlacesProtocolHandler.js
   components/storage-Legacy.js
   components/storage-mozStorage.js
   components/txEXSLTRegExFunctions.js
-  components/WeaveCrypto.js
   components/Weave.js
   components/WebContentConverter.js
   defaults/autoconfig/platform.js
   defaults/autoconfig/prefcalls.js
   defaults/pref/firefox-branding.js
   defaults/pref/firefox.js
   defaults/pref/firefox-l10n.js
   defaults/pref/services-sync.js
--- a/browser/themes/winstripe/browser/browser-aero.css
+++ b/browser/themes/winstripe/browser/browser-aero.css
@@ -28,29 +28,22 @@
     border-top: none;
     -moz-border-left-colors: rgba(255,255,255,.5) rgba(83,42,6,.9);
     -moz-border-bottom-colors: rgba(255,255,255,.5) rgba(83,42,6,.9);
     -moz-border-right-colors: rgba(255,255,255,.5) rgba(83,42,6,.9);
     box-shadow: 0 1px 0 rgba(255,255,255,.25) inset,
                 0 0 2px 1px rgba(255,255,255,.25) inset;
   }
 
-  #main-window[privatebrowsingmode=temporary] #appmenu-button:not(:-moz-window-inactive) {
+  #main-window[privatebrowsingmode=temporary] #appmenu-button {
     -moz-border-left-colors: rgba(255,255,255,.5) rgba(43,8,65,.9);
     -moz-border-bottom-colors: rgba(255,255,255,.5) rgba(43,8,65,.9);
     -moz-border-right-colors: rgba(255,255,255,.5) rgba(43,8,65,.9);
   }
 
-  #appmenu-button:-moz-window-inactive {
-    -moz-border-left-colors: rgba(255,255,255,.4) rgba(0,0,0,.5);
-    -moz-border-bottom-colors: rgba(255,255,255,.4) rgba(0,0,0,.5);
-    -moz-border-right-colors: rgba(255,255,255,.4) rgba(0,0,0,.5);
-    box-shadow: 0 0 0 1px rgba(255,255,255,.25) inset;
-  }
-
   /* Bug 413060, comment 16: Vista Aero is a special case where we use a
      tooltip appearance for the address bar popup panels */
   #identity-popup,
   #editBookmarkPanel {
     -moz-appearance: tooltip;
     color: InfoText;
   }
 
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -121,26 +121,21 @@
   color: white;
   text-shadow: 0 0 1px rgba(0,0,0,.7),
                0 1px 1.5px rgba(0,0,0,.5);
   font-weight: bold;
   padding: .1em 1.5em .15em;
   margin: 0;
 }
 
-#main-window[privatebrowsingmode=temporary] #appmenu-button:not(:-moz-window-inactive) {
+#main-window[privatebrowsingmode=temporary] #appmenu-button {
   background-image: -moz-linear-gradient(rgb(153,38,211), rgb(105,19,163) 95%);
   border-color: rgba(43,8,65,.9);
 }
 
-#appmenu-button:-moz-window-inactive {
-  background-image: none;
-  border-color: rgba(0,0,0,.4);
-}
-
 #appmenu-button:hover:not(:active):not([open]) {
   background-image: -moz-radial-gradient(center bottom, farthest-side, rgba(252,240,89,.5) 10%, rgba(252,240,89,0) 70%),
                     -moz-radial-gradient(center bottom, farthest-side, rgb(236,133,0), rgba(255,229,172,0)),
                     -moz-linear-gradient(rgb(246,170,69), rgb(209,74,0) 95%);
   border-color: rgba(83,42,6,.9);
   box-shadow: 0 1px 0 rgba(255,255,255,.1) inset,
               0 0 1.5px 1px rgba(250,234,169,.7) inset,
               0 -1px 0 rgba(250,234,169,.5) inset;
--- a/chrome/src/nsChromeRegistryChrome.cpp
+++ b/chrome/src/nsChromeRegistryChrome.cpp
@@ -452,28 +452,28 @@ EnumerateOverride(nsIURI* aURIKey,
     chromeURI, overrideURI
   };
   overrides->AppendElement(override);
   return (PLDHashOperator)PL_DHASH_NEXT;
 }
 
 struct EnumerationArgs
 {
-  nsTArray<ChromePackage>& packages;
+  InfallibleTArray<ChromePackage>& packages;
   const nsCString& selectedLocale;
   const nsCString& selectedSkin;
 };
 
 void
 nsChromeRegistryChrome::SendRegisteredChrome(
     mozilla::dom::PContentParent* aParent)
 {
-  nsTArray<ChromePackage> packages;
-  nsTArray<ResourceMapping> resources;
-  nsTArray<OverrideMapping> overrides;
+  InfallibleTArray<ChromePackage> packages;
+  InfallibleTArray<ResourceMapping> resources;
+  InfallibleTArray<OverrideMapping> overrides;
 
   EnumerationArgs args = {
     packages, mSelectedLocale, mSelectedSkin
   };
   PL_DHashTableEnumerate(&mPackagesHash, CollectPackages, &args);
 
   nsCOMPtr<nsIIOService> io (do_GetIOService());
   NS_ENSURE_TRUE(io, );
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -607,17 +607,16 @@ OS_TEST=@OS_TEST@
 SOLARIS_SUNPRO_CC = @SOLARIS_SUNPRO_CC@
 SOLARIS_SUNPRO_CXX = @SOLARIS_SUNPRO_CXX@
 
 # For AIX build
 AIX_OBJMODEL = @AIX_OBJMODEL@
 
 # For OS/2 build
 MOZ_OS2_TOOLS = @MOZ_OS2_TOOLS@
-MOZ_OS2_USE_DECLSPEC = @MOZ_OS2_USE_DECLSPEC@
 MOZ_OS2_HIGH_MEMORY = @MOZ_OS2_HIGH_MEMORY@
 
 HAVE_XIE=@HAVE_XIE@
 
 MOZ_PSM=@MOZ_PSM@
 
 # Gssapi (krb5) libraries and headers for the Negotiate auth method
 GSSAPI_INCLUDES = @GSSAPI_INCLUDES@
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1244,36 +1244,20 @@ endif
 ifeq ($(OS_ARCH),OS2)
 $(DEF_FILE): $(OBJS) $(SHARED_LIBRARY_LIBS)
 	rm -f $@
 	echo LIBRARY $(SHARED_LIBRARY_NAME) INITINSTANCE TERMINSTANCE > $@
 	echo PROTMODE >> $@
 	echo CODE    LOADONCALL MOVEABLE DISCARDABLE >> $@
 	echo DATA    PRELOAD MOVEABLE MULTIPLE NONSHARED >> $@
 	echo EXPORTS >> $@
-ifeq ($(IS_COMPONENT),1)
-ifeq ($(HAS_EXTRAEXPORTS),1)
-ifndef MOZ_OS2_USE_DECLSPEC
-	$(FILTER) $(OBJS) $(SHARED_LIBRARY_LIBS) >> $@
-endif	
-else
-	echo    _NSModule >> $@
-endif
-else
-ifndef MOZ_OS2_USE_DECLSPEC
-	$(FILTER) $(OBJS) $(SHARED_LIBRARY_LIBS) >> $@
-endif	
-endif
+
 	$(ADD_TO_DEF_FILE)
 
-ifdef MOZ_OS2_USE_DECLSPEC
 $(IMPORT_LIBRARY): $(SHARED_LIBRARY)
-else
-$(IMPORT_LIBRARY): $(DEF_FILE)
-endif
 	rm -f $@
 	$(IMPLIB) $@ $^
 	$(RANLIB) $@
 endif # OS/2
 
 $(HOST_LIBRARY): $(HOST_OBJS) Makefile
 	rm -f $@
 	$(HOST_AR) $(HOST_AR_FLAGS) $(HOST_OBJS)
--- a/configure.in
+++ b/configure.in
@@ -2637,17 +2637,17 @@ ia64*-hpux*)
     DSO_PIC_CFLAGS=
     AR=emxomfar
     AR_FLAGS='r $@'
     CFLAGS="$CFLAGS -Zomf"
     CXXFLAGS="$CXXFLAGS -Zomf"
     DSO_LDOPTS='-Zdll'
     BIN_FLAGS='-Zlinker /ST:0x100000'
     IMPLIB='emximp -o'
-    FILTER='emxexp -o'
+    FILTER='true'
     LDFLAGS='-Zmap'
     WARNINGS_AS_ERRORS='-Werror'
     MOZ_DEBUG_FLAGS="-g -fno-inline"
     MOZ_OPTIMIZE_FLAGS="-O2"
     MOZ_OPTIMIZE_LDFLAGS="-s -Zlinker /EXEPACK:2 -Zlinker /PACKCODE -Zlinker /PACKDATA"
     DYNAMIC_XPCOM_LIBS='-L$(LIBXUL_DIST)/lib $(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
     LIBXUL_LIBS='-L$(LIBXUL_DIST)/lib $(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
     TARGET_MD_ARCH=os2
@@ -2673,19 +2673,18 @@ ia64*-hpux*)
     _DEFINES_CXXFLAGS="$_DEFINES_CXXFLAGS -Uunix -U__unix -U__unix__"
 
     AC_CACHE_CHECK(for __declspec(dllexport),
         ac_os2_declspec,
         [AC_TRY_COMPILE([__declspec(dllexport) void ac_os2_declspec(void) {}],
                         [return 0;],
                         ac_os2_declspec="yes",
                         ac_os2_declspec="no")])
-    if test "$ac_os2_declspec" = "yes"; then
-        FILTER='true'
-        MOZ_OS2_USE_DECLSPEC='1'
+    if test "$ac_os2_declspec" != "yes"; then
+        AC_MSG_ERROR([Compiler does not support __declspec(dllexport), install GCC-4.3.2 or newer])
     fi
     ;;
 
 alpha*-*-osf*)
     if test "$GNU_CC"; then
       MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,$@ -o $@'
       MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,$@ -o $@'
 
@@ -8929,17 +8928,16 @@ AC_SUBST(XPCOM_LIBS)
 AC_SUBST(XPCOM_FROZEN_LDOPTS)
 AC_SUBST(XPCOM_GLUE_LDOPTS)
 AC_SUBST(XPCOM_STANDALONE_GLUE_LDOPTS)
 
 AC_SUBST(USE_DEPENDENT_LIBS)
 
 AC_SUBST(MOZ_BUILD_ROOT)
 AC_SUBST(MOZ_OS2_TOOLS)
-AC_SUBST(MOZ_OS2_USE_DECLSPEC)
 
 AC_SUBST(MOZ_POST_DSO_LIB_COMMAND)
 AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
 AC_SUBST(MOZ_TIMELINE)
 AC_SUBST(OGLES_SDK_DIR)
 
 AC_SUBST(MOZ_APP_NAME)
 AC_SUBST(MOZ_APP_DISPLAYNAME)
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1695,16 +1695,24 @@ public:
    * A sub document isn't tabbable when it's a zombie document.
    *
    * @param aElement element to test.
    *
    * @return Whether the subdocument is tabbable.
    */
   static bool IsSubDocumentTabbable(nsIContent* aContent);
 
+  /**
+   * Flushes the layout tree (recursively)
+   *
+   * @param aWindow the window the flush should start at
+   *
+   */
+  static void FlushLayoutForTree(nsIDOMWindow* aWindow);
+
 private:
 
   static PRBool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static nsIDOMScriptObjectFactory *GetDOMScriptObjectFactory();
 
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -733,17 +733,17 @@ public:
    * observer that is already observing the node must not be added without
    * being removed first.
    */
   void AddMutationObserver(nsIMutationObserver* aMutationObserver)
   {
     nsSlots* s = GetSlots();
     if (s) {
       NS_ASSERTION(s->mMutationObservers.IndexOf(aMutationObserver) ==
-                   nsTArray_base::NoIndex,
+                   nsTArray<int>::NoIndex,
                    "Observer already in the list");
       s->mMutationObservers.AppendElement(aMutationObserver);
     }
   }
 
   /**
    * Same as above, but only adds the observer if its not observing
    * the node already.
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -53,18 +53,19 @@
 #include "nsCOMPtr.h"
 
 typedef PRUptrdiff PtrBits;
 class nsAString;
 class nsIAtom;
 class nsICSSStyleRule;
 class nsISVGValue;
 class nsIDocument;
-template<class E> class nsTArray;
-template<class E> class nsTPtrArray;
+template<class E, class A> class nsTArray;
+template<class E, class A> class nsTPtrArray;
+struct nsTArrayDefaultAllocator;
 
 #define NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM 12
 
 #define NS_ATTRVALUE_BASETYPE_MASK (PtrBits(3))
 #define NS_ATTRVALUE_POINTERVALUE_MASK (~NS_ATTRVALUE_BASETYPE_MASK)
 
 #define NS_ATTRVALUE_INTEGERTYPE_BITS 4
 #define NS_ATTRVALUE_INTEGERTYPE_MASK (PtrBits((1 << NS_ATTRVALUE_INTEGERTYPE_BITS) - 1))
@@ -376,17 +377,17 @@ private:
   // aStrict is set PR_TRUE if stringifying the return value equals with
   // aValue.
   PRInt32 StringToInteger(const nsAString& aValue,
                           PRBool* aStrict,
                           PRInt32* aErrorCode,
                           PRBool aCanBePercent = PR_FALSE,
                           PRBool* aIsPercent = nsnull) const;
 
-  static nsTPtrArray<const EnumTable>* sEnumTableArray;
+  static nsTPtrArray<const EnumTable, nsTArrayDefaultAllocator>* sEnumTableArray;
 
   PtrBits mBits;
 };
 
 /**
  * Implementation of inline methods
  */
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6298,16 +6298,50 @@ nsContentUtils::IsSubDocumentTabbable(ns
   contentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
 
   // If there are 2 viewers for the current docshell, that
   // means the current document is a zombie document.
   // Only navigate into the subdocument if it's not a zombie.
   return !zombieViewer;
 }
 
+void
+nsContentUtils::FlushLayoutForTree(nsIDOMWindow* aWindow)
+{
+    nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
+    if (!piWin)
+        return;
+
+    // Note that because FlushPendingNotifications flushes parents, this
+    // is O(N^2) in docshell tree depth.  However, the docshell tree is
+    // usually pretty shallow.
+
+    nsCOMPtr<nsIDOMDocument> domDoc;
+    aWindow->GetDocument(getter_AddRefs(domDoc));
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+    if (doc) {
+        doc->FlushPendingNotifications(Flush_Layout);
+    }
+
+    nsCOMPtr<nsIDocShellTreeNode> node =
+        do_QueryInterface(piWin->GetDocShell());
+    if (node) {
+        PRInt32 i = 0, i_end;
+        node->GetChildCount(&i_end);
+        for (; i < i_end; ++i) {
+            nsCOMPtr<nsIDocShellTreeItem> item;
+            node->GetChildAt(i, getter_AddRefs(item));
+            nsCOMPtr<nsIDOMWindow> win = do_GetInterface(item);
+            if (win) {
+                FlushLayoutForTree(win);
+            }
+        }
+    }
+}
+
 void nsContentUtils::RemoveNewlines(nsString &aString)
 {
   // strip CR/LF and null
   static const char badChars[] = {'\r', '\n', 0};
   aString.StripChars(badChars);
 }
 
 void
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -200,16 +200,18 @@ static NS_DEFINE_CID(kDOMEventGroupCID, 
 #include "nsHTMLStyleSheet.h"
 #include "nsHTMLCSSStyleSheet.h"
 
 #include "mozilla/dom/Link.h"
 #include "nsIHTMLDocument.h"
 
 using namespace mozilla::dom;
 
+typedef nsTArray<Link*> LinkArray;
+
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
 #define NAME_NOT_VALID ((nsBaseContentList*)1)
 
@@ -3897,17 +3899,17 @@ nsDocument::InternalAllowXULXBL()
   return PR_FALSE;
 }
 
 // Note: We don't hold a reference to the document observer; we assume
 // that it has a live reference to the document.
 void
 nsDocument::AddObserver(nsIDocumentObserver* aObserver)
 {
-  NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray_base::NoIndex,
+  NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
                "Observer already in the list");
   mObservers.AppendElement(aObserver);
   AddMutationObserver(aObserver);
 }
 
 PRBool
 nsDocument::RemoveObserver(nsIDocumentObserver* aObserver)
 {
@@ -5538,18 +5540,20 @@ nsDocument::GetAnimationController()
   if (mAnimationController && shell) {
     nsPresContext *context = shell->GetPresContext();
     if (context &&
         context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
       mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
     }
   }
 
-  // If we're hidden (or being hidden), notify the animation controller.
-  if (!mIsShowing) {
+  // If we're hidden (or being hidden), notify the newly-created animation
+  // controller. (Skip this check for SVG-as-an-image documents, though,
+  // because they don't get OnPageShow / OnPageHide calls).
+  if (!mIsShowing && !mIsBeingUsedAsImage) {
     mAnimationController->OnPageHide();
   }
 
   return mAnimationController;
 }
 #endif // MOZ_SMIL
 
 struct DirTable {
@@ -7541,33 +7545,33 @@ nsDocument::DestroyElementMaps()
   mStyledLinks.Clear();
   mIdentifierMap.Clear();
 }
 
 static
 PLDHashOperator
 EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray)
 {
-  nsTArray<Link*>* array = static_cast<nsTArray<Link*>*>(aArray);
+  LinkArray* array = static_cast<LinkArray*>(aArray);
   (void)array->AppendElement(aEntry->GetKey());
   return PL_DHASH_NEXT;
 }
 
 void
 nsDocument::RefreshLinkHrefs()
 {
   // Get a list of all links we know about.  We will reset them, which will
   // remove them from the document, so we need a copy of what is in the
   // hashtable.
-  nsTArray<Link*> linksToNotify(mStyledLinks.Count());
+  LinkArray linksToNotify(mStyledLinks.Count());
   (void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify);
 
   // Reset all of our styled links.
   MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_STATE, PR_TRUE);
-  for (nsTArray_base::size_type i = 0; i < linksToNotify.Length(); i++) {
+  for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
     linksToNotify[i]->ResetLinkState(true);
   }
 }
 
 NS_IMETHODIMP
 nsDocument::GetScriptTypeID(PRUint32 *aScriptType)
 {
     NS_ERROR("No default script type here - ask some element");
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -718,18 +718,17 @@ nsFrameLoader::ShowRemoteFrame(const nsI
 
   // FIXME/bug 589337: Show()/Hide() is pretty expensive for
   // cross-process layers; need to figure out what behavior we really
   // want here.  For now, hack.
   if (!mRemoteBrowserShown) {
     mRemoteBrowser->Show(size);
     mRemoteBrowserShown = PR_TRUE;
 
-    nsCOMPtr<nsIChromeFrameMessageManager> dummy;
-    GetMessageManager(getter_AddRefs(dummy)); // Initialize message manager.
+    EnsureMessageManager();
   } else {
     mRemoteBrowser->Move(size);
   }
 
   return true;
 }
 #endif
 
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -229,17 +229,17 @@ nsFrameMessageManager::SendSyncMessage()
   NS_ASSERTION(!IsWindowLevel(), "Should not call SendSyncMessage in chrome");
   NS_ASSERTION(!mParentManager, "Should not have parent manager in content!");
   if (mSyncCallback) {
     NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
     nsString messageName;
     nsString json;
     nsresult rv = GetParamsForMessage(messageName, json);
     NS_ENSURE_SUCCESS(rv, rv);
-    nsTArray<nsString> retval;
+    InfallibleTArray<nsString> retval;
     if (mSyncCallback(mCallbackData, messageName, json, &retval)) {
       nsAXPCNativeCallContext* ncc = nsnull;
       rv = nsContentUtils::XPConnect()->GetCurrentNativeCallContext(&ncc);
       NS_ENSURE_SUCCESS(rv, rv);
       NS_ENSURE_STATE(ncc);
 
       JSContext* ctx = nsnull;
       rv = ncc->GetJSContext(&ctx);
@@ -321,17 +321,17 @@ nsFrameMessageManager::GetDocShell(nsIDo
   return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
                                       const nsAString& aMessage,
                                       PRBool aSync, const nsAString& aJSON,
                                       JSObject* aObjectsArray,
-                                      nsTArray<nsString>* aJSONRetVal,
+                                      InfallibleTArray<nsString>* aJSONRetVal,
                                       JSContext* aContext)
 {
   JSContext* ctx = mContext ? mContext : aContext;
   if (!ctx) {
     nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(&ctx);
   }
   if (mListeners.Length()) {
     nsCOMPtr<nsIAtom> name = do_GetAtom(aMessage);
@@ -737,17 +737,17 @@ bool SendAsyncMessageToChildProcess(void
     return cp->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
   }
   return true;
 }
 
 bool SendSyncMessageToParentProcess(void* aCallbackData,
                                     const nsAString& aMessage,
                                     const nsAString& aJSON,
-                                    nsTArray<nsString>* aJSONRetVal)
+                                    InfallibleTArray<nsString>* aJSONRetVal)
 {
   mozilla::dom::ContentChild* cc =
     mozilla::dom::ContentChild::GetSingleton();
   if (cc) {
     return
       cc->SendSyncMessage(nsString(aMessage), nsString(aJSON), aJSONRetVal);
   }
   return true;
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -61,17 +61,17 @@ struct nsMessageListenerInfo
   nsCOMPtr<nsIFrameMessageListener> mListener;
   nsCOMPtr<nsIAtom> mMessage;
 };
 
 typedef bool (*nsLoadScriptCallback)(void* aCallbackData, const nsAString& aURL);
 typedef bool (*nsSyncMessageCallback)(void* aCallbackData,
                                       const nsAString& aMessage,
                                       const nsAString& aJSON,
-                                      nsTArray<nsString>* aJSONRetVal);
+                                      InfallibleTArray<nsString>* aJSONRetVal);
 typedef bool (*nsAsyncMessageCallback)(void* aCallbackData,
                                        const nsAString& aMessage,
                                        const nsAString& aJSON);
 
 class nsFrameMessageManager : public nsIContentFrameMessageManager,
                               public nsIChromeFrameMessageManager
 {
 public:
@@ -124,17 +124,17 @@ public:
   NS_DECL_NSIFRAMEMESSAGEMANAGER
   NS_DECL_NSISYNCMESSAGESENDER
   NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
   NS_DECL_NSICHROMEFRAMEMESSAGEMANAGER
 
   nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage,
                           PRBool aSync, const nsAString& aJSON,
                           JSObject* aObjectsArray,
-                          nsTArray<nsString>* aJSONRetVal,
+                          InfallibleTArray<nsString>* aJSONRetVal,
                           JSContext* aContext = nsnull);
   void AddChildManager(nsFrameMessageManager* aManager,
                        PRBool aLoadScripts = PR_TRUE);
   void RemoveChildManager(nsFrameMessageManager* aManager)
   {
     mChildManagers.RemoveObject(aManager);
   }
 
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -49,17 +49,17 @@
 #include "nsScriptLoader.h"
 #include "nsIJSContextStack.h"
 #include "nsFrameLoader.h"
 #include "nsIPrivateDOMEvent.h"
 
 bool SendSyncMessageToParent(void* aCallbackData,
                              const nsAString& aMessage,
                              const nsAString& aJSON,
-                             nsTArray<nsString>* aJSONRetVal)
+                             InfallibleTArray<nsString>* aJSONRetVal)
 {
   nsInProcessTabChildGlobal* tabChild =
     static_cast<nsInProcessTabChildGlobal*>(aCallbackData);
   nsCOMPtr<nsIContent> owner = tabChild->mOwner;
   nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
   asyncMessages.SwapElements(tabChild->mASyncMessages);
   PRUint32 len = asyncMessages.Length();
   for (PRUint32 i = 0; i < len; ++i) {
--- a/content/base/src/nsScriptElement.cpp
+++ b/content/base/src/nsScriptElement.cpp
@@ -10,17 +10,17 @@
  * 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 Code.
  *
  * The Initial Developer of the Original Code is
- * Mozilla Corporation.
+ * the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2006
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jonas Sicking <jonas@sicking.cc> (original developer)
  *
  * 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
@@ -141,65 +141,35 @@ void
 nsScriptElement::ContentInserted(nsIDocument *aDocument,
                                  nsIContent* aContainer,
                                  nsIContent* aChild,
                                  PRInt32 aIndexInContainer)
 {
   MaybeProcessScript();
 }
 
-static PRBool
-InNonScriptingContainer(nsIContent* aNode)
-{
-  aNode = aNode->GetParent();
-  while (aNode) {
-    // XXX noframes and noembed are currently unconditionally not
-    // displayed and processed. This might change if we support either
-    // prefs or per-document container settings for not allowing
-    // frames or plugins.
-    if (aNode->IsHTML()) {
-      nsIAtom *localName = aNode->Tag();
-      if (localName == nsGkAtoms::iframe ||
-          localName == nsGkAtoms::noframes ||
-          localName == nsGkAtoms::noembed) {
-        return PR_TRUE;
-      }
-    }
-    aNode = aNode->GetParent();
-  }
-
-  return PR_FALSE;
-}
-
 nsresult
 nsScriptElement::MaybeProcessScript()
 {
   nsCOMPtr<nsIContent> cont =
     do_QueryInterface((nsIScriptElement*) this);
 
   NS_ASSERTION(cont->DebugGetSlots()->mMutationObservers.Contains(this),
                "You forgot to add self as observer");
 
   if (mAlreadyStarted || !mDoneAddingChildren || !cont->IsInDoc() ||
       mMalformed || !HasScriptContent()) {
     return NS_OK;
   }
 
   FreezeUriAsyncDefer();
 
-  if (InNonScriptingContainer(cont)) {
-    // Make sure to flag ourselves as evaluated
-    mAlreadyStarted = PR_TRUE;
-    return NS_OK;
-  }
-
-  nsresult scriptresult = NS_OK;
   nsRefPtr<nsScriptLoader> loader = cont->GetOwnerDoc()->ScriptLoader();
   mAlreadyStarted = PR_TRUE;
-  scriptresult = loader->ProcessScriptElement(this);
+  nsresult scriptresult = loader->ProcessScriptElement(this);
 
   // The only error we don't ignore is NS_ERROR_HTMLPARSER_BLOCK
   // However we don't want to override other success values
   // (such as NS_CONTENT_SCRIPT_IS_EVENTHANDLER)
   if (NS_FAILED(scriptresult) &&
       scriptresult != NS_ERROR_HTMLPARSER_BLOCK) {
     scriptresult = NS_OK;
   }
--- a/content/base/test/test_bug453736.html
+++ b/content/base/test/test_bug453736.html
@@ -8,62 +8,28 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/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=453736">Mozilla Bug 453736</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-
 </div>
 <pre id="test">
 <script type="application/javascript">
-
 /** Test for Bug 453736 **/
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   const scriptCreationFuncs = [
     function() { return document.createElement("script"); },
     function() { return document.createElementNS("http://www.w3.org/2000/svg", "script"); }
   ];
 
-  const noScriptContainers = ["iframe", "noframes", "noembed"];
-  for (var i = 0; i < noScriptContainers.length; ++i) {
-    for each (var func in scriptCreationFuncs) {
-      var cont = noScriptContainers[i];
-      var node = document.createElement(cont);
-      document.body.appendChild(node);
-      var s = func();
-      s.setAttribute("type", "application/javascript");
-      s.appendChild(document.createTextNode('window["'+cont+'ScriptRan"] = true'));
-
-      window[cont+"ScriptRan"] = false;
-      document.body.appendChild(s.cloneNode(true));
-      is(window[cont+"ScriptRan"], true,
-         "Clone of non-inserted script created with " + func +" should run");
-   
-      window[cont+"ScriptRan"] = false;
-      node.appendChild(s);
-      is(window[cont+"ScriptRan"], false,
-         "Script created with " + func +" shouldn't run when inserting in <"+cont+">");
-
-      window[cont+"ScriptRan"] = false;
-      document.body.appendChild(s);
-      is(window[cont+"ScriptRan"], false,
-         "Script created with " + func + " shouldn't run when moving out of <"+cont+">");
-
-      window[cont+"ScriptRan"] = false;
-      document.body.appendChild(s.cloneNode(true));
-      is(window[cont+"ScriptRan"], false,
-         "Clone of script inside <" + cont + "> created with " + func + " shouldn't run");
-    }
-  }
-
-  const scriptContainers = ["div"];
+  const scriptContainers = ["div", "iframe", "noframes", "noembed"];
   for (var i = 0; i < scriptContainers.length; ++i) {
     for each (var func in scriptCreationFuncs) {
       var cont = scriptContainers[i];
       var node = document.createElement(cont);
       document.body.appendChild(node);
       var s = func();
       s.setAttribute("type", "application/javascript");
       s.appendChild(document.createTextNode('window["'+cont+'ScriptRan"] = true'));
@@ -82,16 +48,12 @@ addLoadEvent(function() {
       document.body.appendChild(s.cloneNode(true));
       is(window[cont+"ScriptRan"], false,
          "Clone of script inside <" + cont + "> created with " + func + " shouldn't run");
     }
   }
 
   SimpleTest.finish();
 });
-
-
-
-
 </script>
 </pre>
 </body>
 </html>
--- a/content/canvas/src/DocumentRendererChild.cpp
+++ b/content/canvas/src/DocumentRendererChild.cpp
@@ -59,62 +59,28 @@
 using namespace mozilla::ipc;
 
 DocumentRendererChild::DocumentRendererChild()
 {}
 
 DocumentRendererChild::~DocumentRendererChild()
 {}
 
-static void
-FlushLayoutForTree(nsIDOMWindow* aWindow)
-{
-    nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
-    if (!piWin)
-        return;
-
-    // Note that because FlushPendingNotifications flushes parents, this
-    // is O(N^2) in docshell tree depth.  However, the docshell tree is
-    // usually pretty shallow.
-
-    nsCOMPtr<nsIDOMDocument> domDoc;
-    aWindow->GetDocument(getter_AddRefs(domDoc));
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
-    if (doc) {
-        doc->FlushPendingNotifications(Flush_Layout);
-    }
-
-    nsCOMPtr<nsIDocShellTreeNode> node =
-        do_QueryInterface(piWin->GetDocShell());
-    if (node) {
-        PRInt32 i = 0, i_end;
-        node->GetChildCount(&i_end);
-        for (; i < i_end; ++i) {
-            nsCOMPtr<nsIDocShellTreeItem> item;
-            node->GetChildAt(i, getter_AddRefs(item));
-            nsCOMPtr<nsIDOMWindow> win = do_GetInterface(item);
-            if (win) {
-                FlushLayoutForTree(win);
-            }
-        }
-    }
-}
-
 bool
 DocumentRendererChild::RenderDocument(nsIDOMWindow *window,
                                       const nsRect& documentRect,
                                       const gfxMatrix& transform,
                                       const nsString& bgcolor,
                                       PRUint32 renderFlags,
                                       PRBool flushLayout, 
                                       const nsIntSize& renderSize,
                                       nsCString& data)
 {
     if (flushLayout)
-        FlushLayoutForTree(window);
+        nsContentUtils::FlushLayoutForTree(window);
 
     nsCOMPtr<nsPresContext> presContext;
     nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
     if (win) {
         nsIDocShell* docshell = win->GetDocShell();
         if (docshell) {
             docshell->GetPresContext(getter_AddRefs(presContext));
         }
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -3557,51 +3557,16 @@ nsCanvasRenderingContext2D::GetGlobalCom
     else CANVAS_OP_TO_THEBES_OP("xor", XOR)
     else return NS_ERROR_FAILURE;
 
 #undef CANVAS_OP_TO_THEBES_OP
 
     return NS_OK;
 }
 
-
-static void
-FlushLayoutForTree(nsIDOMWindow* aWindow)
-{
-    nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
-    if (!piWin)
-        return;
-
-    // Note that because FlushPendingNotifications flushes parents, this
-    // is O(N^2) in docshell tree depth.  However, the docshell tree is
-    // usually pretty shallow.
-
-    nsCOMPtr<nsIDOMDocument> domDoc;
-    aWindow->GetDocument(getter_AddRefs(domDoc));
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
-    if (doc) {
-        doc->FlushPendingNotifications(Flush_Layout);
-    }
-
-    nsCOMPtr<nsIDocShellTreeNode> node =
-        do_QueryInterface(piWin->GetDocShell());
-    if (node) {
-        PRInt32 i = 0, i_end;
-        node->GetChildCount(&i_end);
-        for (; i < i_end; ++i) {
-            nsCOMPtr<nsIDocShellTreeItem> item;
-            node->GetChildAt(i, getter_AddRefs(item));
-            nsCOMPtr<nsIDOMWindow> win = do_GetInterface(item);
-            if (win) {
-                FlushLayoutForTree(win);
-            }
-        }
-    }
-}
-
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY,
                                        float aW, float aH,
                                        const nsAString& aBGColor,
                                        PRUint32 flags)
 {
     NS_ENSURE_ARG(aWindow != nsnull);
 
@@ -3620,17 +3585,17 @@ nsCanvasRenderingContext2D::DrawWindow(n
     if (!nsContentUtils::IsCallerTrustedForRead()) {
       // not permitted to use DrawWindow
       // XXX ERRMSG we need to report an error to developers here! (bug 329026)
         return NS_ERROR_DOM_SECURITY_ERR;
     }
 
     // Flush layout updates
     if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH))
-        FlushLayoutForTree(aWindow);
+        nsContentUtils::FlushLayoutForTree(aWindow);
 
     nsRefPtr<nsPresContext> presContext;
     nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
     if (win) {
         nsIDocShell* docshell = win->GetDocShell();
         if (docshell) {
             docshell->GetPresContext(getter_AddRefs(presContext));
         }
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -368,18 +368,17 @@ nsHTMLCanvasElement::MozGetAsFileImpl(co
 }
 
 nsresult
 nsHTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
                                       nsICanvasRenderingContextInternal **aContext)
 {
   NS_ENSURE_ARG(aContext);
 
-  nsCString ctxId;
-  ctxId.Assign(NS_LossyConvertUTF16toASCII(aContextId));
+  NS_LossyConvertUTF16toASCII ctxId(aContextId);
 
   // check that ctxId is clamped to A-Za-z0-9_-
   for (PRUint32 i = 0; i < ctxId.Length(); i++) {
     if ((ctxId[i] < 'A' || ctxId[i] > 'Z') &&
         (ctxId[i] < 'a' || ctxId[i] > 'z') &&
         (ctxId[i] < '0' || ctxId[i] > '9') &&
         (ctxId[i] != '-') &&
         (ctxId[i] != '_'))
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -16,16 +16,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 1998
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Pierre Phaneuf <pp@ludusdesign.com>
+ *   Geoff Lankow <geoff@darktrojan.net>
  *
  * 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
@@ -535,16 +536,19 @@ UploadLastDir::FetchLastUsedDirectory(ns
 
 nsresult
 UploadLastDir::StoreLastUsedDirectory(nsIURI* aURI, nsILocalFile* aFile)
 {
   NS_PRECONDITION(aURI, "aURI is null");
   NS_PRECONDITION(aFile, "aFile is null");
   nsCOMPtr<nsIFile> parentFile;
   aFile->GetParent(getter_AddRefs(parentFile));
+  if (!parentFile) {
+    return NS_OK;
+  }
   nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(parentFile);
 
   // Store the data in memory instead of the CPS during private browsing mode
   if (mInPrivateBrowsing) {
     nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService =
       do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID);
     if (!hostnameGrouperService)
       return NS_ERROR_NOT_AVAILABLE;
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -1212,18 +1212,17 @@ nsHTMLSelectElement::SetValue(const nsAS
   return rv;
 }
 
 
 NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Autofocus, autofocus)
 NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Disabled, disabled)
 NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Multiple, multiple)
 NS_IMPL_STRING_ATTR(nsHTMLSelectElement, Name, name)
-NS_IMPL_POSITIVE_INT_ATTR_DEFAULT_VALUE(nsHTMLSelectElement, Size, size,
-                                        GetDefaultSize())
+NS_IMPL_POSITIVE_INT_ATTR_DEFAULT_VALUE(nsHTMLSelectElement, Size, size, 0)
 NS_IMPL_INT_ATTR(nsHTMLSelectElement, TabIndex, tabindex)
 
 NS_IMETHODIMP
 nsHTMLSelectElement::Blur()
 {
   return nsGenericHTMLElement::Blur();
 }
 
--- a/content/html/content/src/nsHTMLSelectElement.h
+++ b/content/html/content/src/nsHTMLSelectElement.h
@@ -490,24 +490,16 @@ protected:
   void VerifyOptionsArray();
 #endif
 
   virtual PRBool AcceptAutofocus() const
   {
     return PR_TRUE;
   }
 
-  /**
-   * Helper method to get the default size.
-   */
-  PRInt32 GetDefaultSize() const
-  {
-    return HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ? 4 : 1;
-  }
-
   /** The options[] array */
   nsRefPtr<nsHTMLOptionCollection> mOptions;
   /** false if the parser is in the middle of adding children. */
   PRPackedBool    mIsDoneAddingChildren;
   /** true if our disabled state has changed from the default **/
   PRPackedBool    mDisabledChanged;
   /** true if child nodes are being added or removed.
    *  Used by nsSafeOptionListMutation.
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -235,12 +235,13 @@ include $(topsrcdir)/config/rules.mk
 		test_bug596350.html \
 		test_bug600155.html \
 		test_bug556007.html \
 		test_bug606817.html \
 		test_bug297761.html \
 		file_bug297761.html \
 		test_bug607145.html \
 		test_bug601061.html \
+		reflect.js \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
copy from content/html/content/test/test_bug601061.html
copy to content/html/content/test/reflect.js
--- a/content/html/content/test/test_bug601061.html
+++ b/content/html/content/test/reflect.js
@@ -1,27 +1,12 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=601061
--->
-<head>
-  <title>Test for Bug 601061</title>
-  <script type="application/javascript" src="/MochiKit/packed.js"></script>
-  <script type="application/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=601061">Mozilla Bug 601061</a>
-<p id="display"></p>
-<pre id="test">
-<script type="application/javascript">
-
-/** Test for Bug 601061 **/
-
+/**
+ * Checks that a given attribute name for a given element is correctly reflected
+ * as an unsigned int.
+ */
 function reflectUnsignedInt(aElement, aAttr, aNonNull, aDefault)
 {
   function checkGetter(aElement, aAttr, aValue)
   {
     is(aElement[aAttr], aValue, "." + aAttr + " should be equals " + aValue);
     is(aElement.getAttribute(aAttr), aValue,
        "@" + aAttr + " should be equals " + aValue);
   }
@@ -103,16 +88,8 @@ function reflectUnsignedInt(aElement, aA
   is(aElement.getAttribute(aAttr), 0, "@" + aAttr + " should be equals to 0");
   if (aNonNull) {
     is(aElement[aAttr], aDefault,
        "." + aAttr + " should be equals to " + aDefault);
   } else {
     is(aElement[aAttr], 0, "." + aAttr + " should be equals to 0");
   }
 }
-
-var input = document.createElement("input");
-reflectUnsignedInt(input, "size", true, 20);
-
-</script>
-</pre>
-</body>
-</html>
--- a/content/html/content/test/test_bug551846.html
+++ b/content/html/content/test/test_bug551846.html
@@ -117,43 +117,43 @@ function checkSetSizeException(element)
     caught = true;
   }
   ok(!caught, "Setting an invalid size in the content attribute shouldn't throw an exception");
 
   // reverting to defalut
   element.removeAttribute('size');
 }
 
-function checkSizeWhenChangeMultiple(element)
+function checkSizeWhenChangeMultiple(element, aDefaultNonMultiple, aDefaultMultiple)
 {
   s.setAttribute('size', -1)
-  is(s.size, 1, "Size IDL attribute should be 1");
+  is(s.size, aDefaultNonMultiple, "Size IDL attribute should be 1");
 
   s.multiple = true;
-  is(s.size, 4, "Size IDL attribute should be 4");
+  is(s.size, aDefaultMultiple, "Size IDL attribute should be 4");
 
   is(s.getAttribute('size'), -1, "Size content attribute should be -1");
 
   s.setAttribute('size', -2);
-  is(s.size, 4, "Size IDL attribute should be 4");
+  is(s.size, aDefaultMultiple, "Size IDL attribute should be 4");
 
   s.multiple = false;
-  is(s.size, 1, "Size IDL attribute should be 1");
+  is(s.size, aDefaultNonMultiple, "Size IDL attribute should be 1");
 
   is(s.getAttribute('size'), -2, "Size content attribute should be -2");
 }
 
 var s = document.getElementById('s');
 
-checkSizeReflection(s, 1);
+checkSizeReflection(s, 0);
 checkSetSizeException(s);
 
 s.setAttribute('multiple', 'true');
-checkSizeReflection(s, 4);
+checkSizeReflection(s, 0);
 checkSetSizeException(s);
 s.removeAttribute('multiple');
 
-checkSizeWhenChangeMultiple(s);
+checkSizeWhenChangeMultiple(s, 0, 0);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/content/test/test_bug601061.html
+++ b/content/html/content/test/test_bug601061.html
@@ -2,117 +2,25 @@
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=601061
 -->
 <head>
   <title>Test for Bug 601061</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="reflect.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=601061">Mozilla Bug 601061</a>
 <p id="display"></p>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 601061 **/
 
-function reflectUnsignedInt(aElement, aAttr, aNonNull, aDefault)
-{
-  function checkGetter(aElement, aAttr, aValue)
-  {
-    is(aElement[aAttr], aValue, "." + aAttr + " should be equals " + aValue);
-    is(aElement.getAttribute(aAttr), aValue,
-       "@" + aAttr + " should be equals " + aValue);
-  }
-
-  if (!aDefault) {
-    if (aNonNull) {
-      aDefault = 1;
-    } else {
-      aDefault = 0;
-    }
-  }
-
-  // Check default value.
-  is(aElement[aAttr], aDefault, "default value should be " + aDefault);
-  ok(!aElement.hasAttribute(aAttr), aAttr + " shouldn't be present");
-
-  var values = [ 1, 3, 42, 2147483647 ];
-
-  for each (var value in values) {
-    aElement[aAttr] = value;
-    checkGetter(aElement, aAttr, value);
-  }
-
-  for each (var value in values) {
-    aElement.setAttribute(aAttr, value);
-    checkGetter(aElement, aAttr, value);
-  }
-
-  // -3000000000 is equivalent to 1294967296 when using the IDL attribute.
-  aElement[aAttr] = -3000000000;
-  checkGetter(aElement, aAttr, 1294967296);
-  // When setting the content atribute, it's a string so it will be unvalid.
-  aElement.setAttribute(aAttr, -3000000000);
-  is(aElement.getAttribute(aAttr), -3000000000,
-     "@" + aAttr + " should be equals to " + -3000000000);
-  is(aElement[aAttr], aDefault,
-     "." + aAttr + " should be equals to " + aDefault);
-
-  var nonValidValues = [
-    /* invalid value, value in the unsigned int range */
-    [ -2147483648, 2147483648 ],
-    [ -1,          4294967295 ],
-    [ 3147483647,  3147483647 ],
-  ];
-
-  for each (var values in nonValidValues) {
-    aElement[aAttr] = values[0];
-    is(aElement.getAttribute(aAttr), values[1],
-       "@" + aAttr + " should be equals to " + values[1]);
-    is(aElement[aAttr], aDefault,
-       "." + aAttr + " should be equals to " + aDefault);
-  }
-
-  for each (var values in nonValidValues) {
-    aElement.setAttribute(aAttr, values[0]);
-    is(aElement.getAttribute(aAttr), values[0],
-       "@" + aAttr + " should be equals to " + values[0]);
-    is(aElement[aAttr], aDefault,
-       "." + aAttr + " should be equals to " + aDefault);
-  }
-
-  // Setting to 0 should throw an error if aNonNull is true.
-  var caught = false;
-  try {
-    aElement[aAttr] = 0;
-  } catch(e) {
-    caught = true;
-    is(e.code, DOMException.INDEX_SIZE_ERR, "exception should be INDEX_SIZE_ERR");
-  }
-
-  if (aNonNull) {
-    ok(caught, "an exception should have been caught");
-  } else {
-    ok(!caught, "no exception should have been caught");
-  }
-
-  // If 0 is set in @aAttr, it will be ignored when calling .aAttr.
-  aElement.setAttribute(aAttr, 0);
-  is(aElement.getAttribute(aAttr), 0, "@" + aAttr + " should be equals to 0");
-  if (aNonNull) {
-    is(aElement[aAttr], aDefault,
-       "." + aAttr + " should be equals to " + aDefault);
-  } else {
-    is(aElement[aAttr], 0, "." + aAttr + " should be equals to 0");
-  }
-}
-
-var input = document.createElement("input");
-reflectUnsignedInt(input, "size", true, 20);
+reflectUnsignedInt(document.createElement("input"), "size", true, 20);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_timeupdate_seek.html
+++ b/content/media/test/test_timeupdate_seek.html
@@ -15,59 +15,64 @@
 // WHATWG spec.
 //
 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#seeking
 
 var manager = new MediaTestManager;
 
 function do_loadedmetadata(e) {
   var v = e.target;
-  if (e._finished)
+  if (v._finished)
     return false;
 
-  v._seekTime = Math.round(v.duration / 2);
-  v.currentTime=v._seekTime;
+  var duration = v.duration;
+  v._seekTime = Math.round(duration / 2);
+  ok(!isNaN(duration), v._name + ": duration must be non NaN");
+  ok(v._seekTime >= 0.0, v._name + ": must have non-negative seek target");
+  ok(!isNaN(v._seekTime), v._name + ": seek target must be non NaN");
+  v.currentTime = v._seekTime;
   return false;
 }
 
 function do_seeking(e) {
   var v = e.target;
   if (v._finished)
     return false;
 
   v._seeking = true;
   ok(v.currentTime == v._seekTime,
-     "Check currentTime of " + v.currentTime +
-     " is requested seek time of " + v._seekTime +
-     " in " + v._name);
+      v._name + ": currentTime of " + v.currentTime +
+     " should be requested seek time of " + v._seekTime);
   return false;
 }
 
 function do_timeupdate(e) {
   var v = e.target;
   if (v._finished)
     return false;
 
   ok(v.currentTime == v._seekTime,
-     "Check currentTime of " + v.currentTime +
-     " is requested seek time of " + v._seekTime +
-     " in " + v._name);
-  v._finished= true;
+      v._name + ": currentTime of " + v.currentTime +
+     " should be  requested seek time of " + v._seekTime);
+  v._finished = true;
   v.pause();
   v.parentNode.removeChild(v);
+  v.src = "";
+  ok(true, v._name + ": finished");
   manager.finished(v.token);
   return false;
 }
 
 function startTest(test, token) {
   var type = /^video/.test(test.type) ? "video" : "audio";
   var v = document.createElement(type);
   v.token = token;
   manager.started(token);
   v.src = test.name;
+  ok(true, test.name + ": started");
   v._name = test.name;
   v._seeking = false;
   v._finished = false;
   v.addEventListener("loadedmetadata", do_loadedmetadata, false);
   v.addEventListener("seeking", do_seeking, false);
   v.addEventListener("timeupdate", do_timeupdate, false);
   document.body.appendChild(v);
 }
new file mode 100644
--- /dev/null
+++ b/content/smil/crashtests/572938-1.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <text id="myText">Used Text Element
+      <set attributeName="display" to="none"/>
+    </text>
+  </defs>
+  <use xlink:href="#myText" x="20" y="40"/>
+  <text x="20" y="60">Normal Text Element
+    <set attributeName="display" to="none"/>
+  </text>
+</svg>
new file mode 100644
--- /dev/null
+++ b/content/smil/crashtests/572938-2.svg
@@ -0,0 +1,22 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink"
+  class="reftest-wait">
+
+    <script>
+    function boom()
+    {
+      document.getElementById("circleID").removeChild(
+        document.getElementById("at"));
+      document.documentElement.removeAttribute("class");
+    }
+    window.addEventListener("load", boom, false);
+    </script>
+
+    <circle id="circleID">
+        <animate/>
+        <animateTransform id="at" attributeName="transform"/>
+    </circle>
+    <animate attributeName="stroke-width"/>
+    <use xlink:href="#circleID"/>
+
+</svg>
new file mode 100644
--- /dev/null
+++ b/content/smil/crashtests/572938-3.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <text id="a">Text A</text>
+    <text id="b">Text B</text>
+  </defs>
+  <use xlink:href="#a" x="20" y="40">
+    <set attributeName="xlink:href" to="#b" dur="2s"/>
+  </use>
+</svg>
new file mode 100644
--- /dev/null
+++ b/content/smil/crashtests/572938-4.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="a">
+ <path d=""><animate/></path>
+</g>
+<g display="none">
+<use xlink:href="#a" x="80"/>
+<set attributeName="display" to="inline"/>
+</g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/content/smil/crashtests/605345-1.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
+<script>
+<![CDATA[
+
+function boom()
+{
+  var anim = document.getElementById("a");
+  var newSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+  var oldSvg = document.removeChild(document.documentElement);
+  document.appendChild(newSvg);
+  document.removeChild(document.documentElement);
+  newSvg.pauseAnimations();
+  document.appendChild(newSvg);
+  newSvg.appendChild(anim);
+
+  oldSvg.removeAttribute("class");
+}
+
+window.addEventListener("load", function() { setTimeout(boom, 200); }, false);
+
+]]>
+</script>
+<animate id="a"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/content/smil/crashtests/608549-1.svg
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
+<script>
+<![CDATA[
+
+function boom()
+{
+  try {
+    document.getElementById("set").beginElementAt(NaN);
+    return;
+  } catch (e) {}
+  try {
+    document.getElementById("set").endElementAt(NaN);
+    return;
+  } catch (e) {}
+
+  // If we got here we threw both exceptions and skipped both early-returns, as
+  // expected.
+  document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("load", boom, false);
+
+]]>
+</script>
+
+<set id="set" attributeName="fill" to="green" begin="indefinite"/>
+
+</svg>
--- a/content/smil/crashtests/crashtests.list
+++ b/content/smil/crashtests/crashtests.list
@@ -11,16 +11,22 @@ load 541297-1.svg
 load 547333-1.svg
 load 548899-1.svg
 load 551620-1.svg
 load 554202-1.svg
 load 554202-2.svg
 load 554141-1.svg
 load 555026-1.svg
 load 556841-1.svg
+load 572938-1.svg
+load 572938-2.svg
+load 572938-3.svg
+load 572938-4.svg
 load 588287-1.svg
 load 588287-2.svg
 load 590425-1.html
 load 592477-1.xhtml
 load 594653-1.svg
 load 596796-1.svg
+load 605345-1.svg
 load 606101-1.svg
+load 608549-1.svg
 load 608295-1.html
--- a/content/smil/nsSMILAnimationController.cpp
+++ b/content/smil/nsSMILAnimationController.cpp
@@ -69,19 +69,17 @@ GetRefreshDriverForDoc(nsIDocument* aDoc
 }
 
 //----------------------------------------------------------------------
 // ctors, dtors, factory methods
 
 nsSMILAnimationController::nsSMILAnimationController()
   : mResampleNeeded(PR_FALSE),
     mDeferredStartSampling(PR_FALSE),
-#ifdef DEBUG
     mRunningSample(PR_FALSE),
-#endif
     mDocument(nsnull)
 {
   mAnimationElementTable.Init();
   mChildContainerTable.Init();
 }
 
 nsSMILAnimationController::~nsSMILAnimationController()
 {
@@ -184,17 +182,16 @@ nsSMILAnimationController::WillRefresh(m
 
 //----------------------------------------------------------------------
 // Animation element registration methods:
 
 void
 nsSMILAnimationController::RegisterAnimationElement(
                                   nsISMILAnimationElement* aAnimationElement)
 {
-  NS_ASSERTION(!mRunningSample, "Registering content during sample.");
   mAnimationElementTable.PutEntry(aAnimationElement);
   if (mDeferredStartSampling) {
     mDeferredStartSampling = PR_FALSE;
     if (mChildContainerTable.Count()) {
       // mAnimationElementTable was empty, but now we've added its 1st element
       NS_ABORT_IF_FALSE(mAnimationElementTable.Count() == 1,
                         "we shouldn't have deferred sampling if we already had "
                         "animations registered");
@@ -202,17 +199,16 @@ nsSMILAnimationController::RegisterAnima
     } // else, don't sample until a time container is registered (via AddChild)
   }
 }
 
 void
 nsSMILAnimationController::UnregisterAnimationElement(
                                   nsISMILAnimationElement* aAnimationElement)
 {
-  NS_ASSERTION(!mRunningSample, "Unregistering content during sample.");
   mAnimationElementTable.RemoveEntry(aAnimationElement);
 }
 
 //----------------------------------------------------------------------
 // Page show/hide
 
 void
 nsSMILAnimationController::OnPageShow()
@@ -357,26 +353,21 @@ void
 nsSMILAnimationController::DoSample()
 {
   DoSample(PR_TRUE); // Skip unchanged time containers
 }
 
 void
 nsSMILAnimationController::DoSample(PRBool aSkipUnchangedContainers)
 {
-  // Reset resample flag -- do this before flushing styles since flushing styles
-  // will also flush animation resample requests
   mResampleNeeded = PR_FALSE;
+  // Set running sample flag -- do this before flushing styles so that when we
+  // flush styles we don't end up requesting extra samples
+  mRunningSample = PR_TRUE;
   mDocument->FlushPendingNotifications(Flush_Style);
-#ifdef DEBUG
-  mRunningSample = PR_TRUE;
-#endif
-  // Reset resample flag again -- flushing styles may have set this flag but
-  // since we're about to do a sample now, reset it
-  mResampleNeeded = PR_FALSE;
 
   // STEP 1: Bring model up to date
   // (i)  Rewind elements where necessary
   // (ii) Run milestone samples
   RewindElements();
   DoMilestoneSamples();
 
   // STEP 2: Sample the child time containers
@@ -440,19 +431,17 @@ nsSMILAnimationController::DoSample(PRBo
   }
 
   // STEP 5: Compose currently-animated attributes.
   // XXXdholbert: This step traverses our animation targets in an effectively
   // random order. For animation from/to 'inherit' values to work correctly
   // when the inherited value is *also* being animated, we really should be
   // traversing our animated nodes in an ancestors-first order (bug 501183)
   currentCompositorTable->EnumerateEntries(DoComposeAttribute, nsnull);
-#ifdef DEBUG
   mRunningSample = PR_FALSE;
-#endif
 
   // Update last compositor table
   mLastCompositorTable = currentCompositorTable.forget();
 
   NS_ASSERTION(!mResampleNeeded, "Resample dirty flag set during sample!");
 }
 
 void
--- a/content/smil/nsSMILAnimationController.h
+++ b/content/smil/nsSMILAnimationController.h
@@ -89,17 +89,22 @@ public:
   // Methods for registering and enumerating animation elements
   void RegisterAnimationElement(nsISMILAnimationElement* aAnimationElement);
   void UnregisterAnimationElement(nsISMILAnimationElement* aAnimationElement);
 
   // Methods for resampling all animations
   // (A resample performs the same operations as a sample but doesn't advance
   // the current time and doesn't check if the container is paused)
   void Resample() { DoSample(PR_FALSE); }
-  void SetResampleNeeded() { mResampleNeeded = PR_TRUE; }
+  void SetResampleNeeded()
+  {
+    if (!mRunningSample) {
+      mResampleNeeded = PR_TRUE;
+    }
+  }
   void FlushResampleRequests()
   {
     if (!mResampleNeeded)
       return;
 
     Resample();
   }
 
@@ -198,19 +203,17 @@ protected:
   TimeContainerHashtable     mChildContainerTable;
   mozilla::TimeStamp         mCurrentSampleTime;
   mozilla::TimeStamp         mStartTime;
   PRPackedBool               mResampleNeeded;
   // If we're told to start sampling but there are no animation elements we just
   // record the time, set the following flag, and then wait until we have an
   // animation element. Then we'll reset this flag and actually start sampling.
   PRPackedBool               mDeferredStartSampling;
-#ifdef DEBUG
   PRPackedBool               mRunningSample;
-#endif
 
   // Store raw ptr to mDocument.  It owns the controller, so controller
   // shouldn't outlive it
   nsIDocument* mDocument;
 
   // Contains compositors used in our last sample.  We keep this around
   // so we can detect when an element/attribute used to be animated,
   // but isn't anymore for some reason. (e.g. if its <animate> element is
--- a/content/smil/nsSMILAnimationFunction.cpp
+++ b/content/smil/nsSMILAnimationFunction.cpp
@@ -241,17 +241,17 @@ nsSMILAnimationFunction::ComposeResult(c
 
   // If this interval is active, we must have a non-negative mSampleTime
   NS_ABORT_IF_FALSE(mSampleTime >= 0 || !mIsActive,
       "Negative sample time for active animation");
   NS_ABORT_IF_FALSE(mSimpleDuration.IsResolved() ||
       mSimpleDuration.IsIndefinite() || mLastValue,
       "Unresolved simple duration for active or frozen animation");
 
-  nsSMILValue result(aResult.mType);
+  nsSMILValue result;
 
   if (mSimpleDuration.IsIndefinite() ||
       (values.Length() == 1 && TreatSingleValueAsStatic())) {
     // Indefinite duration or only one value set: Always set the first value
     result = values[0];
 
   } else if (mLastValue) {
 
@@ -397,25 +397,29 @@ nsSMILAnimationFunction::InterpolateResu
   if (calcMode != CALC_DISCRETE) {
     // Get the normalised progress between adjacent values
     const nsSMILValue* from = nsnull;
     const nsSMILValue* to = nsnull;
     // Init to -1 to make sure that if we ever forget to set this, the
     // NS_ABORT_IF_FALSE that tests that intervalProgress is in range will fail.
     double intervalProgress = -1.f;
     if (IsToAnimation()) {
-      from = &aBaseValue;
-      to = &aValues[0];
-      if (calcMode == CALC_PACED) {
-        // Note: key[Times/Splines/Points] are ignored for calcMode="paced"
-        intervalProgress = simpleProgress;
+      if (aBaseValue.IsNull()) {
+        rv = NS_ERROR_FAILURE;
       } else {
-        double scaledSimpleProgress =
-          ScaleSimpleProgress(simpleProgress, calcMode);
-        intervalProgress = ScaleIntervalProgress(scaledSimpleProgress, 0);
+        from = &aBaseValue;
+        to = &aValues[0];
+        if (calcMode == CALC_PACED) {
+          // Note: key[Times/Splines/Points] are ignored for calcMode="paced"
+          intervalProgress = simpleProgress;
+        } else {
+          double scaledSimpleProgress =
+            ScaleSimpleProgress(simpleProgress, calcMode);
+          intervalProgress = ScaleIntervalProgress(scaledSimpleProgress, 0);
+        }
       }
     } else {
       if (calcMode == CALC_PACED) {
         rv = ComputePacedPosition(aValues, simpleProgress,
                                   intervalProgress, from, to);
         // Note: If the above call fails, we'll skip the "from->Interpolate"
         // call below, and we'll drop into the CALC_DISCRETE section
         // instead. (as the spec says we should, because our failure was
--- a/content/smil/nsSMILAnimationFunction.h
+++ b/content/smil/nsSMILAnimationFunction.h
@@ -196,17 +196,17 @@ public:
   /**
    * Indicates if this animation will replace the passed in result rather than
    * adding to it. Animations that replace the underlying value may be called
    * without first calling lower priority animations.
    *
    * @return  True if the animation will replace, false if it will add or
    *          otherwise build on the passed in value.
    */
-  PRBool WillReplace() const;
+  virtual PRBool WillReplace() const;
 
   /**
    * Indicates if the parameters for this animation have changed since the last
    * time it was composited. This allows rendering to be performed only when
    * necessary, particularly when no animations are active.
    *
    * Note that the caller is responsible for determining if the animation
    * target has changed (with help from my UpdateCachedTarget() method).
--- a/content/smil/nsSMILCSSProperty.cpp
+++ b/content/smil/nsSMILCSSProperty.cpp
@@ -97,28 +97,31 @@ nsSMILCSSProperty::nsSMILCSSProperty(nsC
 nsSMILValue
 nsSMILCSSProperty::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 baseValue;
 
-  // SPECIAL CASE: Shorthands
-  if (nsCSSProps::IsShorthand(mPropID)) {
+  // SPECIAL CASE: (a) Shorthands
+  //               (b) 'display'
+  if (nsCSSProps::IsShorthand(mPropID) || mPropID == eCSSProperty_display) {
     // We can't look up the base (computed-style) value of shorthand
-    // properties, because they aren't guaranteed to have a consistent computed
-    // value.  However, that's not a problem, because it turns out the caller
-    // isn't going to end up using the value we return anyway. Base values only
-    // get used when there's interpolation or addition, and the shorthand
-    // properties we know about don't support those operations. So, we can just
-    // return a dummy value (initialized with the right type, so as not to
-    // indicate failure).
-    nsSMILValue tmpVal(&nsSMILCSSValueType::sSingleton);
-    baseValue.Swap(tmpVal);
+    // properties because they aren't guaranteed to have a consistent computed
+    // value.
+    //
+    // Also, although we can look up the base value of the display property,
+    // doing so involves clearing and resetting the property which can cause
+    // frames to be recreated which we'd like to avoid.
+    //
+    // In either case, simply returning a null-typed nsSMILValue to indicate
+    // failure is acceptable because the caller only uses base values when
+    // there's interpolation or addition, and for both the shorthand properties
+    // we know about and the display property those operations aren't supported.
     return baseValue;
   }
 
   // GENERAL CASE: Non-Shorthands
   // (1) Put empty string in override style for property mPropID
   // (saving old override style value, so we can set it again when we're done)
   nsCOMPtr<nsIDOMCSSStyleDeclaration> overrideStyle;
   mElement->GetSMILOverrideStyle(getter_AddRefs(overrideStyle));
--- a/content/smil/nsSMILCompositor.cpp
+++ b/content/smil/nsSMILCompositor.cpp
@@ -103,32 +103,35 @@ nsSMILCompositor::ComposeAttribute()
   nsSMILAnimationFunction::Comparator comparator;
   mAnimationFunctions.Sort(comparator);
 
   // THIRD: Step backwards through animation functions to find out
   // which ones we actually care about.
   PRUint32 firstFuncToCompose = GetFirstFuncToAffectSandwich();
 
   // FOURTH: Get & cache base value
-  nsSMILValue sandwichResultValue = smilAttr->GetBaseValue();
-  if (sandwichResultValue.IsNull()) {
-    NS_WARNING("nsISMILAttr::GetBaseValue failed");
-    return;
+  nsSMILValue sandwichResultValue;
+  if (!mAnimationFunctions[firstFuncToCompose]->WillReplace()) {
+    sandwichResultValue = smilAttr->GetBaseValue();
   }
   UpdateCachedBaseValue(sandwichResultValue);
 
   if (!mForceCompositing) {
     return;
   }
 
   // FIFTH: Compose animation functions
   PRUint32 length = mAnimationFunctions.Length();
   for (PRUint32 i = firstFuncToCompose; i < length; ++i) {
     mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue);
   }
+  if (sandwichResultValue.IsNull()) {
+    smilAttr->ClearAnimValue();
+    return;
+  }
 
   // SIXTH: Set the animated value to the final composited result.
   nsresult rv = smilAttr->SetAnimValue(sandwichResultValue);
   if (NS_FAILED(rv)) {
     NS_WARNING("nsISMILAttr::SetAnimValue failed");
   }
 }
 
--- a/content/smil/nsSMILSetAnimationFunction.cpp
+++ b/content/smil/nsSMILSetAnimationFunction.cpp
@@ -119,8 +119,14 @@ PRBool
 nsSMILSetAnimationFunction::GetAttr(nsIAtom* aAttName,
                                     nsAString& aResult) const
 {
   if (IsDisallowedAttribute(aAttName))
     return nsnull;
 
   return nsSMILAnimationFunction::GetAttr(aAttName, aResult);
 }
+
+PRBool
+nsSMILSetAnimationFunction::WillReplace() const
+{
+  return PR_TRUE;
+}
--- a/content/smil/nsSMILSetAnimationFunction.h
+++ b/content/smil/nsSMILSetAnimationFunction.h
@@ -79,13 +79,14 @@ protected:
   // that value whenever the animation is active (no interpolation or anything).
   NS_OVERRIDE virtual PRBool TreatSingleValueAsStatic() const {
     return PR_TRUE;
   }
   NS_OVERRIDE virtual PRBool             HasAttr(nsIAtom* aAttName) const;
   NS_OVERRIDE virtual const nsAttrValue* GetAttr(nsIAtom* aAttName) const;
   NS_OVERRIDE virtual PRBool             GetAttr(nsIAtom* aAttName,
                                                  nsAString& aResult) const;
+  NS_OVERRIDE virtual PRBool WillReplace() const;
 
   PRBool IsDisallowedAttribute(const nsIAtom* aAttribute) const;
 };
 
 #endif // NS_SMILSETANIMATIONFUNCTION_H_
--- a/content/smil/nsSMILTimeContainer.cpp
+++ b/content/smil/nsSMILTimeContainer.cpp
@@ -210,16 +210,24 @@ nsSMILTimeContainer::Sample()
   mNeedsPauseSample = PR_FALSE;
 }
 
 nsresult
 nsSMILTimeContainer::SetParent(nsSMILTimeContainer* aParent)
 {
   if (mParent) {
     mParent->RemoveChild(*this);
+    // When we're not attached to a parent time container, GetParentTime() will
+    // return 0. We need to adjust our pause state information to be relative to
+    // this new time base.
+    // Note that since "current time = parent time - parent offset" setting the
+    // parent offset and pause start as follows preserves our current time even
+    // while parent time = 0.
+    mParentOffset = -mCurrentTime;
+    mPauseStart = 0L;
   }
 
   mParent = aParent;
 
   nsresult rv = NS_OK;
   if (mParent) {
     rv = mParent->AddChild(*this);
   }
@@ -309,16 +317,17 @@ nsSMILTimeContainer::Unlink()
   mMilestoneEntries.Clear();
 }
 
 void
 nsSMILTimeContainer::UpdateCurrentTime()
 {
   nsSMILTime now = IsPaused() ? mPauseStart : GetParentTime();
   mCurrentTime = now - mParentOffset;
+  NS_ABORT_IF_FALSE(mCurrentTime >= 0, "Container has negative time");
 }
 
 void
 nsSMILTimeContainer::NotifyTimeChange()
 {
   // Called when the container time is changed with respect to the document
   // time. When this happens time dependencies in other time containers need to
   // re-resolve their times because begin and end times are stored in container
--- a/content/smil/nsSMILTimedElement.cpp
+++ b/content/smil/nsSMILTimedElement.cpp
@@ -1099,16 +1099,20 @@ nsSMILTimedElement::IsTimeDependent(cons
     return PR_FALSE;
 
   return thisBegin->IsDependentOn(*otherBegin);
 }
 
 void
 nsSMILTimedElement::BindToTree(nsIContent* aContextNode)
 {
+  // Reset previously registered milestone since we may be registering with
+  // a different time container now.
+  mPrevRegisteredMilestone = sMaxMilestone;
+
   // If we were already active then clear all our timing information and start
   // afresh
   if (mElementState != STATE_STARTUP) {
     mSeekState = SEEK_NOT_SEEKING;
     Rewind();
   }
 
   // Resolve references to other parts of the tree
@@ -1117,18 +1121,16 @@ nsSMILTimedElement::BindToTree(nsIConten
     mBeginSpecs[i]->ResolveReferences(aContextNode);
   }
 
   count = mEndSpecs.Length();
   for (PRUint32 j = 0; j < count; ++j) {
     mEndSpecs[j]->ResolveReferences(aContextNode);
   }
 
-  // Register new milestone
-  mPrevRegisteredMilestone = sMaxMilestone;
   RegisterMilestone();
 }
 
 void
 nsSMILTimedElement::HandleTargetElementChange(Element* aNewTarget)
 {
   PRUint32 count = mBeginSpecs.Length();
   for (PRUint32 i = 0; i < count; ++i) {
--- a/content/smil/test/smilAnimateMotionValueLists.js
+++ b/content/smil/test/smilAnimateMotionValueLists.js
@@ -108,17 +108,23 @@ const gInvalidToBy = [
 const gValidPath = [
  "m0 0     L30 30",
  "M20,20L10    10",
  "M20,20 L30, 30h20",
  "m50 50", "M50 50",
  "m0 0", "M0, 0"
 ];
 
+// paths must start with at least a valid "M" segment to be valid
 const gInvalidPath = [
- "m0 0 L30,,30",
- "M20 20em",
  "M20in 20",
  "h30",
  "L50 50",
  "abc",
+];
+
+// paths that at least start with a valid "M" segment are valid - the spec says
+// to parse everything up to the first invalid token
+const gValidPathWithErrors = [
+ "M20 20em",
+ "m0 0 L30,,30",
  "M10 10 L50 50 abc",
 ];
--- a/content/smil/test/test_smilAnimateMotionInvalidValues.xhtml
+++ b/content/smil/test/test_smilAnimateMotionInvalidValues.xhtml
@@ -152,16 +152,17 @@ function main()
   testAttr("to", gValidToBy, true, false);
   testAttr("to", gInvalidToBy, false, false);
 
   testAttr("by", gValidToBy, true, false);
   testAttr("by", gInvalidToBy, false, false);
 
   testAttr("path", gValidPath, true, false);
   testAttr("path", gInvalidPath, false, false);
+  testAttr("path", gValidPathWithErrors, true, false);
 
   testMpathElem(gValidPath, true, false);
   testMpathElem(gInvalidPath, false, false);
 
   SimpleTest.finish();
 }
 
 window.addEventListener("load", main, false);
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGPathSeg.cpp
@@ -0,0 +1,1056 @@
+/* -*- 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 "DOMSVGPathSeg.h"
+#include "DOMSVGPathSegList.h"
+#include "SVGPathSegUtils.h"
+#include "SVGAnimatedPathSegList.h"
+#include "nsSVGElement.h"
+#include "nsIDOMSVGPathSeg.h"
+#include "nsDOMError.h"
+
+// See the architecture comment in DOMSVGPathSegList.h.
+
+using 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(DOMSVGPathSeg)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGPathSeg)
+  // We may not belong to a list, so we must null check tmp->mList.
+  if (tmp->mList) {
+    tmp->mList->ItemAt(tmp->mListIndex) = nsnull;
+  }
+NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGPathSeg)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPathSeg)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPathSeg)
+
+DOMCI_DATA(SVGPathSeg, DOMSVGPathSeg)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPathSeg)
+  NS_INTERFACE_MAP_ENTRY(DOMSVGPathSeg) // pseudo-interface
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPathSeg)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+
+DOMSVGPathSeg::DOMSVGPathSeg(DOMSVGPathSegList *aList,
+                             PRUint32 aListIndex,
+                             PRBool aIsAnimValItem)
+  : mList(aList)
+  , mListIndex(aListIndex)
+  , mIsAnimValItem(aIsAnimValItem)
+{
+  // These shifts are in sync with the members in the header.
+  NS_ABORT_IF_FALSE(aList &&
+                    aListIndex < (1U << 31), "bad arg");
+
+  NS_ABORT_IF_FALSE(IndexIsValid(), "Bad index for DOMSVGPathSeg!");
+}
+
+DOMSVGPathSeg::DOMSVGPathSeg()
+  : mList(nsnull)
+  , mListIndex(0)
+  , mIsAnimValItem(PR_FALSE)
+{
+}
+
+NS_IMETHODIMP
+DOMSVGPathSeg::GetPathSegType(PRUint16 *aPathSegType)
+{
+  *aPathSegType = PRUint16(Type());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPathSeg::GetPathSegTypeAsLetter(nsAString &aPathSegTypeAsLetter)
+{
+  aPathSegTypeAsLetter = SVGPathSegUtils::GetPathSegTypeAsLetter(Type());
+  return NS_OK;
+}
+
+void
+DOMSVGPathSeg::InsertingIntoList(DOMSVGPathSegList *aList,
+                                 PRUint32 aListIndex,
+                                 PRBool aIsAnimValItem)
+{
+  NS_ABORT_IF_FALSE(!HasOwner(), "Inserting item that is already in a list");
+
+  mList = aList;
+  mListIndex = aListIndex;
+  mIsAnimValItem = aIsAnimValItem;
+
+  NS_ABORT_IF_FALSE(IndexIsValid(), "Bad index for DOMSVGPathSeg!");
+}
+
+void
+DOMSVGPathSeg::RemovingFromList()
+{
+  PRUint32 argCount = SVGPathSegUtils::ArgCountForType(Type());
+  // InternalItem() + 1, because the args come after the encoded seg type
+  memcpy(PtrToMemberArgs(), InternalItem() + 1, argCount * sizeof(float));
+  mList = nsnull;
+  mIsAnimValItem = PR_FALSE;
+}
+
+void
+DOMSVGPathSeg::ToSVGPathSegEncodedData(float* aRaw)
+{
+  NS_ABORT_IF_FALSE(aRaw, "null pointer");
+  PRUint32 argCount = SVGPathSegUtils::ArgCountForType(Type());
+  if (IsInList()) {
+    // 1 + argCount, because we're copying the encoded seg type and args
+    memcpy(aRaw, InternalItem(), (1 + argCount) * sizeof(float));
+  } else {
+    aRaw[0] = SVGPathSegUtils::EncodeType(Type());
+    // aRaw + 1, because the args go after the encoded seg type
+    memcpy(aRaw + 1, PtrToMemberArgs(), argCount * sizeof(float));
+  }
+}
+
+float*
+DOMSVGPathSeg::InternalItem()
+{
+  PRUint32 dataIndex = mList->mItems[mListIndex].mInternalDataIndex;
+  return &(mList->InternalList().mData[dataIndex]);
+}
+
+#ifdef DEBUG
+PRBool
+DOMSVGPathSeg::IndexIsValid()
+{
+  SVGAnimatedPathSegList *alist = Element()->GetAnimPathSegList();
+  return (mIsAnimValItem &&
+          mListIndex < alist->GetAnimValue().CountItems()) ||
+         (!mIsAnimValItem &&
+          mListIndex < alist->GetBaseValue().CountItems());
+}
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+// Implementation of DOMSVGPathSeg sub-classes below this point
+
+#define CHECK_ARG_COUNT_IN_SYNC(segType)                                      \
+          NS_ABORT_IF_FALSE(NS_ARRAY_LENGTH(mArgs) ==                         \
+            SVGPathSegUtils::ArgCountForType(PRUint32(segType)) ||            \
+            PRUint32(segType) == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH,         \
+            "Arg count/array size out of sync")
+
+#define IMPL_SVGPATHSEG_SUBCLASS_COMMON(segName, segType)                     \
+  DOMSVGPathSeg##segName(const float *aArgs)                                  \
+    : DOMSVGPathSeg()                                                         \
+  {                                                                           \
+    CHECK_ARG_COUNT_IN_SYNC(segType);                                         \
+    memcpy(mArgs, aArgs,                                                      \
+        SVGPathSegUtils::ArgCountForType(PRUint32(segType)) * sizeof(float)); \
+  }                                                                           \
+  DOMSVGPathSeg##segName(DOMSVGPathSegList *aList,                            \
+                         PRUint32 aListIndex,                                 \
+                         PRBool aIsAnimValItem)                               \
+    : DOMSVGPathSeg(aList, aListIndex, aIsAnimValItem)                        \
+  {                                                                           \
+    CHECK_ARG_COUNT_IN_SYNC(segType);                                         \
+  }                                                                           \
+  /* From DOMSVGPathSeg: */                                                   \
+  virtual PRUint32                                                            \
+  Type() const                                                                \
+  {                                                                           \
+    return segType;                                                           \
+  }                                                                           \
+  virtual DOMSVGPathSeg*                                                      \
+  Clone()                                                                     \
+  {                                                                           \
+    /* InternalItem() + 1, because we're skipping the encoded seg type */     \
+    float *args = IsInList() ? InternalItem() + 1 : mArgs;                    \
+    return new DOMSVGPathSeg##segName(args);                                  \
+  }                                                                           \
+  virtual float*                                                              \
+  PtrToMemberArgs()                                                           \
+  {                                                                           \
+    return mArgs;                                                             \
+  }
+
+#define IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(segName)                         \
+  /* Forward to the CYCLE_COLLECTING_ADDREF on our base class */              \
+  NS_IMPL_ADDREF_INHERITED(DOMSVGPathSeg##segName, DOMSVGPathSeg)             \
+  NS_IMPL_RELEASE_INHERITED(DOMSVGPathSeg##segName, DOMSVGPathSeg)            \
+                                                                              \
+  DOMCI_DATA(SVGPathSeg##segName, DOMSVGPathSeg##segName)                     \
+                                                                              \
+  NS_INTERFACE_MAP_BEGIN(DOMSVGPathSeg##segName)                              \
+    NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPathSeg##segName)                         \
+    NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGPathSeg##segName)                 \
+  NS_INTERFACE_MAP_END_INHERITING(DOMSVGPathSeg)
+
+#define IMPL_PROP_WITH_TYPE(segName, propName, index, type)                   \
+  /* attribute type propName; */                                              \
+  NS_IMETHODIMP                                                               \
+  DOMSVGPathSeg##segName::Get##propName(type *a##propName)                    \
+  {                                                                           \
+    if (mIsAnimValItem && HasOwner()) {                                       \
+      Element()->FlushAnimations(); /* May make HasOwner() == PR_FALSE */     \
+    }                                                                         \
+    *a##propName = type(HasOwner() ? InternalItem()[1+index] : mArgs[index]); \
+    return NS_OK;                                                             \
+  }                                                                           \
+  NS_IMETHODIMP                                                               \
+  DOMSVGPathSeg##segName::Set##propName(type a##propName)                     \
+  {                                                                           \
+    if (mIsAnimValItem) {                                                     \
+      return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;                        \
+    }                                                                         \
+    NS_ENSURE_FINITE(float(a##propName), NS_ERROR_ILLEGAL_VALUE);             \
+    if (HasOwner()) {                                                         \
+      InternalItem()[1+index] = float(a##propName);                           \
+      NS_ABORT_IF_FALSE(IsInList(), "DidChangePathSegList() is wrong");       \
+      Element()->DidChangePathSegList(PR_TRUE);                               \
+      if (mList->AttrIsAnimating()) {                                         \
+        Element()->AnimationNeedsResample();                                  \
+      }                                                                       \
+    } else {                                                                  \
+      mArgs[index] = float(a##propName);                                      \
+    }                                                                         \
+    return NS_OK;                                                             \
+  }
+
+// For float, the normal type of arguments
+#define IMPL_FLOAT_PROP(segName, propName, index) \
+  IMPL_PROP_WITH_TYPE(segName, propName, index, float)
+
+// For the boolean flags in arc commands
+#define IMPL_BOOL_PROP(segName, propName, index) \
+  IMPL_PROP_WITH_TYPE(segName, propName, index, PRBool)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegClosePath
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegClosePath
+{
+public:
+  DOMSVGPathSegClosePath()
+    : DOMSVGPathSeg()
+  {
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGCLOSEPATH
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(ClosePath, PATHSEG_CLOSEPATH)
+
+protected:
+  // To allow IMPL_SVGPATHSEG_SUBCLASS_COMMON above to compile we need an
+  // mArgs, but since C++ doesn't allow zero-sized arrays we need to give it
+  // one (unused) element.
+  float mArgs[1];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(ClosePath)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegMovetoAbs
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegMovetoAbs
+{
+public:
+  DOMSVGPathSegMovetoAbs(float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x;
+    mArgs[1] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGMOVETOABS
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(MovetoAbs, PATHSEG_MOVETO_ABS)
+
+protected:
+  float mArgs[2];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(MovetoAbs)
+
+IMPL_FLOAT_PROP(MovetoAbs, X, 0)
+IMPL_FLOAT_PROP(MovetoAbs, Y, 1)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegMovetoRel
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegMovetoRel
+{
+public:
+  DOMSVGPathSegMovetoRel(float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x;
+    mArgs[1] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGMOVETOREL
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(MovetoRel, PATHSEG_MOVETO_REL)
+
+protected:
+  float mArgs[2];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(MovetoRel)
+
+IMPL_FLOAT_PROP(MovetoRel, X, 0)
+IMPL_FLOAT_PROP(MovetoRel, Y, 1)
+
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegLinetoAbs
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegLinetoAbs
+{
+public:
+  DOMSVGPathSegLinetoAbs(float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x;
+    mArgs[1] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGLINETOABS
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoAbs, PATHSEG_LINETO_ABS)
+
+protected:
+  float mArgs[2];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(LinetoAbs)
+
+IMPL_FLOAT_PROP(LinetoAbs, X, 0)
+IMPL_FLOAT_PROP(LinetoAbs, Y, 1)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegLinetoRel
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegLinetoRel
+{
+public:
+  DOMSVGPathSegLinetoRel(float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x;
+    mArgs[1] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGLINETOREL
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoRel, PATHSEG_LINETO_REL)
+
+protected:
+  float mArgs[2];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(LinetoRel)
+
+IMPL_FLOAT_PROP(LinetoRel, X, 0)
+IMPL_FLOAT_PROP(LinetoRel, Y, 1)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegCurvetoCubicAbs
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegCurvetoCubicAbs
+{
+public:
+  DOMSVGPathSegCurvetoCubicAbs(float x1, float y1,
+                               float x2, float y2,
+                               float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x1;
+    mArgs[1] = y1;
+    mArgs[2] = x2;
+    mArgs[3] = y2;
+    mArgs[4] = x;
+    mArgs[5] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGCURVETOCUBICABS
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoCubicAbs, PATHSEG_CURVETO_CUBIC_ABS)
+
+protected:
+  float mArgs[6];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(CurvetoCubicAbs)
+
+IMPL_FLOAT_PROP(CurvetoCubicAbs, X1, 0)
+IMPL_FLOAT_PROP(CurvetoCubicAbs, Y1, 1)
+IMPL_FLOAT_PROP(CurvetoCubicAbs, X2, 2)
+IMPL_FLOAT_PROP(CurvetoCubicAbs, Y2, 3)
+IMPL_FLOAT_PROP(CurvetoCubicAbs, X, 4)
+IMPL_FLOAT_PROP(CurvetoCubicAbs, Y, 5)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegCurvetoCubicRel
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegCurvetoCubicRel
+{
+public:
+  DOMSVGPathSegCurvetoCubicRel(float x1, float y1,
+                               float x2, float y2,
+                               float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x1;
+    mArgs[1] = y1;
+    mArgs[2] = x2;
+    mArgs[3] = y2;
+    mArgs[4] = x;
+    mArgs[5] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGCURVETOCUBICREL
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoCubicRel, PATHSEG_CURVETO_CUBIC_REL)
+
+protected:
+  float mArgs[6];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(CurvetoCubicRel)
+
+IMPL_FLOAT_PROP(CurvetoCubicRel, X1, 0)
+IMPL_FLOAT_PROP(CurvetoCubicRel, Y1, 1)
+IMPL_FLOAT_PROP(CurvetoCubicRel, X2, 2)
+IMPL_FLOAT_PROP(CurvetoCubicRel, Y2, 3)
+IMPL_FLOAT_PROP(CurvetoCubicRel, X, 4)
+IMPL_FLOAT_PROP(CurvetoCubicRel, Y, 5)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegCurvetoQuadraticAbs
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegCurvetoQuadraticAbs
+{
+public:
+  DOMSVGPathSegCurvetoQuadraticAbs(float x1, float y1,
+                                   float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x1;
+    mArgs[1] = y1;
+    mArgs[2] = x;
+    mArgs[3] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGCURVETOQUADRATICABS
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoQuadraticAbs, PATHSEG_CURVETO_QUADRATIC_ABS)
+
+protected:
+  float mArgs[4];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(CurvetoQuadraticAbs)
+
+IMPL_FLOAT_PROP(CurvetoQuadraticAbs, X1, 0)
+IMPL_FLOAT_PROP(CurvetoQuadraticAbs, Y1, 1)
+IMPL_FLOAT_PROP(CurvetoQuadraticAbs, X, 2)
+IMPL_FLOAT_PROP(CurvetoQuadraticAbs, Y, 3)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegCurvetoQuadraticRel
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegCurvetoQuadraticRel
+{
+public:
+  DOMSVGPathSegCurvetoQuadraticRel(float x1, float y1,
+                                   float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x1;
+    mArgs[1] = y1;
+    mArgs[2] = x;
+    mArgs[3] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGCURVETOQUADRATICREL
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoQuadraticRel, PATHSEG_CURVETO_QUADRATIC_REL)
+
+protected:
+  float mArgs[4];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(CurvetoQuadraticRel)
+
+IMPL_FLOAT_PROP(CurvetoQuadraticRel, X1, 0)
+IMPL_FLOAT_PROP(CurvetoQuadraticRel, Y1, 1)
+IMPL_FLOAT_PROP(CurvetoQuadraticRel, X, 2)
+IMPL_FLOAT_PROP(CurvetoQuadraticRel, Y, 3)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegArcAbs
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegArcAbs
+{
+public:
+  DOMSVGPathSegArcAbs(float r1, float r2, float angle,
+                      PRBool largeArcFlag, PRBool sweepFlag,
+                      float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = r1;
+    mArgs[1] = r2;
+    mArgs[2] = angle;
+    mArgs[3] = largeArcFlag;
+    mArgs[4] = sweepFlag;
+    mArgs[5] = x;
+    mArgs[6] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGARCABS
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(ArcAbs, PATHSEG_ARC_ABS)
+
+protected:
+  float mArgs[7];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(ArcAbs)
+
+IMPL_FLOAT_PROP(ArcAbs, R1, 0)
+IMPL_FLOAT_PROP(ArcAbs, R2, 1)
+IMPL_FLOAT_PROP(ArcAbs, Angle, 2)
+IMPL_BOOL_PROP(ArcAbs, LargeArcFlag, 3)
+IMPL_BOOL_PROP(ArcAbs, SweepFlag, 4)
+IMPL_FLOAT_PROP(ArcAbs, X, 5)
+IMPL_FLOAT_PROP(ArcAbs, Y, 6)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegArcRel
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegArcRel
+{
+public:
+  DOMSVGPathSegArcRel(float r1, float r2, float angle,
+                      PRBool largeArcFlag, PRBool sweepFlag,
+                      float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = r1;
+    mArgs[1] = r2;
+    mArgs[2] = angle;
+    mArgs[3] = largeArcFlag;
+    mArgs[4] = sweepFlag;
+    mArgs[5] = x;
+    mArgs[6] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGARCREL
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(ArcRel, PATHSEG_ARC_REL)
+
+protected:
+  float mArgs[7];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(ArcRel)
+
+IMPL_FLOAT_PROP(ArcRel, R1, 0)
+IMPL_FLOAT_PROP(ArcRel, R2, 1)
+IMPL_FLOAT_PROP(ArcRel, Angle, 2)
+IMPL_BOOL_PROP(ArcRel, LargeArcFlag, 3)
+IMPL_BOOL_PROP(ArcRel, SweepFlag, 4)
+IMPL_FLOAT_PROP(ArcRel, X, 5)
+IMPL_FLOAT_PROP(ArcRel, Y, 6)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegLinetoHorizontalAbs
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegLinetoHorizontalAbs
+{
+public:
+  DOMSVGPathSegLinetoHorizontalAbs(float x)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGLINETOHORIZONTALABS
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoHorizontalAbs, PATHSEG_LINETO_HORIZONTAL_ABS)
+
+protected:
+  float mArgs[1];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(LinetoHorizontalAbs)
+
+IMPL_FLOAT_PROP(LinetoHorizontalAbs, X, 0)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegLinetoHorizontalRel
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegLinetoHorizontalRel
+{
+public:
+  DOMSVGPathSegLinetoHorizontalRel(float x)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGLINETOHORIZONTALREL
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoHorizontalRel, PATHSEG_LINETO_HORIZONTAL_REL)
+
+protected:
+  float mArgs[1];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(LinetoHorizontalRel)
+
+IMPL_FLOAT_PROP(LinetoHorizontalRel, X, 0)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegLinetoVerticalAbs
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegLinetoVerticalAbs
+{
+public:
+  DOMSVGPathSegLinetoVerticalAbs(float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGLINETOVERTICALABS
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoVerticalAbs, PATHSEG_LINETO_VERTICAL_ABS)
+
+protected:
+  float mArgs[1];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(LinetoVerticalAbs)
+
+IMPL_FLOAT_PROP(LinetoVerticalAbs, Y, 0)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegLinetoVerticalRel
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegLinetoVerticalRel
+{
+public:
+  DOMSVGPathSegLinetoVerticalRel(float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGLINETOVERTICALREL
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(LinetoVerticalRel, PATHSEG_LINETO_VERTICAL_REL)
+
+protected:
+  float mArgs[1];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(LinetoVerticalRel)
+
+IMPL_FLOAT_PROP(LinetoVerticalRel, Y, 0)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegCurvetoCubicSmoothAbs
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegCurvetoCubicSmoothAbs
+{
+public:
+  DOMSVGPathSegCurvetoCubicSmoothAbs(float x2, float y2,
+                                     float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x2;
+    mArgs[1] = y2;
+    mArgs[2] = x;
+    mArgs[3] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGCURVETOCUBICSMOOTHABS
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoCubicSmoothAbs, PATHSEG_CURVETO_CUBIC_SMOOTH_ABS)
+
+protected:
+  float mArgs[4];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(CurvetoCubicSmoothAbs)
+
+IMPL_FLOAT_PROP(CurvetoCubicSmoothAbs, X2, 0)
+IMPL_FLOAT_PROP(CurvetoCubicSmoothAbs, Y2, 1)
+IMPL_FLOAT_PROP(CurvetoCubicSmoothAbs, X, 2)
+IMPL_FLOAT_PROP(CurvetoCubicSmoothAbs, Y, 3)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegCurvetoCubicSmoothRel
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegCurvetoCubicSmoothRel
+{
+public:
+  DOMSVGPathSegCurvetoCubicSmoothRel(float x2, float y2,
+                                     float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x2;
+    mArgs[1] = y2;
+    mArgs[2] = x;
+    mArgs[3] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGCURVETOCUBICSMOOTHREL
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoCubicSmoothRel, PATHSEG_CURVETO_CUBIC_SMOOTH_REL)
+
+protected:
+  float mArgs[4];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(CurvetoCubicSmoothRel)
+
+IMPL_FLOAT_PROP(CurvetoCubicSmoothRel, X2, 0)
+IMPL_FLOAT_PROP(CurvetoCubicSmoothRel, Y2, 1)
+IMPL_FLOAT_PROP(CurvetoCubicSmoothRel, X, 2)
+IMPL_FLOAT_PROP(CurvetoCubicSmoothRel, Y, 3)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegCurvetoQuadraticSmoothAbs
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs
+{
+public:
+  DOMSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x;
+    mArgs[1] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGCURVETOQUADRATICSMOOTHABS
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoQuadraticSmoothAbs, PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS)
+
+protected:
+  float mArgs[2];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(CurvetoQuadraticSmoothAbs)
+
+IMPL_FLOAT_PROP(CurvetoQuadraticSmoothAbs, X, 0)
+IMPL_FLOAT_PROP(CurvetoQuadraticSmoothAbs, Y, 1)
+
+
+////////////////////////////////////////////////////////////////////////
+
+class DOMSVGPathSegCurvetoQuadraticSmoothRel
+  : public DOMSVGPathSeg
+  , public nsIDOMSVGPathSegCurvetoQuadraticSmoothRel
+{
+public:
+  DOMSVGPathSegCurvetoQuadraticSmoothRel(float x, float y)
+    : DOMSVGPathSeg()
+  {
+    mArgs[0] = x;
+    mArgs[1] = y;
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSVGPATHSEGCURVETOQUADRATICSMOOTHREL
+  IMPL_SVGPATHSEG_SUBCLASS_COMMON(CurvetoQuadraticSmoothRel, PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL)
+
+protected:
+  float mArgs[2];
+};
+
+IMPL_NSISUPPORTS_SVGPATHSEG_SUBCLASS(CurvetoQuadraticSmoothRel)
+
+IMPL_FLOAT_PROP(CurvetoQuadraticSmoothRel, X, 0)
+IMPL_FLOAT_PROP(CurvetoQuadraticSmoothRel, Y, 1)
+
+
+
+// This must come after DOMSVGPathSegClosePath et. al. have been declared.
+/* static */ DOMSVGPathSeg*
+DOMSVGPathSeg::CreateFor(DOMSVGPathSegList *aList,
+                         PRUint32 aListIndex,
+                         PRBool aIsAnimValItem)
+{
+  PRUint32 dataIndex = aList->mItems[aListIndex].mInternalDataIndex;
+  float *data = &aList->InternalList().mData[dataIndex];
+  PRUint32 type = SVGPathSegUtils::DecodeType(data[0]);
+
+  switch (type)
+  {
+  case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
+    return new DOMSVGPathSegClosePath(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
+    return new DOMSVGPathSegMovetoAbs(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
+    return new DOMSVGPathSegMovetoRel(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
+    return new DOMSVGPathSegLinetoAbs(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
+    return new DOMSVGPathSegLinetoRel(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
+    return new DOMSVGPathSegCurvetoCubicAbs(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
+    return new DOMSVGPathSegCurvetoCubicRel(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
+    return new DOMSVGPathSegCurvetoQuadraticAbs(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
+    return new DOMSVGPathSegCurvetoQuadraticRel(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
+    return new DOMSVGPathSegArcAbs(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
+    return new DOMSVGPathSegArcRel(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
+    return new DOMSVGPathSegLinetoHorizontalAbs(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
+    return new DOMSVGPathSegLinetoHorizontalRel(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
+    return new DOMSVGPathSegLinetoVerticalAbs(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
+    return new DOMSVGPathSegLinetoVerticalRel(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
+    return new DOMSVGPathSegCurvetoCubicSmoothAbs(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
+    return new DOMSVGPathSegCurvetoCubicSmoothRel(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
+    return new DOMSVGPathSegCurvetoQuadraticSmoothAbs(aList, aListIndex, aIsAnimValItem);
+  case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
+    return new DOMSVGPathSegCurvetoQuadraticSmoothRel(aList, aListIndex, aIsAnimValItem);
+  default:
+    NS_NOTREACHED("Invalid path segment type");
+    return nsnull;
+  }
+}
+
+
+
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegClosePath()
+{
+  return new DOMSVGPathSegClosePath();
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegMovetoAbs(float x, float y)
+{
+  return new DOMSVGPathSegMovetoAbs(x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegMovetoRel(float x, float y)
+{
+  return new DOMSVGPathSegMovetoRel(x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoAbs(float x, float y)
+{
+  return new DOMSVGPathSegLinetoAbs(x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoRel(float x, float y)
+{
+  return new DOMSVGPathSegLinetoRel(x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoCubicAbs(float x, float y,
+                                float x1, float y1,
+                                float x2, float y2)
+{
+  // Note that we swap from DOM API argument order to the argument order used
+  // in the <path> element's 'd' attribute (i.e. we put the arguments for the
+  // end point of the segment last instead of first).
+
+  return new DOMSVGPathSegCurvetoCubicAbs(x1, y1, x2, y2, x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoCubicRel(float x, float y,
+                                float x1, float y1,
+                                float x2, float y2)
+{
+  // See comment in NS_NewSVGPathSegCurvetoCubicAbs!
+
+  return new DOMSVGPathSegCurvetoCubicRel(x1, y1, x2, y2, x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoQuadraticAbs(float x, float y,
+                                    float x1, float y1)
+{
+  // See comment in NS_NewSVGPathSegCurvetoCubicAbs!
+
+  return new DOMSVGPathSegCurvetoQuadraticAbs(x1, y1, x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoQuadraticRel(float x, float y,
+                                    float x1, float y1)
+{
+  // See comment in NS_NewSVGPathSegCurvetoCubicAbs!
+
+  return new DOMSVGPathSegCurvetoQuadraticRel(x1, y1, x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegArcAbs(float x, float y,
+                       float r1, float r2, float angle,
+                       PRBool largeArcFlag, PRBool sweepFlag)
+{
+  // See comment in NS_NewSVGPathSegCurvetoCubicAbs!
+
+  return new DOMSVGPathSegArcAbs(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegArcRel(float x, float y,
+                       float r1, float r2, float angle,
+                       PRBool largeArcFlag, PRBool sweepFlag)
+{
+  // See comment in NS_NewSVGPathSegCurvetoCubicAbs!
+
+  return new DOMSVGPathSegArcRel(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoHorizontalAbs(float x)
+{
+  return new DOMSVGPathSegLinetoHorizontalAbs(x);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoHorizontalRel(float x)
+{
+  return new DOMSVGPathSegLinetoHorizontalRel(x);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoVerticalAbs(float y)
+{
+  return new DOMSVGPathSegLinetoVerticalAbs(y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoVerticalRel(float y)
+{
+  return new DOMSVGPathSegLinetoVerticalRel(y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoCubicSmoothAbs(float x, float y,
+                                      float x2, float y2)
+{
+  // See comment in NS_NewSVGPathSegCurvetoCubicAbs!
+
+  return new DOMSVGPathSegCurvetoCubicSmoothAbs(x2, y2, x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoCubicSmoothRel(float x, float y,
+                                      float x2, float y2)
+{
+  // See comment in NS_NewSVGPathSegCurvetoCubicAbs!
+
+  return new DOMSVGPathSegCurvetoCubicSmoothRel(x2, y2, x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y)
+{
+  return new DOMSVGPathSegCurvetoQuadraticSmoothAbs(x, y);
+}
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoQuadraticSmoothRel(float x, float y)
+{
+  return new DOMSVGPathSegCurvetoQuadraticSmoothRel(x, y);
+}
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGPathSeg.h
@@ -0,0 +1,276 @@
+/* -*- 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_DOMSVGPATHSEG_H__
+#define MOZILLA_DOMSVGPATHSEG_H__
+
+#include "nsIDOMSVGPathSeg.h"
+#include "DOMSVGPathSegList.h"
+#include "SVGPathSegUtils.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsAutoPtr.h"
+
+class nsSVGElement;
+
+// We make DOMSVGPathSeg a pseudo-interface to allow us to QI to it in order to
+// check that the objects that scripts pass to DOMSVGPathSegList methods are
+// our *native* path seg objects.
+//
+// {494A7566-DC26-40C8-9122-52ABD76870C4}
+#define MOZILLA_DOMSVGPATHSEG_IID \
+  { 0x494A7566, 0xDC26, 0x40C8, { 0x91, 0x22, 0x52, 0xAB, 0xD7, 0x68, 0x70, 0xC4 } }
+
+namespace mozilla {
+
+/**
+ * Class DOMSVGPathSeg
+ *
+ * This class is the base class of the classes that create the DOM objects that
+ * wrap the internal path segments that are encoded in an SVGPathData. Its
+ * sub-classes are also used to create the objects returned by
+ * SVGPathElement.createSVGPathSegXxx().
+ *
+ * See the architecture comment in DOMSVGPathSegList.h for an overview of the
+ * important points regarding these DOM wrapper structures.
+ *
+ * See the architecture comment in DOMSVGLength.h (yes, LENGTH) for an overview
+ * of the important points regarding how this specific class works.
+ *
+ * The main differences between this class and DOMSVGLength is that we have
+ * sub-classes (it does not), and the "internal counterpart" that we provide a
+ * DOM wrapper for is a list of floats, not an instance of an internal class.
+ */
+class DOMSVGPathSeg : public nsIDOMSVGPathSeg
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGPATHSEG_IID)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGPathSeg)
+  NS_DECL_NSIDOMSVGPATHSEG
+
+  /**
+   * This convenient factory method creates instances of the correct sub-class.
+   */
+  static DOMSVGPathSeg *CreateFor(DOMSVGPathSegList *aList,
+                                  PRUint32 aListIndex,
+                                  PRBool aIsAnimValItem);
+
+  /**
+   * Create an unowned copy of this object. The caller is responsible for the
+   * first AddRef()!
+   */
+  virtual DOMSVGPathSeg* Clone() = 0;
+
+  PRBool IsInList() const {
+    return !!mList;
+  }
+
+  /**
+   * In future, if this class is used for non-list segments, 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 DOMSVGPathSegList).)
+   */
+  void InsertingIntoList(DOMSVGPathSegList *aList,
+                         PRUint32 aListIndex,
+                         PRBool 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
+   * "lose" its value on being removed.)
+   */
+  void RemovingFromList();
+
+  /**
+   * This method converts the segment to a string of floats as found in
+   * SVGPathData (i.e. the first float contains the type of the segment,
+   * encoded into a float, followed by its arguments in the same order as they
+   * are given in the <path> element's 'd' attribute).
+   */
+  void ToSVGPathSegEncodedData(float *aData);
+
+  /**
+   * The type of this path segment.
+   */
+  virtual PRUint32 Type() const = 0;
+
+protected:
+
+  /**
+   * Generic ctor for DOMSVGPathSeg objects that are created for an attribute.
+   */
+  DOMSVGPathSeg(DOMSVGPathSegList *aList,
+                PRUint32 aListIndex,
+                PRBool aIsAnimValItem);
+
+  /**
+   * Ctor for creating the objects returned by
+   * SVGPathElement.createSVGPathSegXxx(), which do not initially belong to an
+   * attribute.
+   */
+  DOMSVGPathSeg();
+
+  virtual ~DOMSVGPathSeg() {
+    // 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->ItemAt(mListIndex) = nsnull;
+    }
+  }
+
+  nsSVGElement* Element() {
+    return mList->Element();
+  }
+
+  /**
+   * Get a reference to the internal SVGPathSeg list item that this DOM wrapper
+   * object currently wraps.
+   *
+   * To simplify 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.
+   */
+  float* InternalItem();
+
+  virtual float* PtrToMemberArgs() = 0;
+
+#ifdef DEBUG
+  PRBool IndexIsValid();
+#endif
+
+  nsRefPtr<DOMSVGPathSegList> 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:31;
+  PRUint32 mIsAnimValItem:1; // PRUint32 because MSVC won't pack otherwise
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGPathSeg, MOZILLA_DOMSVGPATHSEG_IID)
+
+} // namespace mozilla
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegClosePath();
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegMovetoAbs(float x, float y);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegMovetoRel(float x, float y);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoAbs(float x, float y);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoRel(float x, float y);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoCubicAbs(float x, float y,
+                                float x1, float y1,
+                                float x2, float y2);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoCubicRel(float x, float y,
+                                float x1, float y1,
+                                float x2, float y2);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoQuadraticAbs(float x, float y,
+                                    float x1, float y1);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoQuadraticRel(float x, float y,
+                                    float x1, float y1);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegArcAbs(float x, float y,
+                       float r1, float r2, float angle,
+                       PRBool largeArcFlag, PRBool sweepFlag);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegArcRel(float x, float y,
+                       float r1, float r2, float angle,
+                       PRBool largeArcFlag, PRBool sweepFlag);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoHorizontalAbs(float x);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoHorizontalRel(float x);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoVerticalAbs(float y);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegLinetoVerticalRel(float y);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoCubicSmoothAbs(float x, float y,
+                                      float x2, float y2);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoCubicSmoothRel(float x, float y,
+                                      float x2, float y2);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y);
+
+nsIDOMSVGPathSeg*
+NS_NewSVGPathSegCurvetoQuadraticSmoothRel(float x, float y);
+
+#endif // MOZILLA_DOMSVGPATHSEG_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGPathSegList.cpp
@@ -0,0 +1,489 @@
+/* -*- 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 "DOMSVGPathSegList.h"
+#include "DOMSVGPathSeg.h"
+#include "nsDOMError.h"
+#include "SVGAnimatedPathSegList.h"
+#include "nsCOMPtr.h"
+#include "nsSVGAttrTearoffTable.h"
+
+// See the comment in this file's header.
+
+using namespace mozilla;
+
+static nsSVGAttrTearoffTable<void, DOMSVGPathSegList>
+  sSVGPathSegListTearoffTable;
+
+NS_SVG_VAL_IMPL_CYCLE_COLLECTION(DOMSVGPathSegList, mElement)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPathSegList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPathSegList)
+
+DOMCI_DATA(SVGPathSegList, DOMSVGPathSegList)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPathSegList)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPathSegList)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGPathSegList)
+NS_INTERFACE_MAP_END
+
+
+/* static */ already_AddRefed<DOMSVGPathSegList>
+DOMSVGPathSegList::GetDOMWrapper(void *aList,
+                                 nsSVGElement *aElement,
+                                 PRBool aIsAnimValList)
+{
+  DOMSVGPathSegList *wrapper =
+    sSVGPathSegListTearoffTable.GetTearoff(aList);
+  if (!wrapper) {
+    wrapper = new DOMSVGPathSegList(aElement, aIsAnimValList);
+    sSVGPathSegListTearoffTable.AddTearoff(aList, wrapper);
+  }
+  NS_ADDREF(wrapper);
+  return wrapper;
+}
+
+/* static */ DOMSVGPathSegList*
+DOMSVGPathSegList::GetDOMWrapperIfExists(void *aList)
+{
+  return sSVGPathSegListTearoffTable.GetTearoff(aList);
+}
+
+DOMSVGPathSegList::~DOMSVGPathSegList()
+{
+  // We no longer have any list items, and there are no script references to
+  // us.
+  //
+  // Do NOT use InternalList() here! That's different!
+  void *key = mIsAnimValList ?
+    InternalAList().GetAnimValKey() :
+    InternalAList().GetBaseValKey();
+  sSVGPathSegListTearoffTable.RemoveTearoff(key);
+}
+
+void
+DOMSVGPathSegList::InternalListWillChangeTo(const SVGPathData& aNewValue)
+{
+  // When the number of items in our internal counterpart changes, we MUST stay
+  // in sync. Everything in the scary comment in
+  // DOMSVGLengthList::InternalBaseValListWillChangeTo applies here just as
+  // much, but we have the additional issue that failing to stay in sync would
+  // mean that - assuming we aren't reading bad memory - we would likely end up
+  // decoding command types from argument floats when looking in our
+  // SVGPathData's data array! Either way, we'll likely then go down
+  // NS_NOTREACHED code paths, or end up reading/setting more bad memory!!
+
+  // The only time that our other DOM list type implementations remove items is
+  // if those items become surplus items due to an attribute change or SMIL
+  // animation sample shortening the list. In general though, they try to keep
+  // their existing DOM items, even when things change. To be consistent, we'd
+  // really like to do the same thing. However, because different types of path
+  // segment correspond to different DOMSVGPathSeg subclasses, the type of
+  // items in our list are generally not the same, which makes this harder for
+  // us. We have to remove DOM segments if their type is not the same as the
+  // type of the new internal segment at their index.
+  //
+  // We also need to sync up mInternalDataIndex, but since we need to loop over
+  // all the items in the new list checking types anyway, that's almost
+  // insignificant in terms of overhead.
+  //
+  // Note that this method is called on every single SMIL animation resample
+  // and we have no way to short circuit the overhead since we don't have a
+  // way to tell if the call is due to a new animation, or a resample of an
+  // existing animation (when the number and type of items would be the same).
+  // (Note that a new animation could start overriding an existing animation at
+  // any time, so checking IsAnimating() wouldn't work.) Because we get called
+  // on every sample, it would not be acceptable alternative to throw away all
+  // our items and let them be recreated lazily, since that would break what
+  // script sees!
+
+  PRUint32 length = mItems.Length();
+  PRUint32 index = 0;
+
+  PRUint32 dataLength = aNewValue.mData.Length();
+  PRUint32 dataIndex = 0; // index into aNewValue's raw data array
+
+  PRUint32 newSegType;
+
+  while (index < length && dataIndex < dataLength) {
+    newSegType = SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]);
+    if (ItemAt(index) && ItemAt(index)->Type() != newSegType) {
+      ItemAt(index)->RemovingFromList();
+      ItemAt(index) = nsnull;
+    }
+    // Only after the RemovingFromList() can we touch mInternalDataIndex!
+    mItems[index].mInternalDataIndex = dataIndex;
+    ++index;
+    dataIndex += 1 + SVGPathSegUtils::ArgCountForType(newSegType);
+  }
+
+  NS_ABORT_IF_FALSE((index == length && dataIndex <= dataLength) ||
+                    (index <= length && dataIndex == dataLength),
+                    "very bad - list corruption?");
+
+  if (index < length) {
+    // aNewValue has fewer items than our previous internal counterpart
+
+    PRUint32 newLength = index;
+
+    // Remove excess items from the list:
+    for (; index < length; ++index) {
+      if (ItemAt(index)) {
+        ItemAt(index)->RemovingFromList();
+        ItemAt(index) = nsnull;
+      }
+    }
+
+    // Only now may we truncate mItems
+    mItems.SetLength(newLength);
+
+  } else if (dataIndex < dataLength) {
+    // aNewValue has more items than our previous internal counterpart
+
+    // Sync mItems:
+    while (dataIndex < dataLength) {
+      if (!mItems.AppendElement(ItemProxy(nsnull, dataIndex))) {
+        // OOM
+        Clear();
+        return;
+      }
+      dataIndex += 1 + SVGPathSegUtils::ArgCountForType(SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]));
+    }
+  }
+
+  NS_ABORT_IF_FALSE(dataIndex == dataLength, "Serious processing error");
+  NS_ABORT_IF_FALSE(index == length, "Serious counting error");
+}
+
+PRBool
+DOMSVGPathSegList::AttrIsAnimating() const
+{
+  return const_cast<DOMSVGPathSegList*>(this)->InternalAList().IsAnimating();
+}
+
+SVGPathData&
+DOMSVGPathSegList::InternalList()
+{
+  SVGAnimatedPathSegList *alist = mElement->GetAnimPathSegList();
+  return mIsAnimValList && alist->IsAnimating() ? *alist->mAnimVal : alist->mBaseVal;
+}
+
+SVGAnimatedPathSegList&
+DOMSVGPathSegList::InternalAList()
+{
+  NS_ABORT_IF_FALSE(mElement->GetAnimPathSegList(), "Internal error");
+  return *mElement->GetAnimPathSegList();
+}
+
+// ----------------------------------------------------------------------------
+// nsIDOMSVGPathSegList implementation:
+
+NS_IMETHODIMP
+DOMSVGPathSegList::GetNumberOfItems(PRUint32 *aNumberOfItems)
+{
+#ifdef MOZ_SMIL
+  if (IsAnimValList()) {
+    Element()->FlushAnimations();
+  }
+#endif
+  *aNumberOfItems = Length();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPathSegList::Clear()
+{
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  if (Length() > 0) {
+    // DOM list items that are to be removed must be removed before we change
+    // the internal list, otherwise they wouldn't be able to copy their
+    // internal counterparts' values!
+
+    InternalListWillChangeTo(SVGPathData()); // clears mItems
+
+    if (!AttrIsAnimating()) {
+      // The anim val list is in sync with the base val list
+      DOMSVGPathSegList *animList =
+        GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
+      if (animList) {
+        animList->InternalListWillChangeTo(SVGPathData()); // clears its mItems
+      }
+    }
+
+    InternalList().Clear();
+    Element()->DidChangePathSegList(PR_TRUE);
+#ifdef MOZ_SMIL
+    if (AttrIsAnimating()) {
+      Element()->AnimationNeedsResample();
+    }
+#endif
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPathSegList::Initialize(nsIDOMSVGPathSeg *aNewItem,
+                              nsIDOMSVGPathSeg **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  // If aNewItem is already in a list we should insert a clone of aNewItem,
+  // and for consistency, this should happen even if *this* is the list that
+  // aNewItem is currently in. Note that in the case of aNewItem 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 aNewItem, it would actually insert aNewItem. To prevent that
+  // from happening we have to do the clone here, if necessary.
+
+  nsCOMPtr<DOMSVGPathSeg> domItem = do_QueryInterface(aNewItem);
+  if (!domItem) {
+    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
+  }
+  if (domItem->HasOwner()) {
+    aNewItem = domItem->Clone();
+  }
+
+  Clear();
+  return InsertItemBefore(aNewItem, 0, _retval);
+}
+
+NS_IMETHODIMP
+DOMSVGPathSegList::GetItem(PRUint32 aIndex,
+                           nsIDOMSVGPathSeg **_retval)
+{
+#ifdef MOZ_SMIL
+  if (IsAnimValList()) {
+    Element()->FlushAnimations();
+  }
+#endif
+  if (aIndex < Length()) {
+    EnsureItemAt(aIndex);
+    NS_ADDREF(*_retval = ItemAt(aIndex));
+    return NS_OK;
+  }
+  *_retval = nsnull;
+  return NS_ERROR_DOM_INDEX_SIZE_ERR;
+}
+
+NS_IMETHODIMP
+DOMSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *aNewItem,
+                                    PRUint32 aIndex,
+                                    nsIDOMSVGPathSeg **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  nsCOMPtr<DOMSVGPathSeg> domItem = do_QueryInterface(aNewItem);
+  if (!domItem) {
+    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
+  }
+  if (domItem->HasOwner()) {
+    domItem = domItem->Clone(); // must do this before changing anything!
+  }
+  PRUint32 internalIndex;
+  if (aIndex < Length()) {
+    internalIndex = mItems[aIndex].mInternalDataIndex;
+  } else {
+    aIndex = Length();
+    internalIndex = InternalList().mData.Length();
+  }
+  PRUint32 argCount = SVGPathSegUtils::ArgCountForType(domItem->Type());
+
+  // Ensure we have enough memory so we can avoid complex error handling below:
+  if (!mItems.SetCapacity(mItems.Length() + 1) ||
+      !InternalList().mData.SetCapacity(InternalList().mData.Length() + 1 + argCount)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS];
+  domItem->ToSVGPathSegEncodedData(segAsRaw);
+
+  InternalList().mData.InsertElementsAt(internalIndex, segAsRaw, 1 + argCount);
+  mItems.InsertElementAt(aIndex, ItemProxy(domItem.get(), internalIndex));
+
+  // This MUST come after the insertion into InternalList(), or else under the
+  // insertion into InternalList() the data read from domItem would be bad data
+  // from InternalList() itself!:
+  domItem->InsertingIntoList(this, aIndex, IsAnimValList());
+
+  for (PRUint32 i = aIndex + 1; i < Length(); ++i) {
+    mItems[i].mInternalDataIndex += 1 + argCount;
+    if (ItemAt(i)) {
+      ItemAt(i)->UpdateListIndex(i);
+    }
+  }
+
+  Element()->DidChangePathSegList(PR_TRUE);
+#ifdef MOZ_SMIL
+  if (AttrIsAnimating()) {
+    Element()->AnimationNeedsResample();
+  }
+#endif
+  *_retval = domItem.forget().get();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPathSegList::ReplaceItem(nsIDOMSVGPathSeg *aNewItem,
+                               PRUint32 aIndex,
+                               nsIDOMSVGPathSeg **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  nsCOMPtr<DOMSVGPathSeg> domItem = do_QueryInterface(aNewItem);
+  if (!domItem) {
+    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
+  }
+  if (aIndex >= Length()) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
+  if (domItem->HasOwner()) {
+    domItem = domItem->Clone(); // must do this before changing anything!
+  }
+
+  if (ItemAt(aIndex)) {
+    // Notify any existing DOM item of removal *before* modifying the lists so
+    // that the DOM item can copy the *old* value at its index:
+    ItemAt(aIndex)->RemovingFromList();
+  }
+
+  PRUint32 internalIndex = mItems[aIndex].mInternalDataIndex;
+  PRUint32 oldArgCount = SVGPathSegUtils::ArgCountForType(domItem->Type());
+  PRUint32 newArgCount = SVGPathSegUtils::ArgCountForType(domItem->Type());
+
+  float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS];
+  domItem->ToSVGPathSegEncodedData(segAsRaw);
+
+  PRBool ok = !!InternalList().mData.ReplaceElementsAt(
+                  internalIndex, 1 + oldArgCount,
+                  segAsRaw, 1 + newArgCount);
+  if (!ok) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  ItemAt(aIndex) = domItem;
+
+  // This MUST come after the ToSVGPathSegEncodedData call otherwise that call
+  // would end up reading bad data from InternalList()!
+  domItem->InsertingIntoList(this, aIndex, IsAnimValList());
+
+  PRUint32 delta = newArgCount - oldArgCount;
+  if (delta != 0) {
+    for (PRUint32 i = aIndex + 1; i < Length(); ++i) {
+      mItems[i].mInternalDataIndex += delta;
+    }
+  }
+
+  Element()->DidChangePathSegList(PR_TRUE);
+#ifdef MOZ_SMIL
+  if (AttrIsAnimating()) {
+    Element()->AnimationNeedsResample();
+  }
+#endif
+  NS_ADDREF(*_retval = domItem.get());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPathSegList::RemoveItem(PRUint32 aIndex,
+                              nsIDOMSVGPathSeg **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  if (aIndex >= Length()) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
+  // We have to return the removed item, so make sure it exists:
+  EnsureItemAt(aIndex);
+
+  // Notify the DOM item of removal *before* modifying the lists so that the
+  // DOM item can copy its *old* value:
+  ItemAt(aIndex)->RemovingFromList();
+  NS_ADDREF(*_retval = ItemAt(aIndex));
+
+  PRUint32 internalIndex = mItems[aIndex].mInternalDataIndex;
+  PRUint32 segType = SVGPathSegUtils::DecodeType(InternalList().mData[internalIndex]);
+  PRUint32 argCount = SVGPathSegUtils::ArgCountForType(segType);
+
+  InternalList().mData.RemoveElementsAt(internalIndex, 1 + argCount);
+  mItems.RemoveElementAt(aIndex);
+
+  for (PRUint32 i = aIndex; i < Length(); ++i) {
+    mItems[i].mInternalDataIndex -= 1 + argCount;
+    if (ItemAt(i)) {
+      ItemAt(i)->UpdateListIndex(i);
+    }
+  }
+
+  Element()->DidChangePathSegList(PR_TRUE);
+#ifdef MOZ_SMIL
+  if (AttrIsAnimating()) {
+    Element()->AnimationNeedsResample();
+  }
+#endif
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPathSegList::AppendItem(nsIDOMSVGPathSeg *aNewItem,
+                              nsIDOMSVGPathSeg **_retval)
+{
+  return InsertItemBefore(aNewItem, Length(), _retval);
+}
+
+void
+DOMSVGPathSegList::EnsureItemAt(PRUint32 aIndex)
+{
+  if (!ItemAt(aIndex)) {
+    ItemAt(aIndex) = DOMSVGPathSeg::CreateFor(this, aIndex, IsAnimValList());
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGPathSegList.h
@@ -0,0 +1,235 @@
+/* -*- 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_DOMSVGPATHSEGLIST_H__
+#define MOZILLA_DOMSVGPATHSEGLIST_H__
+
+#include "nsIDOMSVGPathSegList.h"
+#include "SVGPathData.h"
+#include "SVGPathSegUtils.h"
+#include "nsCOMArray.h"
+#include "nsAutoPtr.h"
+
+class nsSVGElement;
+
+namespace mozilla {
+
+class DOMSVGPathSeg;
+class SVGAnimatedPathSegList;
+
+/**
+ * Class DOMSVGPathSegList
+ *
+ * This class is used to create the DOM tearoff objects that wrap internal
+ * SVGPathData objects.
+ *
+ * See the architecture comment in DOMSVGAnimatedLengthList.h first (that's
+ * LENGTH list), then continue reading the remainder of this comment.
+ *
+ * The architecture of this class is very similar to that of DOMSVGLengthList
+ * except that, since there is no nsIDOMSVGAnimatedPathSegList interface
+ * in SVG, we have no parent DOMSVGAnimatedPathSegList (unlike DOMSVGLengthList
+ * which has a parent DOMSVGAnimatedLengthList class). (There is an
+ * SVGAnimatedPathData interface, but that is quite different to
+ * DOMSVGAnimatedLengthList, since it is inherited by elements rather than
+ * elements having members of that type.) As a consequence, much of the logic
+ * that would otherwise be in DOMSVGAnimatedPathSegList (and is in
+ * DOMSVGAnimatedLengthList) is contained in this class.
+ *
+ * This class is strongly intertwined with DOMSVGPathSeg. Our DOMSVGPathSeg
+ * items are friends of us and responsible for nulling out our pointers to
+ * them when they die.
+ *
+ * Our DOM items are created lazily on demand as and when script requests them.
+ */
+class DOMSVGPathSegList : public nsIDOMSVGPathSegList
+{
+  friend class DOMSVGPathSeg;
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGPathSegList)
+  NS_DECL_NSIDOMSVGPATHSEGLIST
+
+  /**
+   * Factory method to create and return a DOMSVGPathSegList wrapper
+   * for a given internal SVGPathData object. The factory takes care
+   * of caching the object that it returns so that the same object can be
+   * returned for the given SVGPathData 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 SVGPathData will naturally result in a new
+   * DOMSVGPathSegList being returned.
+   *
+   * It's unfortunate that aList is a void* instead of a typed argument. This
+   * is because the mBaseVal and mAnimVal members of SVGAnimatedPathSegList are
+   * of different types - a plain SVGPathData, and a SVGPathData*. We
+   * use the addresses of these members as the key for the hash table, and
+   * clearly SVGPathData* and a SVGPathData** are not the same type.
+   */
+  static already_AddRefed<DOMSVGPathSegList>
+  GetDOMWrapper(void *aList,
+                nsSVGElement *aElement,
+                PRBool aIsAnimValList);
+
+  /**
+   * This method returns the DOMSVGPathSegList wrapper for an internal
+   * SVGPathData object if it currently has a wrapper. If it does
+   * not, then nsnull is returned.
+   */
+  static DOMSVGPathSegList*
+  GetDOMWrapperIfExists(void *aList);
+
+  /**
+   * This will normally be the same as InternalList().CountItems(), 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<DOMSVGPathSegList*>(this)->InternalList().CountItems(),
+                      "DOM wrapper's list length is out of sync");
+    return mItems.Length();
+  }
+
+  /**
+   * WATCH OUT! If you add code to call this on a baseVal wrapper, then you
+   * must also call it on the animVal wrapper too if necessary!! See other
+   * callers!
+   *
+   * Called by internal code to notify us when we need to sync the length of
+   * this DOM list with its internal list. This is called immediately prior to
+   * the length of the internal list being changed so that any DOM list items
+   * that need to be removed from the DOM list can first copy 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 InternalListWillChangeTo(const SVGPathData& aNewValue);
+
+  /**
+   * Returns true if our attribute is animating (in which case our animVal is
+   * not simply a mirror of our baseVal).
+   */
+  PRBool AttrIsAnimating() const;
+
+private:
+
+  /**
+   * Only our static GetDOMWrapper() factory method may create objects of our
+   * type.
+   */
+  DOMSVGPathSegList(nsSVGElement *aElement, PRBool aIsAnimValList)
+    : mElement(aElement)
+    , mIsAnimValList(aIsAnimValList)
+  {
+    // This call populates mItems with the same number of items as there are
+    // segments contained in the internal list. We ignore OOM failure since
+    // being out of sync is safe so long as we have *fewer* items than our
+    // internal list.
+
+    InternalListWillChangeTo(InternalList());
+  }
+
+  ~DOMSVGPathSegList();
+
+  nsSVGElement* Element() {
+    return mElement.get();
+  }
+
+  /// Used to determine if this list is the baseVal or animVal list.
+  PRBool IsAnimValList() const {
+    return mIsAnimValList;
+  }
+
+  /**
+   * Get a reference to this object's corresponding internal SVGPathData.
+   *
+   * To simplify the code we just have this one method for obtaining both
+   * base val and anim val internal lists. This means that anim val lists don't
+   * get const protection, but our setter methods guard against changing
+   * anim val lists.
+   */
+  SVGPathData& InternalList();
+
+  SVGAnimatedPathSegList& InternalAList();
+
+  /// Creates an instance of the appropriate DOMSVGPathSeg sub-class for
+  // aIndex, if it doesn't already exist.
+  void EnsureItemAt(PRUint32 aIndex);
+
+  DOMSVGPathSeg*& ItemAt(PRUint32 aIndex) {
+    return mItems[aIndex].mItem;
+  }
+
+  /**
+   * This struct is used in our array of mItems to provide us with somewhere to
+   * store the indexes into the internal SVGPathData of the internal seg data
+   * that our DOMSVGPathSeg items wrap (the internal segment data is or varying
+   * length, so we can't just use the index of our DOMSVGPathSeg items
+   * themselves). The reason that we have this separate struct rather than
+   * just storing the internal indexes in the DOMSVGPathSeg items is because we
+   * want to create the DOMSVGPathSeg items lazily on demand.
+   */
+  struct ItemProxy {
+    ItemProxy(){}
+    ItemProxy(DOMSVGPathSeg *aItem, PRUint32 aInternalDataIndex)
+      : mItem(aItem)
+      , mInternalDataIndex(aInternalDataIndex)
+    {}
+
+    DOMSVGPathSeg *mItem;
+    PRUint32 mInternalDataIndex;
+  };
+
+  // Weak refs to our DOMSVGPathSeg items. The items are friends and take care
+  // of clearing our pointer to them when they die.
+  nsTArray<ItemProxy> mItems;
+
+  // Strong ref to our element to keep it alive. We hold this not only for
+  // ourself, but also for our DOMSVGPathSeg items too.
+  nsRefPtr<nsSVGElement> mElement;
+
+  PRPackedBool mIsAnimValList;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_DOMSVGPATHSEGLIST_H__
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -47,16 +47,18 @@ include $(DEPTH)/config/autoconf.mk
 MODULE		= content
 LIBRARY_NAME	= gkcontentsvg_s
 LIBXUL_LIBRARY	= 1
 
 CPPSRCS		= \
 		DOMSVGAnimatedLengthList.cpp \
 		DOMSVGLength.cpp \
 		DOMSVGLengthList.cpp \
+		DOMSVGPathSeg.cpp \
+		DOMSVGPathSegList.cpp \
 		nsDOMSVGZoomEvent.cpp \
 		nsDOMSVGEvent.cpp \
 		nsSVGAElement.cpp \
 		nsSVGAltGlyphElement.cpp \
 		nsSVGAngle.cpp \
 		nsSVGAnimatedNumberList.cpp \
 		nsSVGAnimatedTransformList.cpp \
 		nsSVGBoolean.cpp \
@@ -85,18 +87,16 @@ CPPSRCS		= \
 		nsSVGMatrix.cpp \
 		nsSVGMetadataElement.cpp \
 		nsSVGNumber.cpp \
 		nsSVGNumber2.cpp \
 		nsSVGNumberList.cpp \
 		nsSVGPathDataParser.cpp \
 		nsSVGPathElement.cpp \
 		nsSVGPathGeometryElement.cpp \
-		nsSVGPathSeg.cpp \
-		nsSVGPathSegList.cpp \
 		nsSVGPatternElement.cpp \
 		nsSVGPoint.cpp \
 		nsSVGPointList.cpp \
 		nsSVGPolyElement.cpp \
 		nsSVGPolygonElement.cpp \
 		nsSVGPolylineElement.cpp \
 		nsSVGPreserveAspectRatio.cpp \
 		nsSVGScriptElement.cpp \
@@ -118,18 +118,21 @@ CPPSRCS		= \
 		nsSVGTitleElement.cpp \
 		nsSVGTransform.cpp \
 		nsSVGTransformList.cpp \
 		nsSVGTransformListParser.cpp \
 		nsSVGUseElement.cpp \
 		nsSVGValue.cpp \
 		nsSVGViewBox.cpp \
 		SVGAnimatedLengthList.cpp \
+		SVGAnimatedPathSegList.cpp \
 		SVGLength.cpp \
 		SVGLengthList.cpp \
+		SVGPathData.cpp \
+		SVGPathSegUtils.cpp \
 		$(NULL)
 
 ifdef MOZ_SMIL
 CPPSRCS += nsSVGAnimateElement.cpp \
            nsSVGAnimateTransformElement.cpp \
            nsSVGAnimateMotionElement.cpp \
            nsSVGAnimationElement.cpp \
            nsSVGMpathElement.cpp \
@@ -137,16 +140,17 @@ CPPSRCS += nsSVGAnimateElement.cpp \
            nsSVGTransformSMILType.cpp \
            nsSVGTransformSMILAttr.cpp \
            SVGLengthListSMILType.cpp \
            SVGMotionSMILType.cpp \
            SVGMotionSMILAttr.cpp \
            SVGMotionSMILAnimationFunction.cpp \
            SVGMotionSMILPathUtils.cpp \
            SVGOrientSMILType.cpp \
+           SVGPathSegListSMILType.cpp \
            SVGViewBoxSMILType.cpp \
            $(NULL)
 endif
 
 include $(topsrcdir)/config/config.mk
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGAnimatedPathSegList.cpp
@@ -0,0 +1,240 @@
+/* -*- 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 "SVGAnimatedPathSegList.h"
+#include "DOMSVGPathSegList.h"
+#include "nsSVGElement.h"
+#include "nsSVGAttrTearoffTable.h"
+#ifdef MOZ_SMIL
+#include "nsSMILValue.h"
+#include "SVGPathSegListSMILType.h"
+#endif // MOZ_SMIL
+
+// See the comments in this file's header!
+
+using namespace mozilla;
+
+nsresult
+SVGAnimatedPathSegList::SetBaseValueString(const nsAString& aValue)
+{
+  SVGPathData newBaseValue;
+
+  // The spec says that the path data is parsed and accepted up to the first
+  // error encountered, so we don't return early if an error occurs. However,
+  // we do want to throw any error code from setAttribute if there's a problem.
+
+  nsresult rv = newBaseValue.SetValueFromString(aValue);
+
+  // We must send these notifications *before* changing mBaseVal! 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's values *before* we
+  // change them. See the comments in
+  // DOMSVGPathSegList::InternalListWillChangeTo().
+
+  DOMSVGPathSegList *baseValWrapper =
+    DOMSVGPathSegList::GetDOMWrapperIfExists(GetBaseValKey());
+  if (baseValWrapper) {
+    baseValWrapper->InternalListWillChangeTo(newBaseValue);
+  }
+
+  DOMSVGPathSegList *animValWrapper;
+  if (!IsAnimating()) {  // DOM anim val wraps our base val too!
+    animValWrapper = DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
+    if (animValWrapper) {
+      animValWrapper->InternalListWillChangeTo(newBaseValue);
+    }
+  }
+
+  // Only now may we modify mBaseVal!
+
+  // We don't need to call DidChange* here - we're only called by
+  // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
+  // which takes care of notifying.
+
+  nsresult rv2 = mBaseVal.CopyFrom(newBaseValue);
+  if (NS_FAILED(rv2)) {
+    // Attempting to increase mBaseVal's length failed (mBaseVal is left
+    // unmodified). We MUST keep any DOM wrappers in sync:
+    if (baseValWrapper) {
+      baseValWrapper->InternalListWillChangeTo(mBaseVal);
+    }
+    if (animValWrapper) {
+      animValWrapper->InternalListWillChangeTo(mBaseVal);
+    }
+    return rv2;
+  }
+  return rv;
+}
+
+void
+SVGAnimatedPathSegList::ClearBaseValue()
+{
+  // We must send these notifications *before* changing mBaseVal! (See above.)
+
+  DOMSVGPathSegList *baseValWrapper =
+    DOMSVGPathSegList::GetDOMWrapperIfExists(GetBaseValKey());
+  if (baseValWrapper) {
+    baseValWrapper->InternalListWillChangeTo(SVGPathData());
+  }
+
+  if (!IsAnimating()) { // DOM anim val wraps our base val too!
+    DOMSVGPathSegList *animValWrapper =
+      DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
+    if (animValWrapper) {
+      animValWrapper->InternalListWillChangeTo(SVGPathData());
+    }
+  }
+
+  mBaseVal.Clear();
+  // Caller notifies
+}
+
+nsresult
+SVGAnimatedPathSegList::SetAnimValue(const SVGPathData& aNewAnimValue,
+                                     nsSVGElement *aElement)
+{
+  // Note that a new animation may totally change the number of items in the
+  // animVal list, either replacing what was essentially a mirror of the
+  // baseVal list, or else replacing and overriding an existing animation.
+  // Unfortunately it is 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. In the case of DOMSVGPathSegList
+  // the InternalListWillChangeTo method is not virtually free as it is for the
+  // other DOM list classes, so this is a shame. We'd quite like to be able to
+  // skip the call if possible.
+
+  // We must send these notifications *before* changing mAnimVal! (See above.)
+
+  DOMSVGPathSegList *domWrapper =
+    DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
+  if (domWrapper) {
+    domWrapper->InternalListWillChangeTo(aNewAnimValue);
+  }
+  if (!mAnimVal) {
+    mAnimVal = new SVGPathData();
+  }
+  nsresult rv = mAnimVal->CopyFrom(aNewAnimValue);
+  if (NS_FAILED(rv)) {
+    // OOM. We clear the animation and, importantly, ClearAnimValue() ensures
+    // that mAnimVal's DOM wrapper (if any) is kept in sync!
+    ClearAnimValue(aElement);
+  }
+  aElement->DidAnimatePathSegList();
+  return rv;
+}
+
+void
+SVGAnimatedPathSegList::ClearAnimValue(nsSVGElement *aElement)
+{
+  // We must send these notifications *before* changing mAnimVal! (See above.)
+
+  DOMSVGPathSegList *domWrapper =
+    DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
+  if (domWrapper) {
+    // When all animation ends, animVal simply mirrors baseVal, which may have
+    // a different number of items to the last active animated value.
+    //
+    domWrapper->InternalListWillChangeTo(mBaseVal);
+  }
+  mAnimVal = nsnull;
+  aElement->DidAnimatePathSegList();
+}
+
+#ifdef MOZ_SMIL
+nsISMILAttr*
+SVGAnimatedPathSegList::ToSMILAttr(nsSVGElement *aElement)
+{
+  return new SMILAnimatedPathSegList(this, aElement);
+}
+
+nsresult
+SVGAnimatedPathSegList::
+  SMILAnimatedPathSegList::ValueFromString(const nsAString& aStr,
+                               const nsISMILAnimationElement* /*aSrcElement*/,
+                               nsSMILValue& aValue,
+                               PRBool& aPreventCachingOfSandwich) const
+{
+  nsSMILValue val(&SVGPathSegListSMILType::sSingleton);
+  SVGPathDataAndOwner *list = static_cast<SVGPathDataAndOwner*>(val.mU.mPtr);
+  nsresult rv = list->SetValueFromString(aStr);
+  if (NS_SUCCEEDED(rv)) {
+    list->SetElement(mElement);
+    aValue.Swap(val);
+  }
+  aPreventCachingOfSandwich = PR_FALSE;
+  return rv;
+}
+
+nsSMILValue
+SVGAnimatedPathSegList::SMILAnimatedPathSegList::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(&SVGPathSegListSMILType::sSingleton);
+  SVGPathDataAndOwner *list = static_cast<SVGPathDataAndOwner*>(tmp.mU.mPtr);
+  nsresult rv = list->CopyFrom(mVal->mBaseVal);
+  if (NS_SUCCEEDED(rv)) {
+    list->SetElement(mElement);
+    val.Swap(tmp);
+  }
+  return val;
+}
+
+nsresult
+SVGAnimatedPathSegList::SMILAnimatedPathSegList::SetAnimValue(const nsSMILValue& aValue)
+{
+  NS_ASSERTION(aValue.mType == &SVGPathSegListSMILType::sSingleton,
+               "Unexpected type to assign animated value");
+  if (aValue.mType == &SVGPathSegListSMILType::sSingleton) {
+    mVal->SetAnimValue(*static_cast<SVGPathDataAndOwner*>(aValue.mU.mPtr),
+                       mElement);
+  }
+  return NS_OK;
+}
+
+void
+SVGAnimatedPathSegList::SMILAnimatedPathSegList::ClearAnimValue()
+{
+  if (mVal->mAnimVal) {
+    mVal->ClearAnimValue(mElement);
+  }
+}
+#endif // MOZ_SMIL
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGAnimatedPathSegList.h
@@ -0,0 +1,160 @@
+/* -*- 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_SVGANIMATEDPATHSEGLIST_H__
+#define MOZILLA_SVGANIMATEDPATHSEGLIST_H__
+
+#include "SVGPathData.h"
+
+class nsSVGElement;
+
+#ifdef MOZ_SMIL
+#include "nsISMILAttr.h"
+#endif // MOZ_SMIL
+
+namespace mozilla {
+
+/**
+ * Class SVGAnimatedPathSegList
+ *
+ * Despite the fact that no SVGAnimatedPathSegList interface or objects exist
+ * in the SVG specification (unlike e.g. SVGAnimated*Length*List), we
+ * nevertheless have this internal class. (Note that there is an
+ * SVGAnimatedPathData interface, but that's quite different to
+ * SVGAnimatedLengthList since it is inherited by elements, as opposed to
+ * elements having members of that type.) The reason that we have this class is
+ * to provide a single locked down point of entry to the SVGPathData objects,
+ * which helps ensure that the DOM wrappers for SVGPathData objects' are always
+ * kept in sync. This is vitally important (see the comment in
+ * DOMSVGPathSegList::InternalListWillChangeTo) and frees consumers from having
+ * to know or worry about wrappers (or forget about them!) for the most part.
+ */
+class SVGAnimatedPathSegList
+{
+  // friends so that they can get write access to mBaseVal and mAnimVal
+  friend class DOMSVGPathSeg;
+  friend class DOMSVGPathSegList;
+
+public:
+  SVGAnimatedPathSegList() {}
+
+  /**
+   * Because it's so important that mBaseVal and its DOMSVGPathSegList wrapper
+   * (if any) be kept in sync (see the comment in
+   * DOMSVGPathSegList::InternalListWillChangeTo), this method returns a const
+   * reference. Only our friend classes may get mutable references to mBaseVal.
+   */
+  const SVGPathData& GetBaseValue() const {
+    return mBaseVal;
+  }
+
+  nsresult SetBaseValueString(const nsAString& aValue);
+
+  void ClearBaseValue();
+
+  /**
+   * const! See comment for GetBaseValue!
+   */
+  const SVGPathData& GetAnimValue() const {
+    return mAnimVal ? *mAnimVal : mBaseVal;
+  }
+
+  nsresult SetAnimValue(const SVGPathData& aValue,
+                        nsSVGElement *aElement);
+
+  void ClearAnimValue(nsSVGElement *aElement);
+
+  /**
+   * Needed for correct DOM wrapper construction since GetAnimValue may
+   * actually return the baseVal!
+   */
+  void *GetBaseValKey() const {
+    return (void*)&mBaseVal;
+  }
+  void *GetAnimValKey() const {
+    return (void*)&mAnimVal;
+  }
+  
+  PRBool IsAnimating() const {
+    return !!mAnimVal;
+  }
+
+#ifdef MOZ_SMIL
+  /// Callers own the returned nsISMILAttr
+  nsISMILAttr* ToSMILAttr(nsSVGElement* aElement);
+#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="">).
+
+  SVGPathData mBaseVal;
+  nsAutoPtr<SVGPathData> mAnimVal;
+
+#ifdef MOZ_SMIL
+  struct SMILAnimatedPathSegList : public nsISMILAttr
+  {
+  public:
+    SMILAnimatedPathSegList(SVGAnimatedPathSegList* aVal,
+                            nsSVGElement* aElement)
+      : mVal(aVal)
+      , mElement(aElement)
+    {}
+
+    // 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.
+    SVGAnimatedPathSegList *mVal;
+    nsSVGElement *mElement;
+
+    // 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_SVGANIMATEDPATHSEGLIST_H__
--- a/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp
+++ b/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp
@@ -36,17 +36,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "SVGMotionSMILAnimationFunction.h"
 #include "nsSMILParserUtils.h"
 #include "nsSVGAngle.h"
 #include "SVGMotionSMILType.h"
 #include "SVGMotionSMILPathUtils.h"
 #include "nsSVGPathDataParser.h"
-#include "nsSVGPathSeg.h"
 #include "nsSVGPathElement.h" // for nsSVGPathList
 #include "nsSVGMpathElement.h"
 
 namespace mozilla {
 
 SVGMotionSMILAnimationFunction::SVGMotionSMILAnimationFunction()
   : mRotateType(eRotateType_Explicit),
     mRotateAngle(0.0f),
@@ -248,92 +247,54 @@ void
 SVGMotionSMILAnimationFunction::
   RebuildPathAndVerticesFromMpathElem(nsSVGMpathElement* aMpathElem)
 {
   mPathSourceType = ePathSourceType_Mpath;
 
   // Use the path that's the target of our chosen <mpath> child.
   nsSVGPathElement* pathElem = aMpathElem->GetReferencedPath();
   if (pathElem) {
-    const nsAttrValue* value = pathElem->GetParsedAttr(nsGkAtoms::d);
-    if (value) {
-      const nsAString& pathSpec = value->GetStringValue();
-      nsresult rv = SetPathVerticesFromPathString(pathSpec);
-      if (NS_SUCCEEDED(rv)) {
+    const SVGPathData &path = pathElem->GetAnimPathSegList()->GetAnimValue();
+    // Path data must contain of at least one path segment (if the path data
+    // doesn't begin with a valid "M", then it's invalid).
+    if (path.Length()) {
+      PRBool ok =
+        path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices);
+      if (ok && mPathVertices.Length()) {
         mPath = pathElem->GetFlattenedPath(
-          pathElem->PrependLocalTransformTo(gfxMatrix()));
+                  pathElem->PrependLocalTransformTo(gfxMatrix()));
       }
     }
   }
 }
 
 void
 SVGMotionSMILAnimationFunction::RebuildPathAndVerticesFromPathAttr()
 {
   const nsAString& pathSpec = GetAttr(nsGkAtoms::path)->GetStringValue();
   mPathSourceType = ePathSourceType_PathAttr;
 
   // Generate gfxFlattenedPath from |path| attr
-  nsresult rv;
-  nsSVGPathList pathData;
-  nsSVGPathDataParserToInternal pathParser(&pathData);
-  rv = pathParser.Parse(pathSpec);
-  if (NS_FAILED(rv)) {
-    // Parse error.
+  SVGPathData path;
+  nsSVGPathDataParserToInternal pathParser(&path);
+
+  // We ignore any failure returned from Parse() since the SVG spec says to
+  // accept all segments up to the first invalid token. Instead we must
+  // explicitly check that the parse produces at least one path segment (if
+  // the path data doesn't begin with a valid "M", then it's invalid).
+  pathParser.Parse(pathSpec);
+  if (!path.Length()) {
     return;
   }
-  mPath = pathData.GetFlattenedPath(gfxMatrix());
 
-  // Generate list of vertices from |path| attr
-  rv = SetPathVerticesFromPathString(pathSpec);
-
-  if (NS_FAILED(rv)) {
-    // The first parser liked our string, but the second did not. (unexpected,
-    // but possible depending on parser implementations.) Clear path so we
-    // completely (instead of partially) fail.
+  mPath = path.ToFlattenedPath(gfxMatrix());
+  PRBool ok = path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices);
+  if (!ok || !mPathVertices.Length()) {
     mPath = nsnull;
-    NS_WARNING("nsSVGPathDataParserToInternal successfully parsed path string, but "
-               "nsSVGPathDataParserToDOM did not");
-  }
-}
-
-nsresult
-SVGMotionSMILAnimationFunction::SetPathVerticesFromPathString(const nsAString& aPathSpec)
-{
-  // Parse the string to an array of path segments.
-  nsCOMArray<nsIDOMSVGPathSeg> pathSegments;
-  nsSVGPathDataParserToDOM segmentParser(&pathSegments);
-  nsresult rv = segmentParser.Parse(aPathSpec);
-  if (NS_FAILED(rv)) {
-    return rv;
   }
-
-  // Iterate across the parsed segments to populate our mPathVertices array.
-  PRUint32 numSegments = pathSegments.Count();
-  nsSVGPathSegTraversalState ts;
-  double runningDistTotal = 0.0;
-  for (PRUint32 i = 0; i < numSegments; ++i) {
-    nsSVGPathSeg* segment = static_cast<nsSVGPathSeg*>(pathSegments[i]);
-
-    PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
-    segment->GetPathSegType(&type);
-    if (i == 0 ||
-        (type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
-         type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL)) {
-
-      // Increment running length total (note that MoveTo's have 0 length)
-      runningDistTotal += segment->GetLength(&ts);
-
-      // Add an entry for the current point.
-      if (!mPathVertices.AppendElement(runningDistTotal)) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-    }
-  }
-  return NS_OK;
 }
 
 // Helper to regenerate our path representation & its list of vertices
 void
 SVGMotionSMILAnimationFunction::
   RebuildPathAndVertices(const nsIContent* aTargetElement)
 {
   NS_ABORT_IF_FALSE(mIsPathStale, "rebuilding path when it isn't stale");
--- a/content/svg/content/src/SVGMotionSMILAnimationFunction.h
+++ b/content/svg/content/src/SVGMotionSMILAnimationFunction.h
@@ -90,17 +90,16 @@ protected:
   void     CheckKeyPoints();
   nsresult SetKeyPoints(const nsAString& aKeyPoints, nsAttrValue& aResult);
   void     UnsetKeyPoints();
   nsresult SetRotate(const nsAString& aRotate, nsAttrValue& aResult);
   void     UnsetRotate();
 
   // Helpers for GetValues
   void     MarkStaleIfAttributeAffectsPath(nsIAtom* aAttribute);
-  nsresult SetPathVerticesFromPathString(const nsAString& aPathSpec);
   void     RebuildPathAndVertices(const nsIContent* aContextElem);
   void     RebuildPathAndVerticesFromMpathElem(nsSVGMpathElement* aMpathElem);
   void     RebuildPathAndVerticesFromPathAttr();
   void     RebuildPathAndVerticesFromBasicAttrs(const nsIContent* aContextElem);
   PRBool   GenerateValuesForPathAndPoints(gfxFlattenedPath* aPath,
                                           PRBool aIsKeyPoints,
                                           nsTArray<double>& aPointDistances,
                                           nsTArray<nsSMILValue>& aResult);
--- a/content/svg/content/src/SVGMotionSMILType.cpp
+++ b/content/svg/content/src/SVGMotionSMILType.cpp
@@ -39,17 +39,16 @@
 
 #include "SVGMotionSMILType.h"
 #include "nsSMILValue.h"
 #include "nsDebug.h"
 #include "nsSVGTransform.h"
 #include "nsSVGAngle.h"
 #include "nsIDOMSVGAngle.h"
 #include "nsSVGPathElement.h"
-#include "nsSVGPathSeg.h"
 #include "nsIDOMSVGPathSeg.h"
 #include "nsIDOMSVGPathSegList.h"
 #include "nsMathUtils.h"
 #include <math.h>
 
 namespace mozilla {
 
 /*static*/ SVGMotionSMILType SVGMotionSMILType::sSingleton;
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPathData.cpp
@@ -0,0 +1,750 @@
+/* -*- 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 "SVGPathData.h"
+#include "SVGAnimatedPathSegList.h"
+#include "SVGPathSegUtils.h"
+#include "nsSVGElement.h"
+#include "nsISVGValueUtils.h"
+#include "nsDOMError.h"
+#include "nsContentUtils.h"
+#include "nsString.h"
+#include "nsSVGUtils.h"
+#include "string.h"
+#include "nsSVGPathDataParser.h"
+#include "nsSVGPathGeometryElement.h" // for nsSVGMark
+#include "gfxPlatform.h"
+#include <stdarg.h>
+
+using namespace mozilla;
+
+nsresult
+SVGPathData::CopyFrom(const SVGPathData& rhs)
+{
+  if (!mData.SetCapacity(rhs.mData.Length())) {
+    // Yes, we do want fallible alloc here
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  mData = rhs.mData;
+  return NS_OK;
+}
+
+void
+SVGPathData::GetValueAsString(nsAString& aValue) const
+{
+  // we need this function in DidChangePathSegList
+  aValue.Truncate();
+  if (!Length()) {
+    return;
+  }
+  PRUint32 i = 0;
+  for (;;) {
+    nsAutoString segAsString;
+    SVGPathSegUtils::GetValueAsString(&mData[i], segAsString);
+    // We ignore OOM, since it's not useful for us to return an error.
+    aValue.Append(segAsString);
+    i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
+    if (i >= mData.Length()) {
+      NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
+      return;
+    }
+    aValue.Append(' ');
+  }
+}
+
+nsresult
+SVGPathData::SetValueFromString(const nsAString& aValue)
+{
+  // We don't use a temp variable since the spec says to parse everything up to
+  // the first error. We still return any error though so that callers know if
+  // there's a problem.
+
+  nsSVGPathDataParserToInternal pathParser(this);
+  return pathParser.Parse(aValue);
+}
+
+nsresult
+SVGPathData::AppendSeg(PRUint32 aType, ...)
+{
+  PRUint32 oldLength = mData.Length();
+  PRUint32 newLength = oldLength + 1 + SVGPathSegUtils::ArgCountForType(aType);
+  if (!mData.SetLength(newLength)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  mData[oldLength] = SVGPathSegUtils::EncodeType(aType);
+  va_list args;
+  va_start(args, aType);
+  for (PRUint32 i = oldLength + 1; i < newLength; ++i) {
+    // NOTE! 'float' is promoted to 'double' when passed through '...'!
+    mData[i] = float(va_arg(args, double));
+  }
+  va_end(args);
+  return NS_OK;
+}
+
+float
+SVGPathData::GetPathLength() const
+{
+  float length = 0.0;
+  SVGPathTraversalState state;
+
+  PRUint32 i = 0;
+  while (i < mData.Length()) {
+    length += SVGPathSegUtils::GetLength(&mData[i], state);
+    i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
+  }
+
+  NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
+
+  return length;
+}
+
+#ifdef DEBUG
+PRUint32
+SVGPathData::CountItems() const
+{
+  PRUint32 i = 0, count = 0;
+
+  while (i < mData.Length()) {
+    i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
+    count++;
+  }
+
+  NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
+
+  return count;
+}
+#endif
+
+PRBool
+SVGPathData::GetSegmentLengths(nsTArray<double> *aLengths) const
+{
+  aLengths->Clear();
+  SVGPathTraversalState state;
+
+  PRUint32 i = 0;
+  while (i < mData.Length()) {
+    if (!aLengths->AppendElement(SVGPathSegUtils::GetLength(&mData[i], state))) {
+      aLengths->Clear();
+      return PR_FALSE;
+    }
+    i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
+  }
+
+  NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
+
+  return PR_TRUE;
+}
+
+PRBool
+SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(nsTArray<double> *aOutput) const
+{
+  double distRunningTotal = 0.0;
+  SVGPathTraversalState state;
+
+  aOutput->Clear();
+
+  PRUint32 i = 0;
+  while (i < mData.Length()) {
+    PRUint32 segType = SVGPathSegUtils::DecodeType(mData[i]);
+
+    // We skip all moveto commands except an initial moveto. See the text 'A
+    // "move to" command does not count as an additional point when dividing up
+    // the duration...':
+    //
+    // http://www.w3.org/TR/SVG11/animate.html#AnimateMotionElement
+    //
+    // This is important in the non-default case of calcMode="linear". In
+    // this case an equal amount of time is spent on each path segment,
+    // except on moveto segments which are jumped over immediately.
+
+    if (i == 0 || (segType != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
+                   segType != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL)) {
+      distRunningTotal += SVGPathSegUtils::GetLength(&mData[i], state);
+      if (!aOutput->AppendElement(distRunningTotal)) {
+        return PR_FALSE;
+      }
+    }
+    i += 1 + SVGPathSegUtils::ArgCountForType(segType);
+  }
+
+  NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt?");
+
+  return PR_TRUE;
+}
+
+PRUint32
+SVGPathData::GetPathSegAtLength(float aDistance) const
+{
+  // TODO [SVGWG issue] get specified what happen if 'aDistance' < 0, or
+  // 'aDistance' > the length of the path, or the seg list is empty.
+  // Return -1? Throwing would better help authors avoid tricky bugs (DOM
+  // could do that if we return -1).
+
+  double distRunningTotal = 0.0;
+  PRUint32 i = 0, segIndex = 0;
+  SVGPathTraversalState state;
+
+  while (i < mData.Length()) {
+    distRunningTotal += SVGPathSegUtils::GetLength(&mData[i], state);
+    if (distRunningTotal >= aDistance) {
+      return segIndex;
+    }
+    i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
+    segIndex++;
+  }
+
+  NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
+
+  return NS_MAX(0U, segIndex - 1); // -1 because while loop takes us 1 too far
+}
+
+void
+SVGPathData::ConstructPath(gfxContext *aCtx) const
+{
+  PRUint32 segType, prevSegType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
+  gfxPoint pathStart(0.0, 0.0); // start point of [sub]path
+  gfxPoint segEnd(0.0, 0.0);    // end point of previous/current segment
+  gfxPoint cp1, cp2;            // previous bezier's control points
+  gfxPoint tcp1, tcp2;          // temporaries
+
+  // Regarding cp1 and cp2: If the previous segment was a cubic bezier curve,
+  // then cp2 is its second control point. If the previous segment was a
+  // quadratic curve, then cp1 is its (only) control point.
+
+  PRUint32 i = 0;
+  while (i < mData.Length()) {
+    segType = SVGPathSegUtils::DecodeType(mData[i++]);
+
+    switch (segType)
+    {
+    case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
+      segEnd = pathStart;
+      aCtx->ClosePath();
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
+      pathStart = segEnd = gfxPoint(mData[i], mData[i+1]);
+      aCtx->MoveTo(segEnd);
+      i += 2;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
+      pathStart = segEnd += gfxPoint(mData[i], mData[i+1]);
+      aCtx->MoveTo(segEnd);
+      i += 2;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
+      segEnd = gfxPoint(mData[i], mData[i+1]);
+      aCtx->LineTo(segEnd);
+      i += 2;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
+      segEnd += gfxPoint(mData[i], mData[i+1]);
+      aCtx->LineTo(segEnd);
+      i += 2;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
+      cp1 = gfxPoint(mData[i], mData[i+1]);
+      cp2 = gfxPoint(mData[i+2], mData[i+3]);
+      segEnd = gfxPoint(mData[i+4], mData[i+5]);
+      aCtx->CurveTo(cp1, cp2, segEnd);
+      i += 6;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
+      cp1 = segEnd + gfxPoint(mData[i], mData[i+1]);
+      cp2 = segEnd + gfxPoint(mData[i+2], mData[i+3]);
+      segEnd += gfxPoint(mData[i+4], mData[i+5]);
+      aCtx->CurveTo(cp1, cp2, segEnd);
+      i += 6;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
+      cp1 = gfxPoint(mData[i], mData[i+1]);
+      // Convert quadratic curve to cubic curve:
+      tcp1 = segEnd + (cp1 - segEnd) * 2 / 3;
+      segEnd = gfxPoint(mData[i+2], mData[i+3]); // changed before setting tcp2!
+      tcp2 = cp1 + (segEnd - cp1) / 3;
+      aCtx->CurveTo(tcp1, tcp2, segEnd);
+      i += 4;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
+      cp1 = segEnd + gfxPoint(mData[i], mData[i+1]);
+      // Convert quadratic curve to cubic curve:
+      tcp1 = segEnd + (cp1 - segEnd) * 2 / 3;
+      segEnd += gfxPoint(mData[i+2], mData[i+3]); // changed before setting tcp2!
+      tcp2 = cp1 + (segEnd - cp1) / 3;
+      aCtx->CurveTo(tcp1, tcp2, segEnd);
+      i += 4;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
+    {
+      gfxPoint radii(mData[i], mData[i+1]);
+      gfxPoint start = segEnd;
+      gfxPoint end = gfxPoint(mData[i+5], mData[i+6]);
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) {
+        end += start;
+      }
+      segEnd = end;
+      if (start != end) {
+        if (radii.x == 0.0f || radii.y == 0.0f) {
+          aCtx->LineTo(end);
+          i += 7;
+          break;
+        }
+        nsSVGArcConverter converter(start, end, radii, mData[i+2],
+                                    mData[i+3] != 0, mData[i+4] != 0);
+        while (converter.GetNextSegment(&cp1, &cp2, &end)) {
+          aCtx->CurveTo(cp1, cp2, end);
+        }
+      }
+      i += 7;
+      break;
+    }
+
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
+      segEnd = gfxPoint(mData[i++], segEnd.y);
+      aCtx->LineTo(segEnd);
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
+      segEnd += gfxPoint(mData[i++], 0.0f);
+      aCtx->LineTo(segEnd);
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
+      segEnd = gfxPoint(segEnd.x, mData[i++]);
+      aCtx->LineTo(segEnd);
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
+      segEnd += gfxPoint(0.0f, mData[i++]);
+      aCtx->LineTo(segEnd);
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
+      cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segEnd * 2 - cp2 : segEnd;
+      cp2 = gfxPoint(mData[i],   mData[i+1]);
+      segEnd = gfxPoint(mData[i+2], mData[i+3]);
+      aCtx->CurveTo(cp1, cp2, segEnd);
+      i += 4;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
+      cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segEnd * 2 - cp2 : segEnd;
+      cp2 = segEnd + gfxPoint(mData[i], mData[i+1]);
+      segEnd += gfxPoint(mData[i+2], mData[i+3]);
+      aCtx->CurveTo(cp1, cp2, segEnd);
+      i += 4;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
+      cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segEnd * 2 - cp1 : segEnd;
+      // Convert quadratic curve to cubic curve:
+      tcp1 = segEnd + (cp1 - segEnd) * 2 / 3;
+      segEnd = gfxPoint(mData[i], mData[i+1]); // changed before setting tcp2!
+      tcp2 = cp1 + (segEnd - cp1) / 3;
+      aCtx->CurveTo(tcp1, tcp2, segEnd);
+      i += 2;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
+      cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segEnd * 2 - cp1 : segEnd;
+      // Convert quadratic curve to cubic curve:
+      tcp1 = segEnd + (cp1 - segEnd) * 2 / 3;
+      segEnd = segEnd + gfxPoint(mData[i], mData[i+1]); // changed before setting tcp2!
+      tcp2 = cp1 + (segEnd - cp1) / 3;
+      aCtx->CurveTo(tcp1, tcp2, segEnd);
+      i += 2;
+      break;
+
+    default:
+      NS_NOTREACHED("Bad path segment type");
+      return; // according to spec we'd use everything up to the bad seg anyway
+    }
+    prevSegType = segType;
+  }
+  NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
+}
+
+already_AddRefed<gfxFlattenedPath>
+SVGPathData::ToFlattenedPath(const gfxMatrix& aMatrix) const
+{
+  nsRefPtr<gfxContext> ctx =
+    new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
+
+  ctx->SetMatrix(aMatrix);
+  ConstructPath(ctx);
+  ctx->IdentityMatrix();
+
+  return ctx->GetFlattenedPath();
+}
+
+static PRBool IsMoveto(PRUint16 aSegType)
+{
+  return aSegType == nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS ||
+         aSegType == nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL;
+}
+
+static float AngleOfVector(gfxPoint v)
+{
+  // C99 says about atan2 "A domain error may occur if both arguments are
+  // zero" and "On a domain error, the function returns an implementation-
+  // defined value". In the case of atan2 the implementation-defined value
+  // seems to commonly be zero, but it could just as easily be a NaN value.
+  // We specifically want zero in this case, hence the check:
+
+  return (v != gfxPoint(0.0f, 0.0f)) ? atan2(v.y, v.x) : 0.0f;
+}
+
+// TODO replace callers with calls to AngleOfVector
+static double
+CalcVectorAngle(double ux, double uy, double vx, double vy)
+{
+  double ta = atan2(uy, ux);
+  double tb = atan2(vy, vx);
+  if (tb >= ta)
+    return tb-ta;
+  return 2 * M_PI - (ta-tb);
+}
+
+void
+SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
+{
+  // This code should assume that ANY type of segment can appear at ANY index.
+  // It should also assume that segments such as M and Z can appear in weird
+  // places, and repeat multiple times consecutively.
+
+  gfxPoint pathStart, segStart, segEnd;
+  gfxPoint cp1, cp2; // control points for current bezier curve
+  gfxPoint prevCP; // last control point of previous bezier curve
+
+  PRUint16 segType, prevSegType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
+
+  // info on the current [sub]path (reset every M command):
+  gfxPoint pathStartPoint(0, 0);
+  float pathStartAngle = 0;
+
+  float prevSegEndAngle = 0, segStartAngle = 0, segEndAngle = 0;
+
+  PRUint32 i = 0;
+  while (i < mData.Length()) {
+    segType = SVGPathSegUtils::DecodeType(mData[i++]); // advances i to args
+
+    switch (segType) // to find segStartAngle, segEnd and segEndAngle
+    {
+    case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
+      segEnd = pathStart;
+      segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS) {
+        segEnd = gfxPoint(mData[i], mData[i+1]);
+      } else {
+        segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
+      }
+      pathStart = segEnd;
+      // If authors are going to specify multiple consecutive moveto commands
+      // with markers, me might as well make the angle do something useful:
+      segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
+      i += 2;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS) {
+        segEnd = gfxPoint(mData[i], mData[i+1]);
+      } else {
+        segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
+      }
+      segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
+      i += 2;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS) {
+        cp1 = gfxPoint(mData[i],   mData[i+1]);
+        cp2 = gfxPoint(mData[i+2], mData[i+3]);
+        segEnd = gfxPoint(mData[i+4], mData[i+5]);
+      } else {
+        cp1 = segStart + gfxPoint(mData[i],   mData[i+1]);
+        cp2 = segStart + gfxPoint(mData[i+2], mData[i+3]);
+        segEnd = segStart + gfxPoint(mData[i+4], mData[i+5]);
+      }
+      prevCP = cp2;
+      if (cp1 == segStart) {
+        cp1 = cp2;
+      }
+      if (cp2 == segEnd) {
+        cp2 = cp1;
+      }
+      segStartAngle = AngleOfVector(cp1 - segStart);
+      segEndAngle = AngleOfVector(segEnd - cp2);
+      i += 6;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS) {
+        cp1 = gfxPoint(mData[i],   mData[i+1]);
+        segEnd = gfxPoint(mData[i+2], mData[i+3]);
+      } else {
+        cp1 = segStart + gfxPoint(mData[i],   mData[i+1]);
+        segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
+      }
+      prevCP = cp1;
+      segStartAngle = AngleOfVector(cp1 - segStart);
+      segEndAngle = AngleOfVector(segEnd - cp1);
+      i += 4;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
+    {
+      float rx = mData[i];
+      float ry = mData[i+1];
+      float angle = mData[i+2];
+      PRBool largeArcFlag = mData[i+3] != 0.0f;
+      PRBool sweepFlag = mData[i+4] != 0.0f;
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS) {
+        segEnd = gfxPoint(mData[i+5], mData[i+6]);
+      } else {
+        segEnd = segStart + gfxPoint(mData[i+5], mData[i+6]);
+      }
+
+      // See section F.6 of SVG 1.1 for details on what we're doing here:
+      // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+
+      if (segStart == segEnd) {
+        // F.6.2 says "If the endpoints (x1, y1) and (x2, y2) are identical,
+        // then this is equivalent to omitting the elliptical arc segment
+        // entirely." We take that very literally here, not adding a mark, and
+        // not even setting any of the 'prev' variables so that it's as if this
+        // arc had never existed; note the difference this will make e.g. if
+        // the arc is proceeded by a bezier curve and followed by a "smooth"
+        // bezier curve of the same degree!
+        i += 7;
+        continue;
+      }
+
+      // Below we have funny interleaving of F.6.6 (Correction of out-of-range
+      // radii) and F.6.5 (Conversion from endpoint to center parameterization)
+      // which is designed to avoid some unnecessary calculations.
+
+      if (rx == 0.0 || ry == 0.0) {
+        // F.6.6 step 1 - straight line or coincidental points
+        segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
+        i += 7;
+        break;
+      }
+      rx = fabs(rx); // F.6.6.1
+      ry = fabs(ry);
+
+      // F.6.5.1:
+      angle = angle * M_PI/180.0;
+      float x1p = cos(angle) * (segStart.x - segEnd.x) / 2.0
+                + sin(angle) * (segStart.y - segEnd.y) / 2.0;
+      float y1p = -sin(angle) * (segStart.x - segEnd.x) / 2.0
+                 + cos(angle)  *(segStart.y - segEnd.y) / 2.0;
+
+      // This is the root in F.6.5.2 and the numerator under that root:
+      float root;
+      float numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p;
+
+      if (numerator < 0.0) {
+        // F.6.6 step 3 - |numerator < 0.0| is equivalent to the result of
+        // F.6.6.2 (lamedh) being greater than one. What we have here is radii
+        // that do not reach between segStart and segEnd, so we need to correct
+        // them.
+        float lamedh = 1.0 - numerator/(rx*rx*ry*ry); // equiv to eqn F.6.6.2
+        float s = sqrt(lamedh);
+        rx *= s;  // F.6.6.3
+        ry *= s;
+        // rx and ry changed, so we have to recompute numerator
+        numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p;
+        NS_ABORT_IF_FALSE(numerator >= 0,
+                          "F.6.6.3 should prevent this. Will sqrt(-num)!");
+      }
+      root = sqrt(numerator/(rx*rx*y1p*y1p + ry*ry*x1p*x1p));
+      if (largeArcFlag == sweepFlag)
+        root = -root;
+
+      float cxp =  root * rx * y1p / ry;  // F.6.5.2
+      float cyp = -root * ry * x1p / rx;
+
+      float theta, delta;
+      theta = CalcVectorAngle(1.0, 0.0,  (x1p-cxp)/rx, (y1p-cyp)/ry); // F.6.5.5
+      delta  = CalcVectorAngle((x1p-cxp)/rx, (y1p-cyp)/ry,
+                               (-x1p-cxp)/rx, (-y1p-cyp)/ry);         // F.6.5.6
+      if (!sweepFlag && delta > 0)
+        delta -= 2.0 * M_PI;
+      else if (sweepFlag && delta < 0)
+        delta += 2.0 * M_PI;
+
+      float tx1, ty1, tx2, ty2;
+      tx1 = -cos(angle)*rx*sin(theta) - sin(angle)*ry*cos(theta);
+      ty1 = -sin(angle)*rx*sin(theta) + cos(angle)*ry*cos(theta);
+      tx2 = -cos(angle)*rx*sin(theta+delta) - sin(angle)*ry*cos(theta+delta);
+      ty2 = -sin(angle)*rx*sin(theta+delta) + cos(angle)*ry*cos(theta+delta);
+
+      if (delta < 0.0f) {
+        tx1 = -tx1;
+        ty1 = -ty1;
+        tx2 = -tx2;
+        ty2 = -ty2;
+      }
+
+      segStartAngle = atan2(ty1, tx1);
+      segEndAngle = atan2(ty2, tx2);
+      i += 7;
+      break;
+    }
+
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS) {
+        segEnd = gfxPoint(mData[i++], segStart.y);
+      } else {
+        segEnd = segStart + gfxPoint(mData[i++], 0.0f);
+      }
+      segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS) {
+        segEnd = gfxPoint(segStart.x, mData[i++]);
+      } else {
+        segEnd = segStart + gfxPoint(0.0f, mData[i++]);
+      }
+      segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
+      cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - prevCP : segStart;
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) {
+        cp2 = gfxPoint(mData[i], mData[i+1]);
+        segEnd = gfxPoint(mData[i+2], mData[i+3]);
+      } else {
+        cp2 = segStart + gfxPoint(mData[i], mData[i+1]);
+        segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
+      }
+      prevCP = cp2;
+      if (cp1 == segStart) {
+        cp1 = cp2;
+      }
+      if (cp2 == segEnd) {
+        cp2 = cp1;
+      }
+      segStartAngle = AngleOfVector(cp1 - segStart);
+      segEndAngle = AngleOfVector(segEnd - cp2);
+      i += 4;
+      break;
+
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
+    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
+      cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - prevCP : segStart;
+      if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS) {
+        segEnd = gfxPoint(mData[i], mData[i+1]);
+      } else {
+        segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
+      }
+      prevCP = cp1;
+      segStartAngle = AngleOfVector(cp1 - segStart);
+      segEndAngle = AngleOfVector(segEnd - cp1);
+      i += 2;
+      break;
+
+    default:
+      // Leave any existing marks in aMarks so we have a visual indication of
+      // when things went wrong.
+      NS_ABORT_IF_FALSE(PR_FALSE, "Unknown segment type - path corruption?");
+      return;
+    }
+
+    // Set the angle of the mark at the start of this segment:
+    if (aMarks->Length()) {
+      nsSVGMark &mark = aMarks->ElementAt(aMarks->Length() - 1);
+      if (!IsMoveto(segType) && IsMoveto(prevSegType)) {
+        // start of new subpath
+        pathStartAngle = mark.angle = segStartAngle;
+      } else if (IsMoveto(segType) && !IsMoveto(prevSegType)) {
+        // end of a subpath
+        if (prevSegType != nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH)
+          mark.angle = prevSegEndAngle;
+      } else {
+        if (!(segType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH &&
+              prevSegType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH))
+          mark.angle = nsSVGUtils::AngleBisect(prevSegEndAngle, segStartAngle);
+      }
+    }
+
+    // Add the mark at the end of this segment, and set its position:
+    if (!aMarks->AppendElement(nsSVGMark(segEnd.x, segEnd.y, 0))) {
+      aMarks->Clear(); // OOM, so try to free some
+      return;
+    }
+
+    if (segType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH &&
+        prevSegType != nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH) {
+      aMarks->ElementAt(aMarks->Length() - 1).angle =
+        //aMarks->ElementAt(pathStartIndex).angle =
+        nsSVGUtils::AngleBisect(segEndAngle, pathStartAngle);
+    }
+
+    prevSegType = segType;
+    prevSegEndAngle = segEndAngle;
+    segStart = segEnd;
+  }
+
+  NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
+
+  if (aMarks->Length() &&
+      prevSegType != nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH)
+    aMarks->ElementAt(aMarks->Length() - 1).angle = prevSegEndAngle;
+}
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPathData.h
@@ -0,0 +1,284 @@
+/* -*- 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_SVGPATHDATA_H__
+#define MOZILLA_SVGPATHDATA_H__
+
+#include "SVGPathSegUtils.h"
+#include "nsTArray.h"
+#include "nsSVGElement.h"
+
+class gfxContext;
+class gfxMatrix;
+class gfxFlattenedPath;
+class nsSVGPathDataParserToInternal;
+struct nsSVGMark;
+
+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 DOMSVGPathSegList.
+ *
+ * This class is not called |class SVGPathSegList| for one very good reason;
+ * this class does not provide a list of "SVGPathSeg" items, it provides an
+ * array of floats into which path segments are encoded. See the paragraphs
+ * that follow for why. Note that the Length() method returns the number of
+ * floats in our array, not the number of encoded segments, and the index
+ * operator indexes floats in the array, not segments. If this class were
+ * called SVGPathSegList the names of these methods would be very misleading.
+ *
+ * The reason this class is designed in this way is because there are many
+ * different types of path segment, each taking a different numbers of
+ * arguments. We want to store the segments in an nsTArray to avoid individual
+ * allocations for each item, but the different size of segments means we can't
+ * have one single segment type for the nsTArray (not without using a space
+ * wasteful union or something similar). Since the internal code does not need
+ * to index into the list (the DOM wrapper does, but it handles that itself)
+ * the obvious solution is to have the items in this class take up variable
+ * width and have the internal code iterate over these lists rather than index
+ * into them.
+ *
+ * Implementing indexing to segments with O(1) performance would require us to
+ * allocate and maintain a separate segment index table (keeping that table in
+ * sync when items are inserted or removed from the list). So long as the
+ * internal code doesn't require indexing to segments, we can avoid that
+ * overhead and additional complexity.
+ *
+ * Segment encoding: the first float in the encoding of a segment contains the
+ * segment's type. The segment's type is encoded to/decoded from this float
+ * using the static methods SVGPathSegUtils::EncodeType(PRUint32)/
+ * SVGPathSegUtils::DecodeType(float). If the path segment type in question
+ * takes any arguments then these follow the first float, and are in the same
+ * order as they are given in a <path> element's 'd' attribute (NOT in the
+ * order of the createSVGPathSegXxx() methods' arguments from the SVG DOM
+ * interface SVGPathElement, which are different...grr). Consumers can use
+ * SVGPathSegUtils::ArgCountForType(type) to determine how many arguments
+ * there are (if any), and thus where the current encoded segment ends, and
+ * where the next segment (if any) begins.
+ */
+class SVGPathData
+{
+  friend class SVGAnimatedPathSegList;
+  friend class DOMSVGPathSegList;
+  friend class DOMSVGPathSeg;
+  friend class ::nsSVGPathDataParserToInternal;
+  // nsSVGPathDataParserToInternal will not keep wrappers in sync, so consumers
+  // are responsible for that!
+
+public:
+
+  SVGPathData(){}
+  ~SVGPathData(){}
+
+  // 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 mData.IsEmpty();
+  }
+
+#ifdef DEBUG
+  /**
+   * This method iterates over the encoded segment data and countes the number
+   * of segments we currently have.
+   */
+  PRUint32 CountItems() const;
+#endif
+
+  /**
+   * Returns the number of *floats* in the encoding array, and NOT the number
+   * of segments encoded in this object. (For that, see CountItems() above.)
+   */
+  PRUint32 Length() const {
+    return mData.Length();
+  }
+
+  const float& operator[](PRUint32 aIndex) const {
+    return mData[aIndex];
+  }
+
+  // Used by nsSMILCompositor to check if the cached base val is out of date
+  PRBool operator==(const SVGPathData& rhs) const {
+    // We use memcmp so that we don't need to worry that the data encoded in
+    // the first float may have the same bit pattern as a NaN.
+    return mData.Length() == rhs.mData.Length() &&
+           memcmp(mData.Elements(), rhs.mData.Elements(),
+                  mData.Length() * sizeof(float)) == 0;
+  }
+
+  PRBool SetCapacity(PRUint32 aSize) {
+    return mData.SetCapacity(aSize);
+  }
+
+  void Compact() {
+    mData.Compact();
+  }
+
+
+  float GetPathLength() const;
+
+  PRUint32 GetPathSegAtLength(float aLength) const;
+
+  void GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const;
+
+  /**
+   * Returns PR_TRUE, except on OOM, in which case returns PR_FALSE.
+   */
+  PRBool GetSegmentLengths(nsTArray<double> *aLengths) const;
+
+  /**
+   * Returns PR_TRUE, except on OOM, in which case returns PR_FALSE.
+   */
+  PRBool GetDistancesFromOriginToEndsOfVisibleSegments(nsTArray<double> *aArray) const;
+
+  already_AddRefed<gfxFlattenedPath>
+  ToFlattenedPath(const gfxMatrix& aMatrix) const;
+
+  void ConstructPath(gfxContext *aCtx) const;
+
+  // 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
+  // SVGAnimatedPathSegList 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 SVGPathData& rhs);
+
+  float& operator[](PRUint32 aIndex) {
+    return mData[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 aLength) {
+    return mData.SetLength(aLength);
+  }
+
+  nsresult SetValueFromString(const nsAString& aValue);
+
+  void Clear() {
+    mData.Clear();
+  }
+
+  // Our DOM wrappers have direct access to our mData, so they directly
+  // manipulate it rather than us implementing:
+  //
+  // * InsertItem(PRUint32 aDataIndex, PRUint32 aType, const float *aArgs);
+  // * ReplaceItem(PRUint32 aDataIndex, PRUint32 aType, const float *aArgs);
+  // * RemoveItem(PRUint32 aDataIndex);
+  // * PRBool AppendItem(PRUint32 aType, const float *aArgs);
+
+  nsresult AppendSeg(PRUint32 aType, ...); // variable number of float args
+
+  nsTArray<float> mData;
+};
+
+
+/**
+ * This SVGPathData subclass is for SVGPathSegListSMILType which needs to
+ * have write access to the lists it works with.
+ *
+ * Instances of this class do not have DOM wrappers that need to be kept in
+ * sync, so we can safely expose any protected base class methods required by
+ * the SMIL code.
+ */
+class SVGPathDataAndOwner : public SVGPathData
+{
+public:
+
+  SVGPathDataAndOwner(nsSVGElement *aElement = nsnull)
+    : mElement(aElement)
+  {}
+
+  void SetElement(nsSVGElement *aElement) {
+    mElement = aElement;
+  }
+
+  nsSVGElement* Element() const {
+    return mElement;
+  }
+
+  nsresult CopyFrom(const SVGPathDataAndOwner& rhs) {
+    mElement = rhs.mElement;
+    return SVGPathData::CopyFrom(rhs);
+  }
+
+  /**
+   * Exposed so that SVGPathData baseVals can be copied to
+   * SVGPathDataAndOwner objects. Note that callers should also call
+   * SetElement() when using this method!
+   */
+  nsresult CopyFrom(const SVGPathData& rhs) {
+    return SVGPathData::CopyFrom(rhs);
+  }
+  const float& operator[](PRUint32 aIndex) const {
+    return SVGPathData::operator[](aIndex);
+  }
+  float& operator[](PRUint32 aIndex) {
+    return SVGPathData::operator[](aIndex);
+  }
+  PRBool SetLength(PRUint32 aNumberOfItems) {
+    return SVGPathData::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;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGPATHDATA_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPathSegListSMILType.cpp
@@ -0,0 +1,242 @@
+/* -*- 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 "SVGPathSegListSMILType.h"
+#include "nsSMILValue.h"
+#include "SVGPathData.h"
+#include <math.h>
+
+using namespace mozilla;
+
+/*static*/ SVGPathSegListSMILType SVGPathSegListSMILType::sSingleton;
+
+//----------------------------------------------------------------------
+// nsISMILType implementation
+
+void
+SVGPathSegListSMILType::Init(nsSMILValue &aValue) const
+{
+  NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type");
+  aValue.mU.mPtr = new SVGPathDataAndOwner();
+  aValue.mType = this;
+}
+
+void
+SVGPathSegListSMILType::Destroy(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
+  delete static_cast<SVGPathDataAndOwner*>(aValue.mU.mPtr);
+  aValue.mU.mPtr = nsnull;
+  aValue.mType = &nsSMILNullType::sSingleton;
+}
+
+nsresult
+SVGPathSegListSMILType::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 SVGPathDataAndOwner* src =
+    static_cast<const SVGPathDataAndOwner*>(aSrc.mU.mPtr);
+  SVGPathDataAndOwner* dest =
+    static_cast<SVGPathDataAndOwner*>(aDest.mU.mPtr);
+
+  return dest->CopyFrom(*src);
+}
+
+PRBool
+SVGPathSegListSMILType::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 SVGPathDataAndOwner*>(aLeft.mU.mPtr) ==
+         *static_cast<const SVGPathDataAndOwner*>(aRight.mU.mPtr);
+}
+
+nsresult
+SVGPathSegListSMILType::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");
+
+  SVGPathDataAndOwner& dest =
+    *static_cast<SVGPathDataAndOwner*>(aDest.mU.mPtr);
+  const SVGPathDataAndOwner& valueToAdd =
+    *static_cast<const SVGPathDataAndOwner*>(aValueToAdd.mU.mPtr);
+
+  if (dest.Length() != valueToAdd.Length()) {
+    // Allow addition to empty dest:
+    if (dest.Length() == 0) {
+      return dest.CopyFrom(valueToAdd);
+    }
+    // For now we only support animation to a list with the same number of
+    // items (and with the same segment types).
+    // nsSVGUtils::ReportToConsole
+    return NS_ERROR_FAILURE;
+  }
+
+  PRUint32 i = 0;
+  while (i < dest.Length()) {
+    PRUint32 type = SVGPathSegUtils::DecodeType(dest[i]);
+    if (type != SVGPathSegUtils::DecodeType(valueToAdd[i])) {
+      // nsSVGUtils::ReportToConsole - can't yet animate between different
+      // types, although it would make sense to allow animation between
+      // some.
+      return NS_ERROR_FAILURE;
+    }
+    i++;
+    if ((type == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS ||
+         type == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) &&
+        (dest[i+3] != valueToAdd[i+3] || dest[i+4] != valueToAdd[i+4])) {
+      // boolean args largeArcFlag and sweepFlag must be the same
+      return NS_ERROR_FAILURE;
+    }
+    PRUint32 segEnd = i + SVGPathSegUtils::ArgCountForType(type);
+    for (; i < segEnd; ++i) {
+      dest[i] += valueToAdd[i];
+    }
+  }
+
+  NS_ABORT_IF_FALSE(i == dest.Length(), "Very, very bad - path data corrupt");
+
+  // For now we only support pure 'to' animation.
+  // nsSVGUtils::ReportToConsole
+  return NS_OK;
+}
+
+nsresult
+SVGPathSegListSMILType::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");
+
+  // See https://bugzilla.mozilla.org/show_bug.cgi?id=522306#c18
+
+  // nsSVGUtils::ReportToConsole
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+SVGPathSegListSMILType::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 SVGPathDataAndOwner& start =
+    *static_cast<const SVGPathDataAndOwner*>(aStartVal.mU.mPtr);
+  const SVGPathDataAndOwner& end =
+    *static_cast<const SVGPathDataAndOwner*>(aEndVal.mU.mPtr);
+  SVGPathDataAndOwner& result =
+    *static_cast<SVGPathDataAndOwner*>(aResult.mU.mPtr);
+
+  if (start.Length() != end.Length() && start.Length() != 0) {
+    // For now we only support animation to a list with the same number of
+    // items (and with the same segment types).
+    // nsSVGUtils::ReportToConsole
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!result.SetLength(end.Length())) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  PRUint32 i = 0;
+
+  if (start.Length() == 0) { // identity path
+    while (i < end.Length()) {
+      PRUint32 type = SVGPathSegUtils::DecodeType(end[i]);
+      result[i] = end[i];
+      i++;
+      PRUint32 segEnd = i + SVGPathSegUtils::ArgCountForType(type);
+      if ((type == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS ||
+           type == nsIDOMSVGPathSeg::PATHSEG_ARC_REL)) {
+        result[i] = end[i] * aUnitDistance;
+        result[i+1] = end[i+1] * aUnitDistance;
+        result[i+2] = end[i+2] * aUnitDistance;
+        // boolean args largeArcFlag and sweepFlag must be the same
+        result[i+3] = end[i+3];
+        result[i+4] = end[i+4];
+        result[i+5] = end[i+5] * aUnitDistance;
+        result[i+6] = end[i+6] * aUnitDistance;
+        i = segEnd;
+      } else {
+        for (; i < segEnd; ++i) {
+          result[i] = end[i] * aUnitDistance;
+        }
+      }
+    }
+  } else {
+    while (i < end.Length()) {
+      PRUint32 type = SVGPathSegUtils::DecodeType(end[i]);
+      if (type != SVGPathSegUtils::DecodeType(start[i])) {
+        // nsSVGUtils::ReportToConsole - can't yet interpolate between different
+        // types, although it would make sense to allow interpolation between
+        // some.
+        return NS_ERROR_FAILURE;
+      }
+      result[i] = end[i];
+      i++;
+      if ((type == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS ||
+           type == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) &&
+          (start[i+3] != end[i+3] || start[i+4] != end[i+4])) {
+        // boolean args largeArcFlag and sweepFlag must be the same
+        return NS_ERROR_FAILURE;
+      }
+      PRUint32 segEnd = i + SVGPathSegUtils::ArgCountForType(type);
+      for (; i < segEnd; ++i) {
+        result[i] = start[i] + (end[i] - start[i]) * aUnitDistance;
+      }
+    }
+  }
+
+  NS_ABORT_IF_FALSE(i == end.Length(), "Very, very bad - path data corrupt");
+
+  return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPathSegListSMILType.h
@@ -0,0 +1,86 @@
+/* -*- 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 ***** */
+
+#ifndef MOZILLA_SVGPATHSEGLISTSMILTYPE_H_
+#define MOZILLA_SVGPATHSEGLISTSMILTYPE_H_
+
+#include "nsISMILType.h"
+
+class nsSMILValue;
+
+namespace mozilla {
+
+////////////////////////////////////////////////////////////////////////
+// SVGPathSegListSMILType
+//
+// Operations for animating an SVGPathData.
+//
+class SVGPathSegListSMILType : public nsISMILType
+{
+public:
+  // Singleton for nsSMILValue objects to hold onto.
+  static SVGPathSegListSMILType sSingleton;
+
+protected:
+  // nsISMILType Methods
+  // -------------------
+
+  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.
+  SVGPathSegListSMILType() {}
+  ~SVGPathSegListSMILType() {}
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGPATHSEGLISTSMILTYPE_H_
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPathSegUtils.cpp
@@ -0,0 +1,461 @@
+/* -*- 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 "SVGPathSegUtils.h"
+#include "nsSVGElement.h"
+#include "nsSVGSVGElement.h"
+#include "nsSVGPathDataParser.h"
+#include "nsString.h"
+#include "nsSVGUtils.h"
+#include "nsContentUtils.h"
+#include "nsTextFormatter.h"
+#include "prdtoa.h"
+#include <limits>
+#include "nsMathUtils.h"
+#include "prtypes.h"
+
+using namespace mozilla;
+
+static const float PATH_SEG_LENGTH_TOLERANCE = 0.0000001f;
+static const PRUint32 MAX_RECURSION = 10;
+
+
+/* static */ void
+SVGPathSegUtils::GetValueAsString(const float *aSeg, nsAString& aValue)
+{
+  // Adding new seg type? Is the formatting below acceptable for the new types?
+  PR_STATIC_ASSERT(NS_SVG_PATH_SEG_MAX_ARGS == 7);
+
+  PRUint32 type = DecodeType(aSeg[0]);
+  PRUnichar typeAsChar = GetPathSegTypeAsLetter(type);
+
+  // Special case arcs:
+  if (type == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS ||
+      type == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) {
+    PRBool largeArcFlag = aSeg[4] != 0.0f;
+    PRBool sweepFlag = aSeg[5] != 0.0f;
+    nsTextFormatter::ssprintf(aValue,
+                              NS_LITERAL_STRING("%c%g,%g %g %d,%d %g,%g").get(),
+                              typeAsChar, aSeg[1], aSeg[2], aSeg[3],
+                              largeArcFlag, sweepFlag, aSeg[6], aSeg[7]);
+  } else {
+
+    switch (ArgCountForType(type)) {
+    case 0:
+      aValue = typeAsChar;
+      break;
+
+    case 1:
+      nsTextFormatter::ssprintf(aValue, NS_LITERAL_STRING("%c%g").get(),
+                                typeAsChar, aSeg[1]);
+      break;
+
+    case 2:
+      nsTextFormatter::ssprintf(aValue, NS_LITERAL_STRING("%c%g,%g").get(),
+                                typeAsChar, aSeg[1], aSeg[2]);
+      break;
+
+    case 4:
+      nsTextFormatter::ssprintf(aValue, NS_LITERAL_STRING("%c%g,%g %g,%g").get(),
+                                typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4]);
+      break;
+
+    case 6:
+      nsTextFormatter::ssprintf(aValue,
+                                NS_LITERAL_STRING("%c%g,%g %g,%g %g,%g").get(),
+                                typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4],
+                                aSeg[5], aSeg[6]);
+      break;
+
+    default:
+      NS_ABORT_IF_FALSE(PR_FALSE, "Unknown segment type");
+      aValue = NS_LITERAL_STRING("<unknown-segment-type>").get();
+      return;
+    }
+  }
+  
+  // nsTextFormatter::ssprintf is one of the nsTextFormatter methods that
+  // randomly appends '\0' to its output string, which means that the length
+  // of the output string is one too long. We need to manually remove that '\0'
+  // until nsTextFormatter is fixed.
+  //
+  if (aValue[aValue.Length() - 1] == PRUnichar('\0')) {
+    aValue.SetLength(aValue.Length() - 1);
+  }
+}
+
+
+static float
+CalcDistanceBetweenPoints(const gfxPoint &p1, const gfxPoint &p2)
+{
+  return NS_hypot(p2.x - p1.x, p2.y - p1.y);
+}
+
+
+static void SplitQuadraticBezier(const gfxPoint *curve,
+                                 gfxPoint *left,
+                                 gfxPoint *right)
+{
+  left[0].x = curve[0].x;
+  left[0].y = curve[0].y;
+  right[2].x = curve[2].x;
+  right[2].y = curve[2].y;
+  left[1].x = (curve[0].x + curve[1].x) / 2;
+  left[1].y = (curve[0].y + curve[1].y) / 2;
+  right[1].x = (curve[1].x + curve[2].x) / 2;
+  right[1].y = (curve[1].y + curve[2].y) / 2;
+  left[2].x = right[0].x = (left[1].x + right[1].x) / 2;
+  left[2].y = right[0].y = (left[1].y + right[1].y) / 2;
+}
+
+static void SplitCubicBezier(const gfxPoint *curve,
+                             gfxPoint *left,
+                             gfxPoint *right)
+{
+  gfxPoint tmp;
+  tmp.x = (curve[1].x + curve[2].x) / 4;
+  tmp.y = (curve[1].y + curve[2].y) / 4;
+  left[0].x = curve[0].x;
+  left[0].y = curve[0].y;
+  right[3].x = curve[3].x;
+  right[3].y = curve[3].y;
+  left[1].x = (curve[0].x + curve[1].x) / 2;
+  left[1].y = (curve[0].y + curve[1].y) / 2;
+  right[2].x = (curve[2].x + curve[3].x) / 2;
+  right[2].y = (curve[2].y + curve[3].y) / 2;
+  left[2].x = left[1].x / 2 + tmp.x;
+  left[2].y = left[1].y / 2 + tmp.y;
+  right[1].x = right[2].x / 2 + tmp.x;
+  right[1].y = right[2].y / 2 + tmp.y;
+  left[3].x = right[0].x = (left[2].x + right[1].x) / 2;
+  left[3].y = right[0].y = (left[2].y + right[1].y) / 2;
+}
+
+static gfxFloat CalcBezLengthHelper(gfxPoint *curve, PRUint32 numPts,
+                                    PRUint32 recursion_count,
+                                    void (*split)(const gfxPoint*, gfxPoint*, gfxPoint*))
+{
+  gfxPoint left[4];
+  gfxPoint right[4];
+  gfxFloat length = 0, dist;
+  for (PRUint32 i = 0; i < numPts - 1; i++) {
+    length += CalcDistanceBetweenPoints(curve[i], curve[i+1]);
+  }
+  dist = CalcDistanceBetweenPoints(curve[0], curve[numPts - 1]);
+  if (length - dist > PATH_SEG_LENGTH_TOLERANCE && recursion_count < MAX_RECURSION) {
+    split(curve, left, right);
+    ++recursion_count;
+    return CalcBezLengthHelper(left, numPts, recursion_count, split) +
+           CalcBezLengthHelper(right, numPts, recursion_count, split);
+  }
+  return length;
+}
+
+static inline gfxFloat
+CalcLengthOfCubicBezier(const gfxPoint &pos, const gfxPoint &cp1,
+                        const gfxPoint &cp2, const gfxPoint &to)
+{
+  gfxPoint curve[4] = { pos, cp1, cp2, to };
+  return CalcBezLengthHelper(curve, 4, 0, SplitCubicBezier);
+}
+
+static inline gfxFloat
+CalcLengthOfQuadraticBezier(const gfxPoint &pos, const gfxPoint &cp,
+                            const gfxPoint &to)
+{
+  gfxPoint curve[3] = { pos, cp, to };
+  return CalcBezLengthHelper(curve, 3, 0, SplitQuadraticBezier);
+}
+
+
+static float GetLengthOfClosePath(const float *aArgs, SVGPathTraversalState &aState)
+{
+  float dist = CalcDistanceBetweenPoints(aState.pos, aState.start);
+  aState.pos = aState.cp1 = aState.cp2 = aState.start;
+  return dist;
+}
+
+static float GetLengthOfMovetoAbs(const float *aArgs, SVGPathTraversalState &aState)
+{
+  aState.start = aState.pos = aState.cp1 = aState.cp2 = gfxPoint(aArgs[0], aArgs[1]);
+  return 0.0;
+}
+
+static float GetLengthOfMovetoRel(const float *aArgs, SVGPathTraversalState &aState)
+{
+  // aState.pos must be second from right due to +=
+  aState.start = aState.cp1 = aState.cp2 = aState.pos += gfxPoint(aArgs[0], aArgs[1]);
+  return 0.0;
+}
+
+static float GetLengthOfLinetoAbs(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint to(aArgs[0], aArgs[1]);
+  float dist = CalcDistanceBetweenPoints(aState.pos, to);
+  aState.pos = aState.cp1 = aState.cp2 = to;
+  return dist;
+}
+
+static float GetLengthOfLinetoRel(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint to = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
+  float dist = CalcDistanceBetweenPoints(aState.pos, to);
+  aState.pos = aState.cp1 = aState.cp2 = to;
+  return dist;
+}
+
+static float
+GetLengthOfLinetoHorizontalAbs(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint to(aArgs[0], aState.pos.y);
+  float dist = fabs(to.x - aState.pos.x);
+  aState.pos = aState.cp1 = aState.cp2 = to;
+  return dist;
+}
+
+static float
+GetLengthOfLinetoHorizontalRel(const float *aArgs, SVGPathTraversalState &aState)
+{
+  aState.cp1 = aState.cp2 = aState.pos += gfxPoint(aArgs[0], 0.0);
+  return fabs(aArgs[0]);
+}
+
+static float
+GetLengthOfLinetoVerticalAbs(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint to(aState.pos.x, aArgs[0]);
+  float dist = fabs(to.y - aState.pos.y);
+  aState.pos = aState.cp1 = aState.cp2 = to;
+  return dist;
+}
+
+static float
+GetLengthOfLinetoVerticalRel(const float *aArgs, SVGPathTraversalState &aState)
+{
+  aState.cp1 = aState.cp2 = aState.pos += gfxPoint(0.0, aArgs[0]);
+  return fabs(aArgs[0]);
+}
+
+static float GetLengthOfCurvetoCubicAbs(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint cp1(aArgs[0], aArgs[1]);
+  gfxPoint cp2(aArgs[2], aArgs[3]);
+  gfxPoint to(aArgs[4], aArgs[5]);
+
+  float dist = (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
+
+  aState.cp2 = cp2;
+  aState.pos = aState.cp1 = to;
+
+  return dist;
+}
+
+static float
+GetLengthOfCurvetoCubicSmoothAbs(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint cp1 = aState.pos - (aState.cp2 - aState.pos);
+  gfxPoint cp2(aArgs[0], aArgs[1]);
+  gfxPoint to(aArgs[2], aArgs[3]);
+
+  float dist = (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
+
+  aState.cp2 = cp2;
+  aState.pos = aState.cp1 = to;
+
+  return dist;
+}
+
+static float
+GetLengthOfCurvetoCubicRel(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint cp1 = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
+  gfxPoint cp2 = aState.pos + gfxPoint(aArgs[2], aArgs[3]);
+  gfxPoint to  = aState.pos + gfxPoint(aArgs[4], aArgs[5]);
+
+  float dist = (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
+
+  aState.cp2 = cp2;
+  aState.pos = aState.cp1 = to;
+
+  return dist;
+}
+
+static float
+GetLengthOfCurvetoCubicSmoothRel(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint cp1 = aState.pos - (aState.cp2 - aState.pos);
+  gfxPoint cp2 = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
+  gfxPoint to  = aState.pos + gfxPoint(aArgs[2], aArgs[3]);
+
+  float dist = (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
+
+  aState.cp2 = cp2;
+  aState.pos = aState.cp1 = to;
+
+  return dist;
+}
+
+static float
+GetLengthOfCurvetoQuadraticAbs(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint cp(aArgs[0], aArgs[1]);
+  gfxPoint to(aArgs[2], aArgs[3]);
+
+  float dist = (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
+
+  aState.cp1 = cp;
+  aState.pos = aState.cp2 = to;
+
+  return dist;
+}
+
+static float
+GetLengthOfCurvetoQuadraticSmoothAbs(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint cp = aState.pos - (aState.cp1 - aState.pos);
+  gfxPoint to(aArgs[0], aArgs[1]);
+
+  float dist = (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
+
+  aState.cp1 = cp;
+  aState.pos = aState.cp2 = to;
+
+  return dist;
+}
+
+static float
+GetLengthOfCurvetoQuadraticRel(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint cp = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
+  gfxPoint to = aState.pos + gfxPoint(aArgs[2], aArgs[3]);
+
+  float dist = (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
+
+  aState.cp1 = cp;
+  aState.pos = aState.cp2 = to;
+
+  return dist;
+}
+
+static float
+GetLengthOfCurvetoQuadraticSmoothRel(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint cp = aState.pos - (aState.cp1 - aState.pos);
+  gfxPoint to = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
+
+  float dist = (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
+
+  aState.cp1 = cp;
+  aState.pos = aState.cp2 = to;
+
+  return dist;
+}
+
+static float
+GetLengthOfArcAbs(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint radii(aArgs[0], aArgs[1]);
+  gfxPoint to(aArgs[5], aArgs[6]);
+  gfxPoint bez[4] = { aState.pos, gfxPoint(0,0), gfxPoint(0,0), gfxPoint(0,0) };
+  nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2],
+                              aArgs[3] != 0, aArgs[4] != 0);
+  float dist = 0;
+  while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3]))
+  {
+    dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
+    bez[0] = bez[3];
+  }
+  aState.pos = aState.cp1 = aState.cp2 = to;
+  return dist;
+}
+
+static float
+GetLengthOfArcRel(const float *aArgs, SVGPathTraversalState &aState)
+{
+  gfxPoint radii(aArgs[0], aArgs[1]);
+  gfxPoint to = aState.pos + gfxPoint(aArgs[5], aArgs[6]);
+  gfxPoint bez[4] = { aState.pos, gfxPoint(0,0), gfxPoint(0,0), gfxPoint(0,0) };
+  nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2],
+                              aArgs[3] != 0, aArgs[4] != 0);
+  float dist = 0;
+  while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3]))
+  {
+    dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
+    bez[0] = bez[3];
+  }
+  aState.pos = aState.cp1 = aState.cp2 = to;
+  return dist;
+}
+
+
+typedef float (*getLengthFunc)(const float*, SVGPathTraversalState&);
+
+/* static */ float
+SVGPathSegUtils::GetLength(const float *seg, SVGPathTraversalState &aState)
+{
+  PRUint32 type = DecodeType(seg[0]);
+
+  static getLengthFunc lengthFuncTable[20] = {
+    nsnull, //  0 == PATHSEG_UNKNOWN
+    GetLengthOfClosePath,
+    GetLengthOfMovetoAbs,
+    GetLengthOfMovetoRel,
+    GetLengthOfLinetoAbs,
+    GetLengthOfLinetoRel,
+    GetLengthOfCurvetoCubicAbs,
+    GetLengthOfCurvetoCubicRel,
+    GetLengthOfCurvetoQuadraticAbs,
+    GetLengthOfCurvetoQuadraticRel,
+    GetLengthOfArcAbs,
+    GetLengthOfArcRel,
+    GetLengthOfLinetoHorizontalAbs,
+    GetLengthOfLinetoHorizontalRel,
+    GetLengthOfLinetoVerticalAbs,
+    GetLengthOfLinetoVerticalRel,
+    GetLengthOfCurvetoCubicSmoothAbs,
+    GetLengthOfCurvetoCubicSmoothRel,
+    GetLengthOfCurvetoQuadraticSmoothAbs,
+    GetLengthOfCurvetoQuadraticSmoothRel
+  };
+
+  NS_ABORT_IF_FALSE(IsValidType(type), "Seg type not recognized");
+
+  NS_ABORT_IF_FALSE(type > 0 && type < NS_ARRAY_LENGTH(lengthFuncTable),
+                    "Seg type not recognized");
+
+  return lengthFuncTable[type](seg + 1, aState);
+}
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPathSegUtils.h
@@ -0,0 +1,217 @@
+/* -*- 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_SVGPATHSEGUTILS_H__
+#define MOZILLA_SVGPATHSEGUTILS_H__
+
+#include "nsIDOMSVGPathSeg.h"
+#include "nsIContent.h"
+#include "nsAString.h"
+#include "nsContentUtils.h"
+#include "gfxPoint.h"
+
+#define NS_SVG_PATH_SEG_MAX_ARGS 7
+
+namespace mozilla {
+
+/**
+ * Code that works with path segments can use an instance of this class to
+ * store/provide information about the start of the current subpath and the
+ * last path segment (if any).
+ */
+struct SVGPathTraversalState
+{
+  SVGPathTraversalState()
+    : start(0.0, 0.0)
+    , pos(0.0, 0.0)
+    , cp1(0.0, 0.0)
+    , cp2(0.0, 0.0)
+  {}
+
+  gfxPoint start; // start point of current sub path (reset each moveto)
+
+  gfxPoint pos;   // current position (end point of previous segment)
+
+  gfxPoint cp1;   // quadratic control point - if the previous segment was a
+                  // quadratic bezier curve then this is set to the absolute
+                  // position of its control point, otherwise its set to pos
+
+  gfxPoint cp2;   // cubic control point - if the previous segment was a cubic
+                  // bezier curve then this is set to the absolute position of
+                  // its second control point, otherwise it's set to pos
+};
+
+
+/**
+ * This class is just a collection of static methods - it doesn't have any data
+ * members, and it's not possible to create instances of this class. This class
+ * exists purely as a convenient place to gather together a bunch of methods
+ * related to manipulating and answering questions about path segments.
+ * Internally we represent path segments purely as an array of floats. See the
+ * comment documenting SVGPathData for more info on that.
+ *
+ * The DOM wrapper classes for encoded path segments (data contained in
+ * instances of SVGPathData) is DOMSVGPathSeg and its sub-classes. Note that
+ * there are multiple different DOM classes for path segs - one for each of the
+ * 19 SVG 1.1 segment types.
+ */
+class SVGPathSegUtils
+{
+private:
+  SVGPathSegUtils(){} // private to prevent instances
+
+public:
+
+  static void GetValueAsString(const float *aSeg, nsAString& aValue);
+
+  /**
+   * Encode a segment type enum to a float.
+   *
+   * At some point in the future we will likely want to encode other
+   * information into the float, such as whether the command was explicit or
+   * not. For now all this method does is save on int to float runtime
+   * conversion by requiring PRUint32 and float to be of the same size so we
+   * can simply do a bitwise PRUint32<->float copy.
+   */
+  static float EncodeType(PRUint32 aType) {
+    PR_STATIC_ASSERT(sizeof(PRUint32) == sizeof(float));
+    NS_ABORT_IF_FALSE(IsValidType(aType), "Seg type not recognized");
+    return *(reinterpret_cast<float*>(&aType));
+  }
+
+  static PRUint32 DecodeType(float aType) {
+    PR_STATIC_ASSERT(sizeof(PRUint32) == sizeof(float));
+    PRUint32 type = *(reinterpret_cast<PRUint32*>(&aType));
+    NS_ABORT_IF_FALSE(IsValidType(type), "Seg type not recognized");
+    return type;
+  }
+
+  static PRUnichar GetPathSegTypeAsLetter(PRUint32 aType) {
+    NS_ABORT_IF_FALSE(IsValidType(aType), "Seg type not recognized");
+
+    static const PRUnichar table[] = {
+      PRUnichar('x'),  //  0 == PATHSEG_UNKNOWN
+      PRUnichar('z'),  //  1 == PATHSEG_CLOSEPATH
+      PRUnichar('M'),  //  2 == PATHSEG_MOVETO_ABS
+      PRUnichar('m'),  //  3 == PATHSEG_MOVETO_REL
+      PRUnichar('L'),  //  4 == PATHSEG_LINETO_ABS
+      PRUnichar('l'),  //  5 == PATHSEG_LINETO_REL
+      PRUnichar('C'),  //  6 == PATHSEG_CURVETO_CUBIC_ABS
+      PRUnichar('c'),  //  7 == PATHSEG_CURVETO_CUBIC_REL
+      PRUnichar('Q'),  //  8 == PATHSEG_CURVETO_QUADRATIC_ABS
+      PRUnichar('q'),  //  9 == PATHSEG_CURVETO_QUADRATIC_REL
+      PRUnichar('A'),  // 10 == PATHSEG_ARC_ABS
+      PRUnichar('a'),  // 11 == PATHSEG_ARC_REL
+      PRUnichar('H'),  // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
+      PRUnichar('h'),  // 13 == PATHSEG_LINETO_HORIZONTAL_REL
+      PRUnichar('V'),  // 14 == PATHSEG_LINETO_VERTICAL_ABS
+      PRUnichar('v'),  // 15 == PATHSEG_LINETO_VERTICAL_REL
+      PRUnichar('S'),  // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
+      PRUnichar('s'),  // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
+      PRUnichar('T'),  // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
+      PRUnichar('t')   // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
+    };
+
+    return table[aType];
+  }
+
+  static PRUint32 ArgCountForType(PRUint32 aType) {
+    NS_ABORT_IF_FALSE(IsValidType(aType), "Seg type not recognized");
+
+    static const PRUint8 table[] = {
+      0,  //  0 == PATHSEG_UNKNOWN
+      0,  //  1 == PATHSEG_CLOSEPATH
+      2,  //  2 == PATHSEG_MOVETO_ABS
+      2,  //  3 == PATHSEG_MOVETO_REL
+      2,  //  4 == PATHSEG_LINETO_ABS
+      2,  //  5 == PATHSEG_LINETO_REL
+      6,  //  6 == PATHSEG_CURVETO_CUBIC_ABS
+      6,  //  7 == PATHSEG_CURVETO_CUBIC_REL
+      4,  //  8 == PATHSEG_CURVETO_QUADRATIC_ABS
+      4,  //  9 == PATHSEG_CURVETO_QUADRATIC_REL
+      7,  // 10 == PATHSEG_ARC_ABS
+      7,  // 11 == PATHSEG_ARC_REL
+      1,  // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
+      1,  // 13 == PATHSEG_LINETO_HORIZONTAL_REL
+      1,  // 14 == PATHSEG_LINETO_VERTICAL_ABS
+      1,  // 15 == PATHSEG_LINETO_VERTICAL_REL
+      4,  // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
+      4,  // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
+      2,  // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
+      2   // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
+    };
+
+    return table[aType];
+  }
+
+  /**
+   * Convenience so that callers can pass a float containing an encoded type
+   * and have it decoded implicitly.
+   */
+  static PRUint32 ArgCountForType(float aType) {
+    return ArgCountForType(DecodeType(aType));
+  }
+
+  static inline PRBool IsValidType(PRUint32 aType) {
+    return aType >= nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH &&
+           aType <= nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
+  }
+
+  static inline PRBool IsCubicType(PRUint32 aType) {
+    return aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL ||
+           aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS ||
+           aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
+           aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
+  }
+
+  static inline PRBool IsQuadraticType(PRUint32 aType) {
+    return aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL ||
+           aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS ||
+           aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
+           aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
+  }
+
+  /**
+   * Returns the user unit length of tracing along the path segment.
+   */
+  static float GetLength(const float *aSeg, SVGPathTraversalState &aState);
+
+  static void ToString(const float *aSeg, nsAString& aValue);
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGPATHSEGUTILS_H__
--- a/content/svg/content/src/crashtests/crashtests.list
+++ b/content/svg/content/src/crashtests/crashtests.list
@@ -41,17 +41,17 @@ load 414188-1.svg
 load 427325-1.svg
 load 428228-1.svg
 load 428841-1.svg
 load 435209-1.svg
 load 436418-mpathRoot-1.svg
 load 448244-1.svg
 load 466576-1.xhtml
 load 499879-1.svg
-asserts(0-2) load 535691-1.svg # Bug 535691
+load 535691-1.svg
 load 539167-1.svg
 load 573316-1.svg
 load 579356-1.svg
 load 579356-2.svg
 load 595608-1.svg
 load 601251-1.html
 load 601406-1.svg
 load 603145-1.svg
--- a/content/svg/content/src/nsSVGAnimateMotionElement.h
+++ b/content/svg/content/src/nsSVGAnimateMotionElement.h
@@ -69,16 +69,21 @@ public:
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   // nsISMILAnimationElement
   virtual nsSMILAnimationFunction& AnimationFunction();
   virtual PRBool GetTargetAttributeName(PRInt32 *aNamespaceID,
                                         nsIAtom **aLocalName) const;
   virtual nsSMILTargetAttrType GetTargetAttributeType() const;
 
+  // nsSVGElement
+  virtual nsIAtom* GetPathDataAttrName() const {
+    return nsGkAtoms::path;
+  }
+
   // Utility method to let our <mpath> children tell us when they've changed,
   // so we can make sure our mAnimationFunction is marked as having changed.
   void MpathChanged() { mAnimationFunction.MpathChanged(); }
 
   virtual nsXPCClassInfo* GetClassInfo();
 };
 
 #endif // NS_SVGANIMATEMOTIONELEMENT_H_
--- a/content/svg/content/src/nsSVGAnimationElement.cpp
+++ b/content/svg/content/src/nsSVGAnimationElement.cpp
@@ -453,16 +453,18 @@ nsSVGAnimationElement::BeginElement(void
 {
   return BeginElementAt(0.f);
 }
 
 /* void beginElementAt (in float offset); */
 NS_IMETHODIMP
 nsSVGAnimationElement::BeginElementAt(float offset)
 {
+  NS_ENSURE_FINITE(offset, NS_ERROR_ILLEGAL_VALUE);
+
   // This will fail if we're not attached to a time container (SVG document
   // fragment).
   nsresult rv = mTimedElement.BeginElementAt(offset);
   if (NS_FAILED(rv))
     return rv;
 
   AnimationNeedsResample();
 
@@ -475,16 +477,18 @@ nsSVGAnimationElement::EndElement(void)
 {
   return EndElementAt(0.f);
 }
 
 /* void endElementAt (in float offset); */
 NS_IMETHODIMP
 nsSVGAnimationElement::EndElementAt(float offset)
 {
+  NS_ENSURE_FINITE(offset, NS_ERROR_ILLEGAL_VALUE);
+
   nsresult rv = mTimedElement.EndElementAt(offset);
   if (NS_FAILED(rv))
     return rv;
 
   AnimationNeedsResample();
  
   return NS_OK;
 }
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -70,16 +70,17 @@
 #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 "SVGAnimatedPathSegList.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsIDOMSVGNumberList.h"
 #include "nsIDOMSVGAnimatedNumberList.h"
 #include "nsIDOMSVGPointList.h"
 #include "nsIDOMSVGAnimatedPoints.h"
 #include "nsIDOMSVGTransformList.h"
 #include "nsIDOMSVGAnimTransformList.h"
 #include "nsIDOMSVGAnimatedRect.h"
@@ -174,16 +175,19 @@ nsSVGElement::Init()
   }
 
   LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
 
   for (i = 0; i < lengthListInfo.mLengthListCount; i++) {
     lengthListInfo.Reset(i);
   }
 
+  // No need to reset SVGPathData since the default value in always the same
+  // (an empty list).
+
   StringAttributesInfo stringInfo = GetStringInfo();
 
   for (i = 0; i < stringInfo.mStringCount; i++) {
     stringInfo.Reset(i);
   }
 
   return NS_OK;
 }
@@ -369,16 +373,32 @@ nsSVGElement::ParseAttribute(PRInt32 aNa
           }
           foundMatch = PR_TRUE;
           break;
         }
       }
     }
 
     if (!foundMatch) {
+      // Check for SVGAnimatedPathSegList attribute
+      if (GetPathDataAttrName() == aAttribute) {
+        SVGAnimatedPathSegList* segList = GetAnimPathSegList();
+        if (segList) {
+          rv = segList->SetBaseValueString(aValue);
+          if (NS_FAILED(rv)) {
+            ReportAttributeParseFailure(GetOwnerDoc(), aAttribute, aValue);
+            // The spec says we parse everything up to the failure, so we don't
+            // call segList->ClearBaseValue()
+          }
+          foundMatch = PR_TRUE;
+        }
+      }
+    }
+
+    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)) {
@@ -564,16 +584,28 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespa
           DidChangeLengthList(i, PR_FALSE);
           foundMatch = PR_TRUE;
           break;
         }
       }
     }
 
     if (!foundMatch) {
+      // Check if this is a path segment list attribute going away
+      if (GetPathDataAttrName() == aName) {
+        SVGAnimatedPathSegList *segList = GetAnimPathSegList();
+        if (segList) {
+          segList->ClearBaseValue();
+          DidChangePathSegList(PR_FALSE);
+          foundMatch = PR_TRUE;
+        }
+      }
+    }
+
+    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
@@ -1541,16 +1573,46 @@ nsSVGElement::GetAnimatedLengthList(PRUi
   LengthListAttributesInfo info = GetLengthListInfo();
   if (aAttrEnum < info.mLengthListCount) {
     return &(info.mLengthLists[aAttrEnum]);
   }
   NS_NOTREACHED("Bad attrEnum");
   return nsnull;
 }
 
+
+void
+nsSVGElement::DidChangePathSegList(PRBool aDoSetAttr)
+{
+  NS_ABORT_IF_FALSE(GetPathDataAttrName(), "Changing non-existant path data?");
+
+  if (!aDoSetAttr)
+    return;
+
+  nsAutoString newStr;
+  GetAnimPathSegList()->GetBaseValue().GetValueAsString(newStr);
+
+  SetAttr(kNameSpaceID_None, GetPathDataAttrName(), newStr, PR_TRUE);
+}
+
+void
+nsSVGElement::DidAnimatePathSegList()
+{
+  NS_ABORT_IF_FALSE(GetPathDataAttrName(),
+                    "Animatinging non-existant path data?");
+
+  nsIFrame* frame = GetPrimaryFrame();
+
+  if (frame) {
+    frame->AttributeChanged(kNameSpaceID_None,
+                            GetPathDataAttrName(),
+                            nsIDOMMutationEvent::MODIFICATION);
+  }
+}
+
 nsSVGElement::NumberAttributesInfo
 nsSVGElement::GetNumberInfo()
 {
   return NumberAttributesInfo(nsnull, nsnull, 0);
 }
 
 void nsSVGElement::NumberAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
@@ -2210,16 +2272,26 @@ nsSVGElement::GetAnimatedAttr(PRInt32 aN
     for (PRUint32 i = 0; i < info.mStringCount; i++) {
       if (aNamespaceID == info.mStringInfo[i].mNamespaceID &&
           aName == *info.mStringInfo[i].mName) {
         return info.mStrings[i].ToSMILAttr(this);
       }
     }
   }
 
+  // PathSegLists:
+  {
+    if (GetPathDataAttrName() == aName) {
+      SVGAnimatedPathSegList *segList = GetAnimPathSegList();
+      if (segList) {
+        return segList->ToSMILAttr(this);
+      }
+    }
+  }
+
   // Mapped attributes:
   if (IsAttributeMapped(aName)) {
     nsCSSProperty prop =
       nsCSSProps::LookupProperty(nsDependentAtomString(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)) {
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -69,29 +69,34 @@ class nsSVGEnum;
 struct nsSVGEnumMapping;
 class nsSVGViewBox;
 class nsSVGPreserveAspectRatio;
 class nsSVGString;
 struct gfxMatrix;
 namespace mozilla {
 class SVGAnimatedLengthList;
 class SVGUserUnitList;
+class SVGAnimatedPathSegList;
 }
 
 typedef nsStyledElement nsSVGElementBase;
 
 class nsSVGElement : public nsSVGElementBase,    // nsIContent
                      public nsISVGValueObserver  // :nsISupportsWeakReference
 {
 protected:
   nsSVGElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   nsresult Init();
   virtual ~nsSVGElement();
 
 public:
+  typedef mozilla::SVGUserUnitList SVGUserUnitList;
+  typedef mozilla::SVGAnimatedLengthList SVGAnimatedLengthList;
+  typedef mozilla::SVGAnimatedPathSegList SVGAnimatedPathSegList;
+
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIContent interface methods
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
@@ -162,47 +167,63 @@ public:
   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 DidChangePathSegList(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 DidAnimatePathSegList();
   virtual void DidAnimateTransform();
   virtual void DidAnimateString(PRUint8 aAttrEnum);
 
   void GetAnimatedLengthValues(float *aFirst, ...);
   void GetAnimatedNumberValues(float *aFirst, ...);
   void GetAnimatedIntegerValues(PRInt32 *aFirst, ...);
-  void GetAnimatedLengthListValues(mozilla::SVGUserUnitList *aFirst, ...);
-  mozilla::SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum);
+  void GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...);
+  SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum);
+  virtual SVGAnimatedPathSegList* GetAnimPathSegList() {
+    // DOM interface 'SVGAnimatedPathData' (*inherited* by nsSVGPathElement)
+    // has a member called 'animatedPathSegList' member, so we have a shorter
+    // name so we don't get hidden by the GetAnimatedPathSegList declared by
+    // NS_DECL_NSIDOMSVGANIMATEDPATHDATA.
+    return nsnull;
+  }
 
 #ifdef MOZ_SMIL
   virtual nsISMILAttr* GetAnimatedAttr(PRInt32 aNamespaceID, nsIAtom* aName);
   void AnimationNeedsResample();
   void FlushAnimations();
+#else
+  void AnimationNeedsResample() { /* do nothing */ }
+  void FlushAnimations() { /* do nothing */ }
 #endif
 
   virtual void RecompileScriptEventListeners();
 
   void GetStringBaseValue(PRUint8 aAttrEnum, nsAString& aResult) const;
   void SetStringBaseValue(PRUint8 aAttrEnum, const nsAString& aValue);
 
+  virtual nsIAtom* GetPathDataAttrName() const {
+    return nsnull;
+  }
+
 protected:
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                                 const nsAString* aValue, PRBool aNotify);
   virtual PRBool ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute,
                                 const nsAString& aValue, nsAttrValue& aResult);
   static nsresult ReportAttributeParseFailure(nsIDocument* aDocument,
                                               nsIAtom* aAttribute,
                                               const nsAString& aValue);
@@ -352,21 +373,21 @@ protected:
      * 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 {
-    mozilla::SVGAnimatedLengthList* mLengthLists;
+    SVGAnimatedLengthList* mLengthLists;
     LengthListInfo*        mLengthListInfo;
     PRUint32               mLengthListCount;
 
-    LengthListAttributesInfo(mozilla::SVGAnimatedLengthList *aLengthLists,
+    LengthListAttributesInfo(SVGAnimatedLengthList *aLengthLists,
                              LengthListInfo *aLengthListInfo,
                              PRUint32 aLengthListCount)
       : mLengthLists(aLengthLists)
       , mLengthListInfo(aLengthListInfo)
       , mLengthListCount(aLengthListCount)
     {}
 
     void Reset(PRUint8 aAttrEnum);
--- a/content/svg/content/src/nsSVGPathDataParser.cpp
+++ b/content/svg/content/src/nsSVGPathDataParser.cpp
@@ -33,23 +33,24 @@
  * 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 "nsSVGPathDataParser.h"
 #include "nsSVGDataParser.h"
-#include "nsSVGPathSeg.h"
 #include "nsSVGPathElement.h"
 #include "prdtoa.h"
 #include "nsSVGUtils.h"
 #include <stdlib.h>
 #include <math.h>
 
+using namespace mozilla;
+
 nsresult nsSVGPathDataParser::Match()
 {
   return MatchSvgPath();
 }
 
 //----------------------------------------------------------------------
 
 nsresult nsSVGPathDataParser::MatchCoordPair(float* aX, float* aY)
@@ -861,585 +862,259 @@ PRBool nsSVGPathDataParser::IsTokenEllip
 {
   return IsTokenNonNegativeNumberStarter();
 }
 
 
 //-----------------------------------------------------------------------
 
 
-// ---------------------------------------------------------------
-// nsSVGPathDataParserToInternal
 
-nsresult
-nsSVGPathDataParserToInternal::Parse(const nsAString &aValue)
-{
-  mPathData->Clear();
-  mPx = mPy = mCx = mCy = mStartX = mStartY = 0;
-  mNumCommands = mNumArguments = 0;
-  mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
-
-  nsresult rv = nsSVGPathDataParser::Parse(aValue);
-
-  PathFini();
-
-  return rv;
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreMoveTo(PRBool absCoords, float x, float y)
-{
-  if (absCoords) {
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS;
-  } else {
-    x += mPx;
-    y += mPy;
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL;
-  }
-  return PathMoveTo(x, y);
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreClosePath()
-{
-  mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH;
-
-  return PathClose();
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreLineTo(PRBool absCoords, float x, float y)
-{
-  if (absCoords) {
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS;
-  } else {
-    x += mPx;
-    y += mPy;
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_REL;
-  }
-  return PathLineTo(x, y);
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreHLineTo(PRBool absCoords, float x)
-{
-  if (absCoords) {
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS;
-  } else {
-    x += mPx;
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL;
-  }
-  return PathLineTo(x, mPy);
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreVLineTo(PRBool absCoords, float y)
-{
-  if (absCoords) {
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS;
-  } else {
-    y += mPy;
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL;
-  }
-  return PathLineTo(mPx, y);
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreCurveTo(PRBool absCoords,
-                                            float x, float y,
-                                            float x1, float y1,
-                                            float x2, float y2)
-{
-  if (absCoords) {
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS;
-  } else {
-    x += mPx;  x1 += mPx;  x2 += mPx;
-    y += mPy;  y1 += mPy;  y2 += mPy;
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL;
-  }
-  mCx = x2;
-  mCy = y2;
-  return PathCurveTo(x1, y1, x2, y2, x, y);
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreSmoothCurveTo(PRBool absCoords,
-                                                  float x, float y,
-                                                  float x2, float y2)
-{
-  float x1, y1;
-
-  // first controlpoint = reflection last one about current point
-  if (mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL        ||
-      mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS        ||
-      mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
-      mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
-    x1 = 2 * mPx - mCx;
-    y1 = 2 * mPy - mCy;
-  } else {
-    x1 = mPx;
-    y1 = mPy;
-  }
-  if (absCoords) {
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
-  } else {
-    x += mPx;
-    x2 += mPx;
-    y += mPy;
-    y2 += mPy;
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL;
-  }
-  mCx = x2;
-  mCy = y2;
-  return PathCurveTo(x1, y1, x2, y2, x, y);
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreQuadCurveTo(PRBool absCoords,
-                                                float x, float y,
-                                                float x1, float y1)
-{
-  if (absCoords) {
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS;
-  } else {
-    x += mPx;
-    x1 += mPx;
-    y += mPy;
-    y1 += mPy;
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL;
-  }
-
-  float x31, y31, x32, y32;
-  // conversion of quadratic bezier curve to cubic bezier curve:
-  x31 = mPx + (x1 - mPx) * 2 / 3;
-  y31 = mPy + (y1 - mPy) * 2 / 3;
-  x32 = x1 + (x - x1) / 3;
-  y32 = y1 + (y - y1) / 3;
-
-  mCx = x1;
-  mCy = y1;
-  return PathCurveTo(x31, y31, x32, y32, x, y);
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreSmoothQuadCurveTo(PRBool absCoords,
-                                                      float x, float y)
-{
-  float x1, y1;
-
-  // first controlpoint = reflection last one about current point
-  if (mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL        ||
-      mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS        ||
-      mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
-      mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
-    x1 = 2 * mPx - mCx;
-    y1 = 2 * mPy - mCy;
-  } else {
-    x1 = mPx;
-    y1 = mPy;
-  }
-  if (absCoords) {
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
-  } else {
-    x += mPx;
-    y += mPy;
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
-  }
-
-  float x31, y31, x32, y32;
-  // conversion of quadratic bezier curve to cubic bezier curve:
-  x31 = mPx + (x1 - mPx) * 2 / 3;
-  y31 = mPy + (y1 - mPy) * 2 / 3;
-  x32 = x1 + (x - x1) / 3;
-  y32 = y1 + (y - y1) / 3;
-
-  mCx = x1;
-  mCy = y1;
-  return PathCurveTo(x31, y31, x32, y32, x, y);
-}
 
 static double
 CalcVectorAngle(double ux, double uy, double vx, double vy)
 {
   double ta = atan2(uy, ux);
   double tb = atan2(vy, vx);
   if (tb >= ta)
     return tb-ta;
   return 2 * M_PI - (ta-tb);
 }
 
-nsresult
-nsSVGPathDataParserToInternal::ConvertArcToCurves(float x2, float y2,
-                                                  float rx, float ry,
-                                                  float angle,
-                                                  PRBool largeArcFlag,
-                                                  PRBool sweepFlag)
-{
-  float x1=mPx, y1=mPy, x3, y3;
-  // Treat out-of-range parameters as described in
-  // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
-  
-  // If the endpoints (x1, y1) and (x2, y2) are identical, then this
-  // is equivalent to omitting the elliptical arc segment entirely
-  if (x1 == x2 && y1 == y2) {
-    return NS_OK;
-  }
-  // If rX = 0 or rY = 0 then this arc is treated as a straight line
-  // segment (a "lineto") joining the endpoints.
-  if (rx == 0.0f || ry == 0.0f) {
-    return PathLineTo(x2, y2);
-  }
-  nsSVGArcConverter converter(x1, y1, x2, y2, rx, ry, angle,
-                              largeArcFlag, sweepFlag);
-  
-  while (converter.GetNextSegment(&x1, &y1, &x2, &y2, &x3, &y3)) {
-    // c) draw the cubic bezier:
-    nsresult rv = PathCurveTo(x1, y1, x2, y2, x3, y3);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
 
-  return NS_OK;
-}
-
-nsresult
-nsSVGPathDataParserToInternal::StoreEllipticalArc(PRBool absCoords,
-                                                  float x, float y,
-                                                  float r1, float r2,
-                                                  float angle,
-                                                  PRBool largeArcFlag,
-                                                  PRBool sweepFlag)
-{
-  if (absCoords) {
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_ARC_ABS;
-  } else {
-    x += mPx;
-    y += mPy;
-    mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_ARC_REL;
-  }
-  return ConvertArcToCurves(x, y, r1, r2, angle, largeArcFlag, sweepFlag);
-}
-
-nsresult
-nsSVGPathDataParserToInternal::PathEnsureSpace(PRUint32 aNumArgs)
-{
-  if (!(mNumCommands % 4) &&
-      !mCommands.AppendElement())
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  if (!mArguments.SetLength(mArguments.Length()+aNumArgs))
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  return NS_OK;
-}
-
-void
-nsSVGPathDataParserToInternal::PathAddCommandCode(PRUint8 aCommand)
-{
-  PRUint32 offset = mNumCommands / 4;
-  PRUint32 shift = 2 * (mNumCommands % 4);
-  if (shift == 0) {
-    // make sure we set the byte, to avoid false UMR reports
-    mCommands[offset] = aCommand;
-  } else {
-    mCommands[offset] |= aCommand << shift;
-  }
-  mNumCommands++;
-}
-
-nsresult
-nsSVGPathDataParserToInternal::PathMoveTo(float x, float y)
-{
-  nsresult rv = PathEnsureSpace(2);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PathAddCommandCode(nsSVGPathList::MOVETO);
-  mArguments[mNumArguments++] = x;
-  mArguments[mNumArguments++] = y;
-
-  mPx = mStartX = x;
-  mPy = mStartY = y;
-
-  return NS_OK;
-}
-
-nsresult
-nsSVGPathDataParserToInternal::PathLineTo(float x, float y)
-{
-  nsresult rv = PathEnsureSpace(2);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PathAddCommandCode(nsSVGPathList::LINETO);
-  mArguments[mNumArguments++] = x;
-  mArguments[mNumArguments++] = y;
-
-  mPx = x;
-  mPy = y;
-
-  return NS_OK;
-}
-
-nsresult
-nsSVGPathDataParserToInternal::PathCurveTo(float x1, float y1,
-                                           float x2, float y2,
-                                           float x3, float y3)
-{
-  nsresult rv = PathEnsureSpace(6);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PathAddCommandCode(nsSVGPathList::CURVETO);
-  mArguments[mNumArguments++] = x1;
-  mArguments[mNumArguments++] = y1;
-  mArguments[mNumArguments++] = x2;
-  mArguments[mNumArguments++] = y2;
-  mArguments[mNumArguments++] = x3;
-  mArguments[mNumArguments++] = y3;
-
-  mPx = x3;
-  mPy = y3;
-
-  return NS_OK;
-}
-
-nsresult
-nsSVGPathDataParserToInternal::PathClose()
-{
-  nsresult rv = PathEnsureSpace(0);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PathAddCommandCode(nsSVGPathList::CLOSEPATH);
-
-  mPx = mStartX;
-  mPy = mStartY;
-
-  return NS_OK;
-}
-
-void
-nsSVGPathDataParserToInternal::PathFini()
-{
-  // We're done adding data to the arrays - copy to a straight array
-  // in mPathData, which allows us to remove the 8-byte overhead per
-  // nsTArray.  For a bonus savings we allocate a single array instead
-  // of two.
-  PRUint32 argArraySize;
-
-  argArraySize = mArguments.Length() * sizeof(float);
-  mPathData->mArguments = (float *)malloc(argArraySize + mCommands.Length());
-  if (!mPathData->mArguments)
-    return;
-
-  memcpy(mPathData->mArguments, mArguments.Elements(), argArraySize);
-  memcpy(mPathData->mArguments + mNumArguments,
-         mCommands.Elements(),
-         mCommands.Length());
-  mPathData->mNumArguments = mNumArguments;
-  mPathData->mNumCommands = mNumCommands;
-}
-
-// ---------------------------------------------------------------
-// nsSVGPathDataParserToDOM
-
-nsresult
-nsSVGPathDataParserToDOM::AppendSegment(nsIDOMSVGPathSeg* seg)
-{
-  NS_ENSURE_TRUE(seg, NS_ERROR_OUT_OF_MEMORY);
-  mData->AppendObject(seg);
-  return NS_OK;
-}
-
-
-nsresult
-nsSVGPathDataParserToDOM::StoreMoveTo(PRBool absCoords, float x, float y)
-{
-  return AppendSegment(
-    absCoords ? NS_NewSVGPathSegMovetoAbs(x, y)
-              : NS_NewSVGPathSegMovetoRel(x, y));
-}
-
-nsresult
-nsSVGPathDataParserToDOM::StoreClosePath()
-{
-  return AppendSegment(NS_NewSVGPathSegClosePath());
-}
-
-nsresult
-nsSVGPathDataParserToDOM::StoreLineTo(PRBool absCoords, float x, float y)
-{
-  return AppendSegment(
-    absCoords ? NS_NewSVGPathSegLinetoAbs(x, y)
-              : NS_NewSVGPathSegLinetoRel(x, y));
-}
-
-nsresult
-nsSVGPathDataParserToDOM::StoreHLineTo(PRBool absCoords, float x)
-{
-  return AppendSegment(
-    absCoords ? NS_NewSVGPathSegLinetoHorizontalAbs(x)
-              : NS_NewSVGPathSegLinetoHorizontalRel(x));
-}
-
-nsresult
-nsSVGPathDataParserToDOM::StoreVLineTo(PRBool absCoords, float y)
-{
-  return AppendSegment(
-    absCoords ? NS_NewSVGPathSegLinetoVerticalAbs(y)
-              : NS_NewSVGPathSegLinetoVerticalRel(y));
-}
-
-nsresult
-nsSVGPathDataParserToDOM::StoreCurveTo(PRBool absCoords,
-                                       float x, float y,
-                                       float x1, float y1,
-                                       float x2, float y2)
-{
-  return AppendSegment(
-    absCoords ? NS_NewSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2)
-              : NS_NewSVGPathSegCurvetoCubicRel(x, y, x1, y1, x2, y2));
-}
-
-nsresult
-nsSVGPathDataParserToDOM::StoreSmoothCurveTo(PRBool absCoords,
-                                             float x, float y,
-                                             float x2, float y2)
-{
-  return AppendSegment(
-    absCoords ? NS_NewSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2)
-              : NS_NewSVGPathSegCurvetoCubicSmoothRel(x, y, x2, y2));
-}
-
-nsresult
-nsSVGPathDataParserToDOM::StoreQuadCurveTo(PRBool absCoords,
-                                           float x, float y,
-                                           float x1, float y1)
-{
-  return AppendSegment(
-    absCoords ? NS_NewSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1)
-              : NS_NewSVGPathSegCurvetoQuadraticRel(x, y, x1, y1));
-}
-
-nsresult
-nsSVGPathDataParserToDOM::StoreSmoothQuadCurveTo(PRBool absCoords,
-                                                 float x, float y)
-{
-  return AppendSegment(
-    absCoords ? NS_NewSVGPathSegCurvetoQuadraticSmoothAbs(x, y)
-              : NS_NewSVGPathSegCurvetoQuadraticSmoothRel(x, y));
-}
-
-nsresult
-nsSVGPathDataParserToDOM::StoreEllipticalArc(PRBool absCoords,
-                                             float x, float y,
-                                             float r1, float r2,
-                                             float angle,
-                                             PRBool largeArcFlag,
-                                             PRBool sweepFlag)
-{
-  return AppendSegment(
-    absCoords ? NS_NewSVGPathSegArcAbs(x, y, r1, r2, angle, 
-                                       largeArcFlag, sweepFlag)
-              : NS_NewSVGPathSegArcRel(x, y, r1, r2, angle,
-                                       largeArcFlag, sweepFlag));
-}
-
-nsSVGArcConverter::nsSVGArcConverter(float x1, float y1,
-                                     float x2, float y2,
-                                     float rx, float ry,
-                                     float angle,
+nsSVGArcConverter::nsSVGArcConverter(const gfxPoint &from,
+                                     const gfxPoint &to,
+                                     const gfxPoint &radii,
+                                     double angle,
                                      PRBool largeArcFlag,
                                      PRBool sweepFlag)
 {
   const double radPerDeg = M_PI/180.0;
 
-  // If rX or rY have negative signs, these are dropped; the absolute
-  // value is used instead.
-  mRx = fabs(rx);
-  mRy = fabs(ry);
-
   // Convert to center parameterization as shown in
   // http://www.w3.org/TR/SVG/implnote.html
+  mRx = fabs(radii.x);
+  mRy = fabs(radii.y);
+
   mSinPhi = sin(angle*radPerDeg);
   mCosPhi = cos(angle*radPerDeg);
 
-  double x1dash =  mCosPhi * (x1-x2)/2.0 + mSinPhi * (y1-y2)/2.0;
-  double y1dash = -mSinPhi * (x1-x2)/2.0 + mCosPhi * (y1-y2)/2.0;
+  double x1dash =  mCosPhi * (from.x-to.x)/2.0 + mSinPhi * (from.y-to.y)/2.0;
+  double y1dash = -mSinPhi * (from.x-to.x)/2.0 + mCosPhi * (from.y-to.y)/2.0;
 
   double root;
   double numerator = mRx*mRx*mRy*mRy - mRx*mRx*y1dash*y1dash -
                      mRy*mRy*x1dash*x1dash;
 
   if (numerator < 0.0) {
     //  If mRx , mRy and are such that there is no solution (basically,
-    //  the ellipse is not big enough to reach from (x1, y1) to (x2,
-    //  y2)) then the ellipse is scaled up uniformly until there is
+    //  the ellipse is not big enough to reach from 'from' to 'to'
+    //  then the ellipse is scaled up uniformly until there is
     //  exactly one solution (until the ellipse is just big enough).
 
     // -> find factor s, such that numerator' with mRx'=s*mRx and
     //    mRy'=s*mRy becomes 0 :
-    float s = (float)sqrt(1.0 - numerator/(mRx*mRx*mRy*mRy));
+    double s = sqrt(1.0 - numerator/(mRx*mRx*mRy*mRy));
 
     mRx *= s;
     mRy *= s;
     root = 0.0;
 
   }
   else {
     root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) *
       sqrt( numerator/(mRx*mRx*y1dash*y1dash + mRy*mRy*x1dash*x1dash) );
   }
 
   double cxdash = root*mRx*y1dash/mRy;
   double cydash = -root*mRy*x1dash/mRx;
 
-  mCx = mCosPhi * cxdash - mSinPhi * cydash + (x1+x2)/2.0;
-  mCy = mSinPhi * cxdash + mCosPhi * cydash + (y1+y2)/2.0;
+  mC.x = mCosPhi * cxdash - mSinPhi * cydash + (from.x+to.x)/2.0;
+  mC.y = mSinPhi * cxdash + mCosPhi * cydash + (from.y+to.y)/2.0;
   mTheta = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/mRx, (y1dash-cydash)/mRy);
   double dtheta = CalcVectorAngle((x1dash-cxdash)/mRx, (y1dash-cydash)/mRy,
                                   (-x1dash-cxdash)/mRx, (-y1dash-cydash)/mRy);
   if (!sweepFlag && dtheta>0)
     dtheta -= 2.0*M_PI;
   else if (sweepFlag && dtheta<0)
     dtheta += 2.0*M_PI;
 
   // Convert into cubic bezier segments <= 90deg
   mNumSegs = static_cast<int>(ceil(fabs(dtheta/(M_PI/2.0))));
   mDelta = dtheta/mNumSegs;
   mT = 8.0/3.0 * sin(mDelta/4.0) * sin(mDelta/4.0) / sin(mDelta/2.0);
 
-  mX1 = x1;
-  mY1 = y1;
+  mFrom = from;
   mSegIndex = 0;
 }
 
 PRBool
-nsSVGArcConverter::GetNextSegment(float *x1, float *y1,
-                                  float *x2, float *y2,
-                                  float *x3, float *y3)
+nsSVGArcConverter::GetNextSegment(gfxPoint *cp1, gfxPoint *cp2, gfxPoint *to)
 {
   if (mSegIndex == mNumSegs) {
-     return PR_FALSE;
+    return PR_FALSE;
   }
   
-  float cosTheta1 = cos(mTheta);
-  float sinTheta1 = sin(mTheta);
-  float theta2 = mTheta + mDelta;
-  float cosTheta2 = cos(theta2);
-  float sinTheta2 = sin(theta2);
+  double cosTheta1 = cos(mTheta);
+  double sinTheta1 = sin(mTheta);
+  double theta2 = mTheta + mDelta;
+  double cosTheta2 = cos(theta2);
+  double sinTheta2 = sin(theta2);
 
   // a) calculate endpoint of the segment:
-  *x3 = mCosPhi * mRx*cosTheta2 - mSinPhi * mRy*sinTheta2 + mCx;
-  *y3 = mSinPhi * mRx*cosTheta2 + mCosPhi * mRy*sinTheta2 + mCy;
+  to->x = mCosPhi * mRx*cosTheta2 - mSinPhi * mRy*sinTheta2 + mC.x;
+  to->y = mSinPhi * mRx*cosTheta2 + mCosPhi * mRy*sinTheta2 + mC.y;
 
   // b) calculate gradients at start/end points of segment:
-  *x1 = mX1 + mT * ( - mCosPhi * mRx*sinTheta1 - mSinPhi * mRy*cosTheta1);
-  *y1 = mY1 + mT * ( - mSinPhi * mRx*sinTheta1 + mCosPhi * mRy*cosTheta1);
+  cp1->x = mFrom.x + mT * ( - mCosPhi * mRx*sinTheta1 - mSinPhi * mRy*cosTheta1);
+  cp1->y = mFrom.y + mT * ( - mSinPhi * mRx*sinTheta1 + mCosPhi * mRy*cosTheta1);
 
-  *x2 = *x3 + mT * ( mCosPhi * mRx*sinTheta2 + mSinPhi * mRy*cosTheta2);
-  *y2 = *y3 + mT * ( mSinPhi * mRx*sinTheta2 - mCosPhi * mRy*cosTheta2);
+  cp2->x = to->x + mT * ( mCosPhi * mRx*sinTheta2 + mSinPhi * mRy*cosTheta2);
+  cp2->y = to->y + mT * ( mSinPhi * mRx*sinTheta2 - mCosPhi * mRy*cosTheta2);
 
   // do next segment
   mTheta = theta2;
-  mX1 = *x3;
-  mY1 = *y3;
+  mFrom = *to;
   ++mSegIndex;
 
   return PR_TRUE;
 }
 
+
+// ---------------------------------------------------------------
+// nsSVGPathDataParserToInternal
+
+nsresult
+nsSVGPathDataParserToInternal::Parse(const nsAString &aValue)
+{
+  mPathSegList->Clear();
+  return nsSVGPathDataParser::Parse(aValue);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreMoveTo(PRBool absCoords, float x, float y)
+{
+  // Because our IDL compiler doesn't know any better, each seg type constant
+  // in nsIDOMSVGPathSeg is in a separate enum. This results in "warning:
+  // enumeral mismatch in conditional expression" under GCC if two bare
+  // nsIDOMSVGPathSeg constants are used as operands of the ?: operator below.
+  // In newer versions of GCC we would be able to turn off this warning using:
+  //
+  //#pragma GCC diagnostic push
+  //#pragma GCC diagnostic ignored "-Wenum-compare"
+  //...
+  //#pragma GCC diagnostic pop
+  //
+  // Unfortunately we need to support older versions of GCC. Instead, to
+  // eliminate this warning noise being sent to the console, we wrap the
+  // operands with PRUint32(...).
+
+  PRUint32 type = absCoords ?
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS) :
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL);
+
+  return mPathSegList->AppendSeg(type, x, y);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreClosePath()
+{
+  return mPathSegList->AppendSeg(nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreLineTo(PRBool absCoords, float x, float y)
+{
+  PRUint32 type = absCoords ?
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS) :
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_REL);
+
+  return mPathSegList->AppendSeg(type, x, y);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreHLineTo(PRBool absCoords, float x)
+{
+  PRUint32 type = absCoords ?
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS) :
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL);
+
+  return mPathSegList->AppendSeg(type, x);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreVLineTo(PRBool absCoords, float y)
+{
+  PRUint32 type = absCoords ?
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS) :
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL);
+
+  return mPathSegList->AppendSeg(type, y);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreCurveTo(PRBool absCoords,
+                                            float x, float y,
+                                            float x1, float y1,
+                                            float x2, float y2)
+{
+  PRUint32 type = absCoords ?
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS) :
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL);
+
+  return mPathSegList->AppendSeg(type, x1, y1, x2, y2, x, y);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreSmoothCurveTo(PRBool absCoords,
+                                                  float x, float y,
+                                                  float x2, float y2)
+{
+  PRUint32 type = absCoords ?
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) :
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL);
+
+  return mPathSegList->AppendSeg(type, x2, y2, x, y);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreQuadCurveTo(PRBool absCoords,
+                                                float x, float y,
+                                                float x1, float y1)
+{
+  PRUint32 type = absCoords ?
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS) :
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL);
+
+  return mPathSegList->AppendSeg(type, x1, y1, x, y);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreSmoothQuadCurveTo(PRBool absCoords,
+                                                      float x, float y)
+{
+  PRUint32 type = absCoords ?
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) :
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL);
+
+  return mPathSegList->AppendSeg(type, x, y);
+}
+
+nsresult
+nsSVGPathDataParserToInternal::StoreEllipticalArc(PRBool absCoords,
+                                                  float x, float y,
+                                                  float r1, float r2,
+                                                  float angle,
+                                                  PRBool largeArcFlag,
+                                                  PRBool sweepFlag)
+{
+  PRUint32 type = absCoords ?
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_ARC_ABS) :
+    PRUint32(nsIDOMSVGPathSeg::PATHSEG_ARC_REL);
+
+  // We can only pass floats after 'type', and per the SVG spec for arc,
+  // non-zero args are treated at 'true'.
+  return mPathSegList->AppendSeg(type, r1, r2, angle,
+                                 largeArcFlag ? 1.0f : 0.0f,
+                                 sweepFlag ? 1.0f : 0.0f,
+                                 x, y);
+}
+
--- a/content/svg/content/src/nsSVGPathDataParser.h
+++ b/content/svg/content/src/nsSVGPathDataParser.h
@@ -39,19 +39,24 @@
 #ifndef __NS_SVGPATHDATAPARSER_H__
 #define __NS_SVGPATHDATAPARSER_H__
 
 #include "nsSVGDataParser.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsIDOMSVGPathSeg.h"
 #include "nsTArray.h"
+#include "gfxPoint.h"
 
 class nsSVGPathList;
 
+namespace mozilla {
+class SVGPathData;
+}
+
 ////////////////////////////////////////////////////////////////////////
 // nsSVGPathDataParser: a simple recursive descent parser that builds
 // nsIDOMSVGPathSegs from path data strings. The grammar for path data
 // can be found in SVG CR 20001102, chapter 8.
 
 class nsSVGPathDataParser : public nsSVGDataParser
 {
 protected:
@@ -135,20 +140,40 @@ protected:
   nsresult MatchEllipticalArcArgSeq(PRBool absCoords);
   nsresult MatchEllipticalArcArg(float* x, float* y,
                                  float* r1, float* r2, float* angle,
                                  PRBool* largeArcFlag, PRBool* sweepFlag);
   PRBool IsTokenEllipticalArcArgStarter();
   
  };
 
+class nsSVGArcConverter
+{
+public:
+  nsSVGArcConverter(const gfxPoint &from,
+                    const gfxPoint &to,
+                    const gfxPoint &radii,
+                    double angle,
+                    PRBool largeArcFlag,
+                    PRBool sweepFlag);
+  PRBool GetNextSegment(gfxPoint *cp1, gfxPoint *cp2, gfxPoint *to);
+protected:
+  PRInt32 mNumSegs, mSegIndex;
+  double mTheta, mDelta, mT;
+  double mSinPhi, mCosPhi;
+  double mRx, mRy;
+  gfxPoint mFrom, mC;
+};
+
 class nsSVGPathDataParserToInternal : public nsSVGPathDataParser
 {
 public:
-  nsSVGPathDataParserToInternal(nsSVGPathList *data) : mPathData(data) {}
+  nsSVGPathDataParserToInternal(mozilla::SVGPathData *aList)
+    : mPathSegList(aList)
+  {}
   nsresult Parse(const nsAString &aValue);
 
 protected:
   virtual nsresult StoreMoveTo(PRBool absCoords, float x, float y);
   virtual nsresult StoreClosePath();
   virtual nsresult StoreLineTo(PRBool absCoords, float x, float y);
   virtual nsresult StoreHLineTo(PRBool absCoords, float x);
   virtual nsresult StoreVLineTo(PRBool absCoords, float y);
@@ -160,83 +185,12 @@ protected:
                                     float x1, float y1);
   virtual nsresult StoreSmoothQuadCurveTo(PRBool absCoords,
                                           float x, float y);
   virtual nsresult StoreEllipticalArc(PRBool absCoords, float x, float y,
                                       float r1, float r2, float angle,
                                       PRBool largeArcFlag, PRBool sweepFlag);
 
 private:
-  nsSVGPathList *mPathData;
-  PRUint16 mPrevSeg;       // previous segment type for "smooth" segments"
-  float mPx, mPy;          // current point
-  float mCx, mCy;          // last control point for "smooth" segments
-  float mStartX, mStartY;  // start of current subpath, for closepath
-
-  // information used to construct PathList 
-  nsTArray<PRUint8> mCommands;
-  nsTArray<float>   mArguments;
-  PRUint32          mNumArguments;
-  PRUint32          mNumCommands;
-
-  // Pathdata helpers
-  nsresult ConvertArcToCurves(float x2, float y2, float rx, float ry,
-                              float angle, PRBool largeArcFlag, PRBool sweepFlag);
-
-  nsresult PathEnsureSpace(PRUint32 aNumArgs);
-  void PathAddCommandCode(PRUint8 aCommand);
-  nsresult PathMoveTo(float x, float y);
-  nsresult PathLineTo(float x, float y);
-  nsresult PathCurveTo(float x1, float y1, float x2, float y2, float x3, float y3);
-  nsresult PathClose();
-  void PathFini();
-};
-
-class nsSVGPathDataParserToDOM : public nsSVGPathDataParser
-{
-public:
-  nsSVGPathDataParserToDOM(nsCOMArray<nsIDOMSVGPathSeg>* data) : mData(data) {}
-
-protected:
-  virtual nsresult StoreMoveTo(PRBool absCoords, float x, float y);
-  virtual nsresult StoreClosePath();
-  virtual nsresult StoreLineTo(PRBool absCoords, float x, float y);
-  virtual nsresult StoreHLineTo(PRBool absCoords, float x);
-  virtual nsresult StoreVLineTo(PRBool absCoords, float y);
-  virtual nsresult StoreCurveTo(PRBool absCoords, float x, float y,
-                                float x1, float y1, float x2, float y2);
-  virtual nsresult StoreSmoothCurveTo(PRBool absCoords, float x, float y,
-                                      float x2, float y2);
-  virtual nsresult StoreQuadCurveTo(PRBool absCoords, float x, float y,
-                                    float x1, float y1);
-  virtual nsresult StoreSmoothQuadCurveTo(PRBool absCoords,
-                                          float x, float y);
-  virtual nsresult StoreEllipticalArc(PRBool absCoords, float x, float y,
-                                      float r1, float r2, float angle,
-                                      PRBool largeArcFlag, PRBool sweepFlag);
-
-private:
-  nsresult AppendSegment(nsIDOMSVGPathSeg* seg);
-
-  nsCOMArray<nsIDOMSVGPathSeg>* mData;
-};
-
-class nsSVGArcConverter
-{
-public:
-  nsSVGArcConverter(float x1, float y1,
-                    float x2, float y2,
-                    float rx, float ry,
-                    float angle,
-                    PRBool largeArcFlag,
-                    PRBool sweepFlag);
-  PRBool GetNextSegment(float *x1, float *y1,
-                        float *x2, float *y2,
-                        float *x3, float *y3);
-protected:
-  PRInt32 mNumSegs, mSegIndex;
-  float mTheta, mDelta, mT;
-  float mSinPhi, mCosPhi;
-  float mX1, mY1, mRx, mRy, mCx, mCy;
-
+  mozilla::SVGPathData *mPathSegList;
 };
 
 #endif // __NS_SVGPATHDATAPARSER_H__
--- a/content/svg/content/src/nsSVGPathElement.cpp
+++ b/content/svg/content/src/nsSVGPathElement.cpp
@@ -32,29 +32,31 @@
  * 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 "nsSVGPathSegList.h"
 #include "nsIDOMSVGPathSeg.h"
-#include "nsSVGPathSeg.h"
+#include "DOMSVGPathSeg.h"
+#include "DOMSVGPathSegList.h"
 #include "nsCOMPtr.h"
 #include "nsIFrame.h"
 #include "nsSVGPathDataParser.h"
 #include "nsSVGPathElement.h"
 #include "nsISVGValueUtils.h"
 #include "nsSVGUtils.h"
 #include "nsSVGPoint.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 
+using namespace mozilla;
+
 nsSVGElement::NumberInfo nsSVGPathElement::sNumberInfo = 
                                                   { &nsGkAtoms::pathLength, 0 };
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Path)
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
@@ -73,22 +75,16 @@ NS_INTERFACE_MAP_END_INHERITING(nsSVGPat
 //----------------------------------------------------------------------
 // Implementation
 
 nsSVGPathElement::nsSVGPathElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsSVGPathElementBase(aNodeInfo)
 {
 }
 
-nsSVGPathElement::~nsSVGPathElement()
-{
-  if (mSegments)
-    NS_REMOVE_SVGVALUE_OBSERVER(mSegments);
-}
-
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGPathElement)
 
 //----------------------------------------------------------------------
 // nsIDOMSVGPathElement methods:
 
@@ -136,48 +132,17 @@ nsSVGPathElement::GetPointAtLength(float
   return NS_NewSVGPoint(_retval, flat->FindPoint(gfxPoint(distance, 0)));
 }
 
 /* unsigned long getPathSegAtLength (in float distance); */
 NS_IMETHODIMP
 nsSVGPathElement::GetPathSegAtLength(float distance, PRUint32 *_retval)
 {
   NS_ENSURE_FINITE(distance, NS_ERROR_ILLEGAL_VALUE);
-
-  //Check if mSegments is null
-  nsresult rv = CreatePathSegList();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 i = 0, numSegments;
-  float distCovered = 0;
-  nsSVGPathSegTraversalState ts;
-
-  mSegments->GetNumberOfItems(&numSegments);
-
-  //  There is no need to check to see if distance falls within the last segment
-  //  because if distance is longer than the total length of the path we return 
-  //  the index of the final segment anyway.
-  while (distCovered < distance && i + 1 < numSegments) {
-    nsCOMPtr<nsIDOMSVGPathSeg> segment;
-    mSegments->GetItem(i, getter_AddRefs(segment));
-    nsSVGPathSeg* curSeg = static_cast<nsSVGPathSeg*>(segment.get());
-    if (i == 0) {
-      curSeg->GetLength(&ts);
-    } else {
-      distCovered += curSeg->GetLength(&ts);
-    }
-
-    if (distCovered >= distance) {
-      break;
-    }
-    ++i;
-  }
-
-  *_retval = i;
-
+  *_retval = mD.GetAnimValue().GetPathSegAtLength(distance);
   return NS_OK;
 }
 
 /* nsIDOMSVGPathSegClosePath createSVGPathSegClosePath (); */
 NS_IMETHODIMP
 nsSVGPathElement::CreateSVGPathSegClosePath(nsIDOMSVGPathSegClosePath **_retval)
 {
   nsIDOMSVGPathSeg* seg = NS_NewSVGPathSegClosePath();
@@ -362,74 +327,49 @@ NS_IMETHODIMP
 nsSVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothRel(float x, float y, nsIDOMSVGPathSegCurvetoQuadraticSmoothRel **_retval)
 {
   NS_ENSURE_FINITE2(x, y, NS_ERROR_ILLEGAL_VALUE);
   nsIDOMSVGPathSeg* seg = NS_NewSVGPathSegCurvetoQuadraticSmoothRel(x, y);
   NS_ENSURE_TRUE(seg, NS_ERROR_OUT_OF_MEMORY);
   return CallQueryInterface(seg, _retval);
 }
 
-nsresult
-nsSVGPathElement::CreatePathSegList()
-{
-  if (mSegments)
-    return NS_OK;
-
-  nsresult rv = NS_NewSVGPathSegList(getter_AddRefs(mSegments));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsISVGValue> value = do_QueryInterface(mSegments);
-
-  nsAutoString d;
-  if (NS_SUCCEEDED(GetAttr(kNameSpaceID_None, nsGkAtoms::d, d)))
-    value->SetValueString(d);
-
-  NS_ADD_SVGVALUE_OBSERVER(mSegments);
-
-  return NS_OK;
-}
-
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsSVGElement::NumberAttributesInfo
 nsSVGPathElement::GetNumberInfo()
 {
   return NumberAttributesInfo(&mPathLength, &sNumberInfo, 1);
 }
 
 //----------------------------------------------------------------------
 // nsIDOMSVGAnimatedPathData methods:
 
 /* readonly attribute nsIDOMSVGPathSegList pathSegList; */
 NS_IMETHODIMP nsSVGPathElement::GetPathSegList(nsIDOMSVGPathSegList * *aPathSegList)
 {
-  nsresult rv = CreatePathSegList();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *aPathSegList = mSegments;
-  NS_ADDREF(*aPathSegList);
-  return NS_OK;
+  void *key = mD.GetBaseValKey();
+  *aPathSegList = DOMSVGPathSegList::GetDOMWrapper(key, this, PR_FALSE).get();
+  return *aPathSegList ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 /* readonly attribute nsIDOMSVGPathSegList normalizedPathSegList; */
 NS_IMETHODIMP nsSVGPathElement::GetNormalizedPathSegList(nsIDOMSVGPathSegList * *aNormalizedPathSegList)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* readonly attribute nsIDOMSVGPathSegList animatedPathSegList; */
 NS_IMETHODIMP nsSVGPathElement::GetAnimatedPathSegList(nsIDOMSVGPathSegList * *aAnimatedPathSegList)
 {
-  nsresult rv = CreatePathSegList();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *aAnimatedPathSegList = mSegments;
-  NS_ADDREF(*aAnimatedPathSegList);
-  return NS_OK;
+  void *key = mD.GetAnimValKey();
+  *aAnimatedPathSegList =
+    DOMSVGPathSegList::GetDOMWrapper(key, this, PR_TRUE).get();
+  return *aAnimatedPathSegList ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 /* readonly attribute nsIDOMSVGPathSegList animatedNormalizedPathSegList; */
 NS_IMETHODIMP nsSVGPathElement::GetAnimatedNormalizedPathSegList(nsIDOMSVGPathSegList * *aAnimatedNormalizedPathSegList)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
@@ -442,85 +382,20 @@ nsSVGPathElement::IsAttributeMapped(cons
   static const MappedAttributeEntry* const map[] = {
     sMarkersMap
   };
 
   return FindAttributeDependence(name, map, NS_ARRAY_LENGTH(map)) ||
     nsSVGPathElementBase::IsAttributeMapped(name);
 }
 
-nsresult
-nsSVGPathElement::BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                               const nsAString* aValue, PRBool aNotify)
-{
-  if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::d) {
-    if (mSegments) {
-      NS_REMOVE_SVGVALUE_OBSERVER(mSegments);
-      mSegments = nsnull;
-    }
-
-    if (aValue) {
-      nsSVGPathDataParserToInternal parser(&mPathData);
-      nsresult rv = parser.Parse(*aValue);
-      if (NS_FAILED(rv)) {
-        ReportAttributeParseFailure(GetOwnerDoc(), aName, *aValue);
-      }
-    } else {
-      mPathData.Clear();
-    }
-  }
-
-  return nsSVGPathElementBase::BeforeSetAttr(aNamespaceID, aName,
-                                             aValue, aNotify);
-}
-
-NS_IMETHODIMP
-nsSVGPathElement::WillModifySVGObservable(nsISVGValue* observable,
-                                          nsISVGValue::modificationType aModType)
-{
-  nsCOMPtr<nsIDOMSVGPathSegList> list = do_QueryInterface(observable);
-
-  if (list && mSegments == list) {
-    return NS_OK;
-  }
-
-  return nsSVGPathElementBase::WillModifySVGObservable(observable, aModType);
-}
-
-NS_IMETHODIMP
-nsSVGPathElement::DidModifySVGObservable(nsISVGValue* observable,
-                                         nsISVGValue::modificationType aModType)
-{
-  nsCOMPtr<nsIDOMSVGPathSegList> list = do_QueryInterface(observable);
-
-  if (list && mSegments == list) {
-    nsCOMPtr<nsISVGValue> value = do_QueryInterface(mSegments);
-    nsAutoString d;
-    nsresult rv = value->GetValueString(d);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Want to keep the seglist alive - SetAttr normally invalidates it
-    nsCOMPtr<nsIDOMSVGPathSegList> deathGrip = mSegments;
-    mSegments = nsnull;
-
-    rv = SetAttr(kNameSpaceID_None, nsGkAtoms::d, d, PR_TRUE);
-
-    // Restore seglist
-    mSegments = deathGrip;
-
-    return rv;
-  }
-
-  return nsSVGPathElementBase::DidModifySVGObservable(observable, aModType);
-}
-
 already_AddRefed<gfxFlattenedPath>
 nsSVGPathElement::GetFlattenedPath(const gfxMatrix &aMatrix)
 {
-  return mPathData.GetFlattenedPath(aMatrix);
+  return mD.GetAnimValue().ToFlattenedPath(aMatrix);
 }
 
 //----------------------------------------------------------------------
 // nsSVGPathGeometryElement methods
 
 PRBool
 nsSVGPathElement::AttributeDefinesGeometry(const nsIAtom *aName)
 {
@@ -532,522 +407,20 @@ nsSVGPathElement::AttributeDefinesGeomet
 }
 
 PRBool
 nsSVGPathElement::IsMarkable()
 {
   return PR_TRUE;
 }
 
-static double
-CalcVectorAngle(double ux, double uy, double vx, double vy)
-{
-  double ta = atan2(uy, ux);
-  double tb = atan2(vy, vx);
-  if (tb >= ta)
-    return tb-ta;
-  return 2 * M_PI - (ta-tb);
-}
-
 void
 nsSVGPathElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
 {
-  if (NS_FAILED(CreatePathSegList()))
-    return;
-
-  PRUint32 count;
-  mSegments->GetNumberOfItems(&count);
-  nsCOMPtr<nsIDOMSVGPathSeg> segment;
-
-  float cx = 0.0f; // current point
-  float cy = 0.0f;
-
-  float cx1 = 0.0f; // last controlpoint (for s,S,t,T)
-  float cy1 = 0.0f;
-
-  PRUint16 lastSegmentType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
-
-  float px = 0, py = 0;    // subpath initial point
-  float pathAngle = 0;
-  PRUint32 pathIndex = 0;
-
-  float prevAngle = 0, startAngle = 0, endAngle = 0;
-
-  PRBool newSegment = PR_FALSE;
-
-  PRUint32 i;
-  for (i = 0; i < count; ++i) {
-    nsCOMPtr<nsIDOMSVGPathSeg> segment;
-    mSegments->GetItem(i, getter_AddRefs(segment));
-
-    PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
-    segment->GetPathSegType(&type);
-
-    float x, y;
-    PRBool absCoords = PR_FALSE;
-
-    switch (type) {
-    case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
-    {
-      x = px;
-      y = py;
-      startAngle = endAngle = atan2(y - cy, x - cx);
-    }
-    break;
-
-    case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
-      absCoords = PR_TRUE;
-    case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
-    {
-      if (!absCoords) {
-        nsCOMPtr<nsIDOMSVGPathSegMovetoRel> moveseg = do_QueryInterface(segment);
-        NS_ASSERTION(moveseg, "interface not implemented");
-        moveseg->GetX(&x);
-        moveseg->GetY(&y);
-        x += cx;
-        y += cy;
-      } else {
-        nsCOMPtr<nsIDOMSVGPathSegMovetoAbs> moveseg = do_QueryInterface(segment);
-        NS_ASSERTION(moveseg, "interface not implemented");
-        moveseg->GetX(&x);
-        moveseg->GetY(&y);
-      }
-      px = x;
-      py = y;
-      startAngle = endAngle = prevAngle;
-      newSegment = PR_TRUE;
-    }
-    break;
-
-    case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
-      absCoords = PR_TRUE;
-    case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
-    {
-      if (!absCoords) {
-        nsCOMPtr<nsIDOMSVGPathSegLinetoRel> lineseg = do_QueryInterface(segment);
-        NS_ASSERTION(lineseg, "interface not implemented");
-        lineseg->GetX(&x);
-        lineseg->GetY(&y);
-        x += cx;
-        y += cy;
-      } else {
-        nsCOMPtr<nsIDOMSVGPathSegLinetoAbs> lineseg = do_QueryInterface(segment);
-        NS_ASSERTION(lineseg, "interface not implemented");
-        lineseg->GetX(&x);
-        lineseg->GetY(&y);
-      }
-      startAngle = endAngle = atan2(y - cy, x - cx);
-    }
-    break;
-
-    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
-      absCoords = PR_TRUE;
-    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
-    {
-      float x1, y1, x2, y2;
-      if (!absCoords) {
-        nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicRel> curveseg = do_QueryInterface(segment);
-        NS_ASSERTION(curveseg, "interface not implemented");
-        curveseg->GetX(&x);
-        curveseg->GetY(&y);
-        curveseg->GetX1(&x1);
-        curveseg->GetY1(&y1);
-        curveseg->GetX2(&x2);
-        curveseg->GetY2(&y2);
-        x  += cx;
-        y  += cy;
-        x1 += cx;
-        y1 += cy;
-        x2 += cx;
-        y2 += cy;
-      } else {
-        nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicAbs> curveseg = do_QueryInterface(segment);
-        NS_ASSERTION(curveseg, "interface not implemented");
-        curveseg->GetX(&x);
-        curveseg->GetY(&y);
-        curveseg->GetX1(&x1);
-        curveseg->GetY1(&y1);
-        curveseg->GetX2(&x2);
-        curveseg->GetY2(&y2);
-      }
-
-      cx1 = x2;
-      cy1 = y2;
-
-      if (x1 == cx && y1 == cy) {
-        x1 = x2;
-        y1 = y2;
-      }
-
-      if (x2 == x && y2 == y) {
-        x2 = x1;
-        y2 = y1;
-      }
-
-      startAngle = atan2(y1 - cy, x1 - cx);
-      endAngle = atan2(y - y2, x - x2);
-    }
-    break;
-
-    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
-      absCoords = PR_TRUE;
-    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
-    {
-      float x1, y1;
-
-      if (!absCoords) {
-        nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticRel> curveseg = do_QueryInterface(segment);
-        NS_ASSERTION(curveseg, "interface not implemented");
-        curveseg->GetX(&x);
-        curveseg->GetY(&y);
-        curveseg->GetX1(&x1);
-        curveseg->GetY1(&y1);
-        x  += cx;
-        y  += cy;
-        x1 += cx;
-        y1 += cy;
-      } else {
-        nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticAbs> curveseg = do_QueryInterface(segment);
-        NS_ASSERTION(curveseg, "interface not implemented");
-        curveseg->GetX(&x);
-        curveseg->GetY(&y);
-        curveseg->GetX1(&x1);
-        curveseg->GetY1(&y1);
-      }
-
-      cx1 = x1;
-      cy1 = y1;
-
-      startAngle = atan2(y1 - cy, x1 - cx);
-      endAngle = atan2(y - y1, x - x1);
-    }
-    break;
-
-    case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
-      absCoords = PR_TRUE;
-    case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
-    {
-      float r1, r2, angle;
-      PRBool largeArcFlag, sweepFlag;
-
-      if (!absCoords) {
-        nsCOMPtr<nsIDOMSVGPathSegArcRel> arcseg = do_QueryInterface(segment);
-        NS_ASSERTION(arcseg, "interface not implemented");
-        arcseg->GetX(&x);
-        arcseg->GetY(&y);
-        arcseg->GetR1(&r1);
-        arcseg->GetR2(&r2);
-        arcseg->GetAngle(&angle);
-        arcseg->GetLargeArcFlag(&largeArcFlag);
-        arcseg->GetSweepFlag(&sweepFlag);
-
-        x  += cx;
-        y  += cy;
-      } else {
-        nsCOMPtr<nsIDOMSVGPathSegArcAbs> arcseg = do_QueryInterface(segment);
-        NS_ASSERTION(arcseg, "interface not implemented");
-        arcseg->GetX(&x);
-        arcseg->GetY(&y);
-        arcseg->GetR1(&r1);
-        arcseg->GetR2(&r2);
-        arcseg->GetAngle(&angle);
-        arcseg->GetLargeArcFlag(&largeArcFlag);
-        arcseg->GetSweepFlag(&sweepFlag);
-      }
-
-      /* check for degenerate ellipse */
-      if (r1 == 0.0 || r2 == 0.0) {
-        startAngle = endAngle = atan2(y - cy, x - cx);
-        break;
-      }
-
-      r1 = fabs(r1);  r2 = fabs(r2);
-
-      float xp, yp, cxp, cyp;
-
-      /* slope fun&games ... see SVG spec, section F.6 */
-      angle = angle*M_PI/180.0;
-      xp = cos(angle)*(cx-x)/2.0 + sin(angle)*(cy-y)/2.0;
-      yp = -sin(angle)*(cx-x)/2.0 + cos(angle)*(cy-y)/2.0;
-
-      /* make sure radii are large enough */
-      float root, numerator = r1*r1*r2*r2 - r1*r1*yp*yp - r2*r2*xp*xp;
-      if (numerator < 0.0) {
-        float s = sqrt(1.0 - numerator/(r1*r1*r2*r2));
-        r1 *= s;
-        r2 *= s;
-        root = 0.0;
-      } else {
-        root = sqrt(numerator/(r1*r1*yp*yp + r2*r2*xp*xp));
-        if (largeArcFlag == sweepFlag)
-          root = -root;
-      }
-      cxp = root*r1*yp/r2;
-      cyp = -root*r2*xp/r1;
-
-      float theta, delta;
-      theta = CalcVectorAngle(1.0, 0.0,  (xp-cxp)/r1, (yp-cyp)/r2);
-      delta  = CalcVectorAngle((xp-cxp)/r1, (yp-cyp)/r2,
-                               (-xp-cxp)/r1, (-yp-cyp)/r2);
-      if (!sweepFlag && delta > 0)
-        delta -= 2.0*M_PI;
-      else if (sweepFlag && delta < 0)
-        delta += 2.0*M_PI;
-
-      float tx1, ty1, tx2, ty2;
-      tx1 = -cos(angle)*r1*sin(theta) - sin(angle)*r2*cos(theta);
-      ty1 = -sin(angle)*r1*sin(theta) + cos(angle)*r2*cos(theta);
-      tx2 = -cos(angle)*r1*sin(theta+delta) - sin(angle)*r2*cos(theta+delta);
-      ty2 = -sin(angle)*r1*sin(theta+delta) + cos(angle)*r2*cos(theta+delta);
-
-      if (delta < 0.0f) {
-        tx1 = -tx1;
-        ty1 = -ty1;
-        tx2 = -tx2;
-        ty2 = -ty2;
-      }
-
-      startAngle = atan2(ty1, tx1);
-      endAngle = atan2(ty2, tx2);
-    }
-    break;
-
-    case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
-      absCoords = PR_TRUE;
-    case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
-    {
-      y = cy;
-      if (!absCoords) {
-        nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalRel> lineseg = do_QueryInterface(segment);
-        NS_ASSERTION(lineseg, "interface not implemented");
-        lineseg->GetX(&x);
-        x += cx;
-      } else {
-        nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalAbs> lineseg = do_QueryInterface(segment);
-        NS_ASSERTION(lineseg, "interface not implemented");
-        lineseg->GetX(&x);
-      }
-      startAngle = endAngle = atan2(0, x - cx);
-    }
-    break;
-
-    case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
-      absCoords = PR_TRUE;
-    case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
-    {
-      x = cx;
-      if (!absCoords) {
-        nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalRel> lineseg = do_QueryInterface(segment);
-        NS_ASSERTION(lineseg, "interface not implemented");
-        lineseg->GetY(&y);
-        y += cy;
-      } else {
-        nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalAbs> lineseg = do_QueryInterface(segment);
-        NS_ASSERTION(lineseg, "interface not implemented");
-        lineseg->GetY(&y);
-      }
-      startAngle = endAngle = atan2(y - cy, 0);
-    }
-    break;
-
-    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
-      absCoords = PR_TRUE;
-    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
-    {
-      float x1, y1, x2, y2;
-
-      if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL        ||
-          lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS        ||
-          lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
-          lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
-        // the first controlpoint is the reflection of the last one about the current point:
-        x1 = 2*cx - cx1;
-        y1 = 2*cy - cy1;
-      }
-      else {
-        // the first controlpoint is equal to the current point:
-        x1 = cx;
-        y1 = cy;
-      }
-
-      if (!absCoords) {
-        nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothRel> curveseg = do_QueryInterface(segment);
-        NS_ASSERTION(curveseg, "interface not implemented");
-        curveseg->GetX(&x);
-        curveseg->GetY(&y);
-        curveseg->GetX2(&x2);
-        curveseg->GetY2(&y2);
-        x  += cx;
-        y  += cy;
-        x2 += cx;
-        y2 += cy;
-      } else {
-        nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothAbs> curveseg = do_QueryInterface(segment);
-        NS_ASSERTION(curveseg, "interface not implemented");
-        curveseg->GetX(&x);
-        curveseg->GetY(&y);
-        curveseg->GetX2(&x2);
-        curveseg->GetY2(&y2);
-      }
-
-      cx1 = x2;
-      cy1 = y2;
-
-      if (x1 == cx && y1 == cy) {
-        x1 = x2;
-        y1 = y2;
-      }
-
-      if (x2 == x && y2 == y) {
-        x2 = x1;
-        y2 = y1;
-      }
-
-      startAngle = atan2(y1 - cy, x1 - cx);
-      endAngle = atan2(y - y2, x - x2);
-    }
-    break;
-
-    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
-      absCoords = PR_TRUE;
-    case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
-      {
-        float x1, y1;
-
-        if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL        ||
-            lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS        ||
-            lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
-            lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
-          // the first controlpoint is the reflection of the last one about the current point:
-          x1 = 2*cx - cx1;
-          y1 = 2*cy - cy1;
-        }
-        else {
-          // the first controlpoint is equal to the current point:
-          x1 = cx;
-          y1 = cy;
-        }
-
-        if (!absCoords) {
-          nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothRel> curveseg = do_QueryInterface(segment);
-          NS_ASSERTION(curveseg, "interface not implemented");
-          curveseg->GetX(&x);
-          curveseg->GetY(&y);
-          x  += cx;
-          y  += cy;
-        } else {
-          nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs> curveseg = do_QueryInterface(segment);
-          NS_ASSERTION(curveseg, "interface not implemented");
-          curveseg->GetX(&x);
-          curveseg->GetY(&y);
-        }
-
-        cx1 = x1;
-        cy1 = y1;
-
-        startAngle = atan2(y1 - cy, x1 - cx);
-        endAngle = atan2(y - y1, x - x1);
-      }
-      break;
-
-    default:
-      NS_ASSERTION(1==0, "unknown path segment");
-      break;
-    }
-    lastSegmentType = type;
-
-    if (newSegment &&
-        type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
-        type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL) {
-      pathIndex = aMarks->Length() - 1;
-      pathAngle = startAngle;
-      aMarks->ElementAt(pathIndex).angle = pathAngle;
-      newSegment = PR_FALSE;
-      prevAngle = endAngle;
-    } else if (type == nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS ||
-               type == nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL) {
-      if (aMarks->Length())
-        aMarks->ElementAt(aMarks->Length() - 1).angle = prevAngle;
-    } else {
-      aMarks->ElementAt(aMarks->Length() - 1).angle =
-        nsSVGUtils::AngleBisect(prevAngle, startAngle);
-      prevAngle = endAngle;
-    }
-
-    aMarks->AppendElement(nsSVGMark(x, y, 0));
-
-    if (type == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH) {
-      prevAngle = nsSVGUtils::AngleBisect(endAngle, pathAngle);
-      aMarks->ElementAt(pathIndex).angle = prevAngle;
-    }
-
-    cx = x;
-    cy = y;
-  }
-
-  if (aMarks->Length())
-    aMarks->ElementAt(aMarks->Length() - 1).angle = prevAngle;
+  mD.GetAnimValue().GetMarkerPositioningData(aMarks);
 }
 
 void
 nsSVGPathElement::ConstructPath(gfxContext *aCtx)
 {
-  mPathData.Playback(aCtx);
-}
-
-//==================================================================
-// nsSVGPathList
-
-void
-nsSVGPathList::Clear()
-{
-  if (mArguments) {
-    free(mArguments);
-    mArguments = nsnull;
-  }
-  mNumCommands = 0;
-  mNumArguments = 0;
+  mD.GetAnimValue().ConstructPath(aCtx);
 }
 
-void
-nsSVGPathList::Playback(gfxContext *aCtx)
-{
-  float *args = mArguments;
-  for (PRUint32 i = 0; i < mNumCommands; i++) {
-    PRUint8 command =
-      reinterpret_cast<PRUint8*>(mArguments + mNumArguments)[i / 4];
-    command = (command >> (2 * (i % 4))) & 0x3;
-    switch (command) {
-    case MOVETO:
-      aCtx->MoveTo(gfxPoint(args[0], args[1]));
-      args += 2;
-      break;
-    case LINETO:
-      aCtx->LineTo(gfxPoint(args[0], args[1]));
-      args += 2;
-      break;
-    case CURVETO:
-      aCtx->CurveTo(gfxPoint(args[0], args[1]),
-                    gfxPoint(args[2], args[3]),
-                    gfxPoint(args[4], args[5]));
-      args += 6;
-      break;
-    case CLOSEPATH:
-      aCtx->ClosePath();
-      break;
-    default:
-      break;
-    }
-  }
-}
-
-already_AddRefed<gfxFlattenedPath>
-nsSVGPathList::GetFlattenedPath(const gfxMatrix& aMatrix)
-{
-  nsRefPtr<gfxContext> ctx =
-    new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
-
-  ctx->SetMatrix(aMatrix);
-  Playback(ctx);
-  ctx->IdentityMatrix();
-
-  return ctx->GetFlattenedPath();
-}
--- a/content/svg/content/src/nsSVGPathElement.h
+++ b/content/svg/content/src/nsSVGPathElement.h
@@ -38,95 +38,75 @@
 
 #ifndef __NS_SVGPATHELEMENT_H__
 #define __NS_SVGPATHELEMENT_H__
 
 #include "nsSVGPathGeometryElement.h"
 #include "nsIDOMSVGPathElement.h"
 #include "nsIDOMSVGAnimatedPathData.h"
 #include "nsSVGNumber2.h"
+#include "SVGAnimatedPathSegList.h"
 #include "gfxPath.h"
 
 class gfxContext;
 
-class nsSVGPathList
-{
-friend class nsSVGPathDataParserToInternal;
-
-public:
-  enum { MOVETO, LINETO, CURVETO, CLOSEPATH };
-  nsSVGPathList() : mArguments(nsnull), mNumCommands(0), mNumArguments(0) {}
-  ~nsSVGPathList() { Clear(); }
-  void Playback(gfxContext *aCtx);
-  already_AddRefed<gfxFlattenedPath> GetFlattenedPath(const gfxMatrix &aMatrix);
-  void Clear();
-
-protected:
-  float   *mArguments;
-  PRUint32 mNumCommands;
-  PRUint32 mNumArguments;
-};
-
 typedef nsSVGPathGeometryElement nsSVGPathElementBase;
 
 class nsSVGPathElement : public nsSVGPathElementBase,
                          public nsIDOMSVGPathElement,
                          public nsIDOMSVGAnimatedPathData
 {
 friend class nsSVGPathFrame;
 friend class nsSVGTextPathFrame;
 
 protected:
   friend nsresult NS_NewSVGPathElement(nsIContent **aResult,
                                        already_AddRefed<nsINodeInfo> aNodeInfo);
   nsSVGPathElement(already_AddRefed<nsINodeInfo> aNodeInfo);
-  virtual ~nsSVGPathElement();
 
 public:
+  typedef mozilla::SVGAnimatedPathSegList SVGAnimatedPathSegList;
   // interfaces:
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGPATHELEMENT
   NS_DECL_NSIDOMSVGANIMATEDPATHDATA
 
   // xxx I wish we could use virtual inheritance
   NS_FORWARD_NSIDOMNODE(nsSVGPathElementBase::)
   NS_FORWARD_