Merge mozilla-central to Places.
authorShawn Wilsher <me@shawnwilsher.com>
Mon, 13 Dec 2010 15:08:04 -0800
changeset 59386 0193e46f04a659f21eef5b20dd491834883baba4
parent 59385 b884172a3a0680629c82cdadbfcd96b732e6bd21 (current diff)
parent 59160 2b3626056e132c6bb9019836c41050b1e83c9e40 (diff)
child 59387 23c8f19e51f1b92ad7c1ccd0e73ab99d110afa6d
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
milestone2.0b8pre
Merge mozilla-central to Places.
browser/base/content/browser.js
browser/base/content/browser.xul
browser/components/nsBrowserGlue.js
configure.in
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/svg/content/src/nsSVGPoint.cpp
content/svg/content/src/nsSVGPoint.h
content/svg/content/src/nsSVGPointList.cpp
content/svg/content/src/nsSVGPointList.h
dom/ipc/ContentParent.cpp
extensions/jssh/ChangeLog
extensions/jssh/Makefile.in
extensions/jssh/install.js
extensions/jssh/jar.mn
extensions/jssh/nsIJSSh.idl
extensions/jssh/nsIJSShServer.idl
extensions/jssh/nsJSSh.cpp
extensions/jssh/nsJSSh.h
extensions/jssh/nsJSShModule.cpp
extensions/jssh/nsJSShServer.cpp
extensions/jssh/nsJSShServer.h
extensions/jssh/nsJSShStarter.js
extensions/jssh/resources/content/configure.xul
extensions/jssh/resources/content/contents.rdf
extensions/jssh/resources/content/jssh-debug.js
extensions/jssh/resources/content/tasksOverlay.xul
extensions/jssh/xemacs/moz-jssh.el
js/src/tests/js1_5/extensions/regress-313500.js
js/src/tests/js1_5/extensions/regress-325269.js
storage/src/mozStorageConnection.cpp
toolkit/components/places/src/nsNavHistory.cpp
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1155,19 +1155,20 @@ nsDocAccessible::ARIAAttributeChanged(ns
     return;
   }
 
   if (aAttribute == nsAccessibilityAtoms::aria_activedescendant) {
     // The activedescendant universal property redirects accessible focus events
     // to the element with the id that activedescendant points to
     nsCOMPtr<nsINode> focusedNode = GetCurrentFocus();
     if (nsCoreUtils::GetRoleContent(focusedNode) == aContent) {
+      nsAccessible* focusedAcc = GetAccService()->GetAccessible(focusedNode);
       nsRefPtr<nsRootAccessible> rootAcc = GetRootAccessible();
-      if (rootAcc) {
-        rootAcc->FireAccessibleFocusEvent(nsnull, focusedNode, nsnull, PR_TRUE);
+      if (rootAcc && focusedAcc) {
+        rootAcc->FireAccessibleFocusEvent(focusedAcc, nsnull, PR_TRUE);
       }
     }
     return;
   }
 
   // For aria drag and drop changes we fire a generic attribute change event;
   // at least until native API comes up with a more meaningful event.
   if (aAttribute == nsAccessibilityAtoms::aria_grabbed ||
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -309,142 +309,100 @@ nsresult nsRootAccessible::RemoveEventLi
 }
 
 nsCaretAccessible*
 nsRootAccessible::GetCaretAccessible()
 {
   return mCaretAccessible;
 }
 
-PRBool
-nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
-                                           nsINode *aNode,
-                                           nsIDOMEvent *aFocusEvent,
+void
+nsRootAccessible::FireAccessibleFocusEvent(nsAccessible* aFocusAccessible,
+                                           nsIContent* aRealFocusContent,
                                            PRBool aForceEvent,
                                            EIsFromUserInput aIsFromUserInput)
 {
   // Implementors: only fire delayed/async events from this method.
 
-  if (mCaretAccessible) {
-    nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aFocusEvent));
-    if (nsevent) {
-      // Use the originally focused node where the selection lives.
-      // For example, use the anonymous HTML:input instead of the containing
-      // XUL:textbox. In this case, sometimes it is a later focus event
-      // which points to the actual anonymous child with focus, so to be safe 
-      // we need to reset the selection listener every time.
-      // This happens because when some bindings handle focus, they retarget
-      // focus to the appropriate child inside of themselves, but DOM focus
-      // stays outside on that binding parent.
-      nsCOMPtr<nsIDOMEventTarget> domEventTarget;
-      nsevent->GetOriginalTarget(getter_AddRefs(domEventTarget));
-      nsCOMPtr<nsIContent> realFocusedNode(do_QueryInterface(domEventTarget));
-      if (!realFocusedNode) {
-        // When FireCurrentFocusEvent() synthesizes a focus event,
-        // the orignal target does not exist, so use the passed-in node
-        // which is the relevant focused node
-        realFocusedNode = do_QueryInterface(aNode);
-      }
-      if (realFocusedNode) {
-        mCaretAccessible->SetControlSelectionListener(realFocusedNode);
+  // Set selection listener for focused element.
+  if (mCaretAccessible && aRealFocusContent)
+    mCaretAccessible->SetControlSelectionListener(aRealFocusContent);
+
+  nsAccessible* focusAccessible = aFocusAccessible;
+
+  // Check for aria-activedescendant, which changes which element has focus.
+  // For activedescendant, the ARIA spec does not require that the user agent
+  // checks whether pointed node is actually a DOM descendant of the element
+  // with the aria-activedescendant attribute.
+  nsIContent* content = focusAccessible->GetContent();
+  if (content) {
+    nsAutoString id;
+    if (content->GetAttr(kNameSpaceID_None,
+                         nsAccessibilityAtoms::aria_activedescendant, id)) {
+      nsIDocument* DOMDoc = content->GetOwnerDoc();
+      nsIContent* activeDescendantContent = DOMDoc->GetElementById(id);
+
+      // If aria-activedescendant is set to nonexistant ID, then treat as focus
+      // on the activedescendant container (which has real DOM focus).
+      if (activeDescendantContent) {
+        focusAccessible =
+          GetAccService()->GetAccessible(activeDescendantContent);
       }
     }
   }
 
-  // Check for aria-activedescendant, which changes which element has focus
-  nsINode *finalFocusNode = aNode;
-  nsAccessible *finalFocusAccessible = aAccessible;
+  // Fire focus only if it changes, but always fire focus events when
+  // aForceEvent == PR_TRUE
+  nsINode* focusNode = focusAccessible->GetNode();
+  if (gLastFocusedNode == focusNode && !aForceEvent)
+    return;
 
-  nsIContent *finalFocusContent = nsCoreUtils::GetRoleContent(finalFocusNode);
-  if (finalFocusContent) {
-    nsAutoString id;
-    if (finalFocusContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_activedescendant, id)) {
-      nsIDocument *doc = aNode->GetOwnerDoc();
-      finalFocusNode = doc->GetElementById(id);
-      if (!finalFocusNode) {
-        // If aria-activedescendant is set to nonexistant ID, then treat as focus
-        // on the activedescendant container (which has real DOM focus)
-        finalFocusNode = aNode;
-      }
-      finalFocusAccessible = nsnull;
-    }
-  }
-
-  // Fire focus only if it changes, but always fire focus events when aForceEvent == PR_TRUE
-  if (gLastFocusedNode == finalFocusNode && !aForceEvent) {
-    return PR_FALSE;
-  }
+  gLastFocusedAccessiblesState = nsAccUtils::State(focusAccessible);
 
-  if (!finalFocusAccessible) {
-    finalFocusAccessible = GetAccService()->GetAccessible(finalFocusNode);
-    // For activedescendant, the ARIA spec does not require that the user agent
-    // checks whether finalFocusNode is actually a DOM descendant of the element
-    // with the aria-activedescendant attribute.
-    if (!finalFocusAccessible) {
-      return PR_FALSE;
-    }
-  }
-
-  gLastFocusedAccessiblesState = nsAccUtils::State(finalFocusAccessible);
-  PRUint32 role = finalFocusAccessible->Role();
-  if (role == nsIAccessibleRole::ROLE_MENUITEM) {
-    if (!mCurrentARIAMenubar) {  // Entering menus
-      // The natural role is the role that this type of element normally has
-      if (role != finalFocusAccessible->NativeRole()) { // Must be a DHTML menuitem
-        nsAccessible *menuBarAccessible =
-          nsAccUtils::GetAncestorWithRole(finalFocusAccessible,
-                                          nsIAccessibleRole::ROLE_MENUBAR);
-        if (menuBarAccessible) {
-          mCurrentARIAMenubar = menuBarAccessible->GetNode();
-          if (mCurrentARIAMenubar) {
-            nsRefPtr<AccEvent> menuStartEvent =
-              new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
-                           menuBarAccessible, aIsFromUserInput,
-                           AccEvent::eAllowDupes);
-            if (menuStartEvent) {
-              FireDelayedAccessibleEvent(menuStartEvent);
-            }
-          }
+  // Fire menu start/end events for ARIA menus.
+  if (focusAccessible->ARIARole() == nsIAccessibleRole::ROLE_MENUITEM) {
+    // The focus is inside a menu.
+    if (!mCurrentARIAMenubar) {
+      // Entering ARIA menu. Fire menu start event.
+      nsAccessible* menuBarAccessible =
+        nsAccUtils::GetAncestorWithRole(focusAccessible,
+                                        nsIAccessibleRole::ROLE_MENUBAR);
+      if (menuBarAccessible) {
+        mCurrentARIAMenubar = menuBarAccessible->GetNode();
+        if (mCurrentARIAMenubar) {
+          nsRefPtr<AccEvent> menuStartEvent =
+            new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
+                         menuBarAccessible, aIsFromUserInput,
+                         AccEvent::eAllowDupes);
+          if (menuStartEvent)
+            FireDelayedAccessibleEvent(menuStartEvent);
         }
       }
     }
   }
   else if (mCurrentARIAMenubar) {
+    // Focus left a menu. Fire menu end event.
     nsRefPtr<AccEvent> menuEndEvent =
       new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mCurrentARIAMenubar,
                    aIsFromUserInput, AccEvent::eAllowDupes);
     if (menuEndEvent) {
       FireDelayedAccessibleEvent(menuEndEvent);
     }
     mCurrentARIAMenubar = nsnull;
   }
 
-  nsCOMPtr<nsIContent> focusContent = do_QueryInterface(finalFocusNode);
-  nsIFrame *focusFrame = nsnull;
-  if (focusContent) {
-    nsIPresShell *shell = nsCoreUtils::GetPresShellFor(finalFocusNode);
-
-    NS_ASSERTION(shell, "No pres shell for final focus node!");
-    if (!shell)
-      return PR_FALSE;
-
-    focusFrame = focusContent->GetPrimaryFrame();
-  }
-
   NS_IF_RELEASE(gLastFocusedNode);
-  gLastFocusedNode = finalFocusNode;
+  gLastFocusedNode = focusNode;
   NS_IF_ADDREF(gLastFocusedNode);
 
   // Coalesce focus events from the same document, because DOM focus event might
   // be fired for the document node and then for the focused DOM element.
   FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
-                             finalFocusNode, AccEvent::eCoalesceFromSameDocument,
+                             focusNode, AccEvent::eCoalesceFromSameDocument,
                              aIsFromUserInput);
-
-  return PR_TRUE;
 }
 
 void
 nsRootAccessible::FireCurrentFocusEvent()
 {
   if (IsDefunct())
     return;
 
@@ -540,17 +498,17 @@ nsRootAccessible::HandleEvent(nsIDOMEven
                         nsIAccessibleStates::STATE_SELECTED)) != 0;
 
     nsRefPtr<AccEvent> accEvent =
       new AccStateChangeEvent(accessible, nsIAccessibleStates::STATE_CHECKED,
                               PR_FALSE, isEnabled);
     nsEventShell::FireEvent(accEvent);
 
     if (isEnabled)
-      FireAccessibleFocusEvent(accessible, targetNode, aEvent);
+      FireAccessibleFocusEvent(accessible, targetContent);
 
     return NS_OK;
   }
 
   if (eventType.EqualsLiteral("CheckboxStateChange")) {
     PRUint32 state = nsAccUtils::State(accessible);
 
     PRBool isEnabled = !!(state & nsIAccessibleStates::STATE_CHECKED);
@@ -644,17 +602,17 @@ nsRootAccessible::HandleEvent(nsIDOMEven
 
           accessible = GetAccService()->GetAccessibleInWeakShell(focusedItem,
                                                                  weakShell);
           if (!accessible)
             return NS_OK;
         }
       }
     }
-    FireAccessibleFocusEvent(accessible, focusedItem, aEvent);
+    FireAccessibleFocusEvent(accessible, targetContent);
   }
   else if (eventType.EqualsLiteral("blur")) {
     NS_IF_RELEASE(gLastFocusedNode);
     gLastFocusedAccessiblesState = 0;
   }
   else if (eventType.EqualsLiteral("AlertActive")) { 
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
   }
@@ -715,17 +673,17 @@ nsRootAccessible::HandleEvent(nsIDOMEven
           fireFocus = PR_TRUE;
           break;
         }
         containerContent = containerContent->GetParent();
       }
     }
     if (fireFocus) {
       // Always asynch, always from user input.
-      FireAccessibleFocusEvent(accessible, targetNode, aEvent, PR_TRUE,
+      FireAccessibleFocusEvent(accessible, targetContent, PR_TRUE,
                                eFromUserInput);
     }
   }
   else if (eventType.EqualsLiteral("DOMMenuBarActive")) {  // Always from user input
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
                             accessible, eFromUserInput);
   }
   else if (eventType.EqualsLiteral("DOMMenuBarInactive")) {  // Always from user input
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -86,32 +86,40 @@ public:
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
 
   // nsRootAccessible
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ROOTACCESSIBLE_IMPL_CID)
 
   /**
-   * Fire an accessible focus event for the current focusAccssible
-   * and attach a new selection listener, if necessary.
+   * Fire an accessible focus event for the focused accessible and attach a new
+   * selection listener to real focused element, if necessary.
+   *
+   * @param  aFocusAccessible   [in] the accessible which has received focus
+   * @param  aRealFocusContent  [in] the actual DOM element which has received
+   *                              focus (see @note section)
+   * @param  aForceEvent        [in, optional] fire a focus event even if
+   *                              the last focused item was the same
+   * @param  aIsFromUserInput   [in, optional] specifies whether the event is
+   *                              from user input
    *
-   * @param  aFocusAccessible  [in] the accessible which has received focus
-   * @param  aFocusNode        [in] the DOM node which has received focus
-   * @param  aFocusEvent       [in] DOM focus event that caused
-   *                             the node/accessible to receive focus
-   * @param  aForceEvent       [in] fire a focus event even if the last focused
-   *                             item was the same
-   * @return                    boolean -- was a focus event actually fired
+   * @note  Use the originally focused node where the selection lives as real
+   *         focus node. For example, use the anonymous HTML:input instead of
+   *         the containing XUL:textbox. In this case, sometimes it is a later
+   *         focus event which points to the actual anonymous child with focus,
+   *         so to be safe we need to reset the selection listener every time.
+   *         This happens because when some bindings handle focus, they
+   *         retarget focus to the appropriate child inside of themselves, but
+   *         DOM focus stays outside on that binding parent.
    */
-  PRBool FireAccessibleFocusEvent(nsAccessible *aFocusAccessible,
-                                  nsINode *aFocusNode,
-                                  nsIDOMEvent *aFocusEvent,
-                                  PRBool aForceEvent = PR_FALSE,
-                                  EIsFromUserInput aIsFromUserInput = eAutoDetect);
+  void FireAccessibleFocusEvent(nsAccessible* aFocusAccessible,
+                                nsIContent* aRealFocusContent,
+                                PRBool aForceEvent = PR_FALSE,
+                                EIsFromUserInput aIsFromUserInput = eAutoDetect);
 
     /**
       * Fire an accessible focus event for the current focused node,
       * if there is a focus.
       */
     void FireCurrentFocusEvent();
 
     nsCaretAccessible *GetCaretAccessible();
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -670,16 +670,25 @@ nsHTMLComboboxAccessible::
 
 PRUint32
 nsHTMLComboboxAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_COMBOBOX;
 }
 
 void
+nsHTMLComboboxAccessible::InvalidateChildren()
+{
+  nsAccessibleWrap::InvalidateChildren();
+
+  if (mListAccessible)
+    mListAccessible->InvalidateChildren();
+}
+
+void
 nsHTMLComboboxAccessible::CacheChildren()
 {
   nsIFrame* frame = GetFrame();
   if (!frame)
     return;
 
   nsIComboboxControlFrame *comboFrame = do_QueryFrame(frame);
   if (!comboFrame)
--- a/accessible/src/html/nsHTMLSelectAccessible.h
+++ b/accessible/src/html/nsHTMLSelectAccessible.h
@@ -191,16 +191,17 @@ public:
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
+  virtual void InvalidateChildren();
 
 protected:
   // nsAccessible
   virtual void CacheChildren();
 
   // nsHTMLComboboxAccessible
 
   /**
--- a/accessible/tests/mochitest/treeupdate/Makefile.in
+++ b/accessible/tests/mochitest/treeupdate/Makefile.in
@@ -46,14 +46,15 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		test_ariadialog.html \
 		test_doc.html \
 		test_list_editabledoc.html \
 		test_list.html \
 		test_recreation.html \
+		test_select.html \
 		test_textleaf.html \
 		test_visibility.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_select.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Add select options test</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+
+    function addOptions(aID)
+    {
+      this.selectNode = getNode(aID);
+      this.select = getAccessible(this.selectNode);
+
+      this.invoke = function addOptions_invoke()
+      {
+        for (i = 0; i < 2; i++) {
+          var opt = document.createElement("option");
+          opt.value = i;
+          opt.text = "Option: Value " + i;
+
+          this.selectNode.add(opt, null);
+        }
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.select)
+      ];
+
+      this.finalCheck = function addOptions_finalCheck()
+      {
+        var tree =
+          { COMBOBOX: [
+            { COMBOBOX_LIST: [
+              { COMBOBOX_OPTION: [
+                { TEXT_LEAF: [] }
+              ] },
+              { COMBOBOX_OPTION: [
+                { TEXT_LEAF: [] }
+              ] }
+            ] }
+          ] };
+        testAccessibleTree(this.select, tree);
+      }
+
+      this.getID = function addOptions_getID()
+      {
+        return "test elements insertion into a select";
+      }
+    }
+
+    //gA11yEventDumpID = "debug";
+
+    function doTest()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new addOptions("select"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=616452"
+     title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree">
+    Mozilla Bug 616452</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <select id="select"></select>
+
+  <div id="debug"/>
+</body>
+</html>
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,45 +1,78 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0"?>
 <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem id="fdm_ffext@freedownloadmanager.org">
       <versionRange minVersion="1.0" maxVersion="1.3.1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
            <versionRange minVersion="3.0a1" maxVersion="*"/>
         </targetApplication>
       </versionRange>
     </emItem>
     <emItem id="langpack-vi-VN@firefox.mozilla.org">
       <versionRange minVersion="2.0" maxVersion="2.0"/>
     </emItem>
+    <emItem id="masterfiler@gmail.com">
+      <versionRange severity="3"/>
+    </emItem>
     <emItem id="mozilla_cc@internetdownloadmanager.com">
+      <versionRange minVersion=" " maxVersion="6.9.8">
+        <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+           <versionRange minVersion="3.7a1pre" maxVersion="*"/>
+        </targetApplication>
+      </versionRange>
       <versionRange minVersion="2.1" maxVersion="3.3">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
            <versionRange minVersion="3.0a1" maxVersion="*"/>
         </targetApplication>
       </versionRange>
     </emItem>
+    <emItem id="msntoolbar@msn.com">
+      <versionRange minVersion=" " maxVersion="6.*"/>
+    </emItem>
+    <emItem id="personas@christopher.beard">
+      <versionRange minVersion="1.6" maxVersion="1.6">
+        <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+           <versionRange minVersion="3.6" maxVersion="3.6.*"/>
+        </targetApplication>
+      </versionRange>
+    </emItem>
     <emItem id="support@daemon-tools.cc">
       <versionRange minVersion=" " maxVersion="1.0.0.5"/>
     </emItem>
+    <emItem id="yslow@yahoo-inc.com">
+      <versionRange minVersion="2.0.5" maxVersion="2.0.5">
+        <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+           <versionRange minVersion="3.5.7" maxVersion="*"/>
+        </targetApplication>
+      </versionRange>
+    </emItem>
     <emItem id="{2224e955-00e9-4613-a844-ce69fccaae91}"/>
+    <emItem id="{27182e60-b5f3-411c-b545-b44205977502}">
+      <versionRange minVersion="1.0" maxVersion="1.0"/>
+    </emItem>
+    <emItem id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
+      <versionRange minVersion="2.2" maxVersion="2.2"/>
+    </emItem>
     <emItem id="{3f963a5b-e555-4543-90e2-c3908898db71}">
       <versionRange minVersion=" " maxVersion="8.5"/>
     </emItem>
     <emItem id="{4B3803EA-5230-4DC3-A7FC-33638F3D3542}">
       <versionRange minVersion="1.2" maxVersion="1.2">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
            <versionRange minVersion="3.0a1" maxVersion="*"/>
         </targetApplication>
       </versionRange>
     </emItem>
+    <emItem id="{8CE11043-9A15-4207-A565-0C94C42D590D}"/>
     <emItem id="{B13721C7-F507-4982-B2E5-502A71474FED}">
       <versionRange minVersion=" " maxVersion="3.3.0.3970" severity="1"/>
     </emItem>
+    <emItem id="{E8E88AB0-7182-11DF-904E-6045E0D72085}"/>
   </emItems>
 <pluginItems>
   <pluginItem>
     <match name="name" exp="^Yahoo Application State Plugin$"/>
     <match name="description" exp="^Yahoo Application State Plugin$"/>
     <match name="filename" exp="npYState.dll"/>
     <versionRange>
       <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -56,10 +89,29 @@
       </targetApplication>
     </versionRange>
   </pluginItem>
   <pluginItem>
     <match name="filename" exp="NPFFAddOn.dll"/>
     <versionRange>
     </versionRange>
   </pluginItem>
+  <pluginItem>
+    <match name="filename" exp="NPMySrch.dll"/>
+    <versionRange>
+    </versionRange>
+  </pluginItem>
+  <pluginItem>
+    <match name="filename" exp="npViewpoint.dll"/>
+    <versionRange>
+      <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+        <versionRange minVersion="3.0" maxVersion="*"/>
+      </targetApplication>
+    </versionRange>
+  </pluginItem>
+  <pluginItem>
+    <match name="name" exp="[0-6]\.0\.[01]\d{2}\.\d+"/>
+    <match name="filename" exp="npdeploytk.dll"/>
+    <versionRange severity="1">
+    </versionRange>
+  </pluginItem>
 </pluginItems>
 </blocklist>
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -60,16 +60,24 @@ function init(aEvent)
       distroIdField.value = distroId + " - " + distroVersion;
       distroIdField.style.display = "block";
     }
   }
   catch (e) {
     // Pref is unset
   }
 
+  // Include the build ID if this is a "pre" (i.e. non-release) build
+  let version = Services.appinfo.version;
+  if (version.indexOf("pre") != -1) {
+    let buildID = Services.appinfo.appBuildID;
+    let buildDate = buildID.slice(0,4) + "-" + buildID.slice(4,6) + "-" + buildID.slice(6,8);
+    document.getElementById("version").value += " (" + buildDate + ")";
+  }
+
 #ifdef MOZ_UPDATER
   gAppUpdater = new appUpdater();
 #endif
 
 #ifdef XP_MACOSX
   // it may not be sized at this point, and we need its width to calculate its position
   window.sizeToContent();
   window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -141,36 +141,38 @@
                     oncommand="HUDConsoleUI.toggleHUD();"
                     key="key_webConsole"/>
           <menuitem id="appmenu_pageInspect"
                     hidden="true"
                     label="&inspectMenu.label;"
                     type="checkbox"
                     command="Tools:Inspect"
                     key="key_inspect"/>
-          <menuseparator/>
           <menuitem id="appmenu_pageSource"
                     label="&viewPageSourceCmd.label;"
                     command="View:PageSource"
                     key="key_viewSource"/>
           <menuseparator/>
 #define ID_PREFIX appmenu_developer_
+#define OMIT_ACCESSKEYS
 #include browser-charsetmenu.inc
 #undef ID_PREFIX
-          <menuseparator/>
+#undef OMIT_ACCESSKEYS
           <menuitem label="&goOfflineCmd.label;"
                     type="checkbox"
                     observes="workOfflineMenuitemState"
                     oncommand="BrowserOffline.toggleOfflineStatus();"/>
         </menupopup>
       </menu>
       <menuseparator class="appmenu-menuseparator"/>
 #define ID_PREFIX appmenu_
+#define OMIT_ACCESSKEYS
 #include browser-charsetmenu.inc
 #undef ID_PREFIX
+#undef OMIT_ACCESSKEYS
       <menuitem id="appmenu_fullScreen"
                 class="menuitem-tooltip"
                 label="&fullScreenCmd.label;"
                 type="checkbox"
                 observes="View:FullScreen"
                 key="key_fullScreen"/>
       <menuitem id="appmenu-quit"
                 class="menuitem-iconic"
@@ -346,17 +348,16 @@
                       oncommand="openTroubleshootingPage()"
                       onclick="checkForMiddleClick(this,event);"/>
             <menuitem id="appmenu_feedbackPage"
                       label="&helpFeedbackPage.label;"
                       oncommand="openFeedbackPage()"
                       onclick="checkForMiddleClick(this, event);"/>
             <menuseparator/>
             <menuitem id="appmenu_safeMode"
-                      accesskey="&appMenuSafeMode.accesskey;"
                       label="&appMenuSafeMode.label;"
                       oncommand="safeModeRestart();"/>
             <menuseparator/>
             <menuitem id="appmenu_about"
                       label="&aboutProduct.label;"
                       oncommand="openAboutDialog();"/>
           </menupopup>
       </splitmenu>
--- a/browser/base/content/browser-charsetmenu.inc
+++ b/browser/base/content/browser-charsetmenu.inc
@@ -33,17 +33,19 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 #filter substitution
 
 #expand <menu id="__ID_PREFIX__charsetMenu"
     label="&charsetMenu.label;"
+#ifndef OMIT_ACCESSKEYS
     accesskey="&charsetMenu.accesskey;"
+#endif
     datasources="rdf:charset-menu"
     ref="NC:BrowserCharsetMenuRoot"
     oncommand="MultiplexHandler(event)"
     onpopupshowing="CreateMenu('browser');UpdateMenus(event)"
     onpopupshown="CreateMenu('more-menu');"
     observes="isImage">
   <template>
     <rule rdf:type="http://home.netscape.com/NC-rdf#BookmarkSeparator">
@@ -54,100 +56,137 @@
     <rule>
       <menupopup>
       <menuitem type="radio" name="charsetGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
       </menupopup>
     </rule>
   </template>
 
   <menupopup>
-  <menu label="&charsetMenuAutodet.label;" accesskey="&charsetMenuAutodet.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserAutodetMenuRoot">
+  <menu label="&charsetMenuAutodet.label;"
+#ifndef OMIT_ACCESSKEYS
+        accesskey="&charsetMenuAutodet.accesskey;"
+#endif
+        datasources="rdf:charset-menu" ref="NC:BrowserAutodetMenuRoot">
     <template>
       <rule rdf:type="http://home.netscape.com/NC-rdf#CharsetDetector">
         <menupopup>
         <menuitem type="radio" name="detectorGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
         </menupopup>
       </rule>
     </template>
     <menupopup>
     </menupopup>
   </menu>
-  <menu label="&charsetMenuMore.label;" accesskey="&charsetMenuMore.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMoreCharsetMenuRoot">
+  <menu label="&charsetMenuMore.label;"
+#ifndef OMIT_ACCESSKEYS
+        accesskey="&charsetMenuMore.accesskey;"
+#endif
+        datasources="rdf:charset-menu" ref="NC:BrowserMoreCharsetMenuRoot">
     <template>
       <rule>
         <menupopup>
         <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
         </menupopup>
       </rule>
     </template>
     <menupopup>
-      <menu label="&charsetMenuMore1.label;" accesskey="&charsetMenuMore1.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore1CharsetMenuRoot">
+      <menu label="&charsetMenuMore1.label;"
+#ifndef OMIT_ACCESSKEYS
+            accesskey="&charsetMenuMore1.accesskey;"
+#endif
+            datasources="rdf:charset-menu" ref="NC:BrowserMore1CharsetMenuRoot">
         <template>
           <rule>
             <menupopup>
             <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
             </menupopup>
           </rule>
         </template>
         <menupopup>
         </menupopup>
       </menu>
-      <menu label="&charsetMenuMore2.label;" accesskey="&charsetMenuMore2.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore2CharsetMenuRoot">
+      <menu label="&charsetMenuMore2.label;"
+#ifndef OMIT_ACCESSKEYS
+            accesskey="&charsetMenuMore2.accesskey;"
+#endif
+            datasources="rdf:charset-menu" ref="NC:BrowserMore2CharsetMenuRoot">
         <template>
           <rule>
             <menupopup>
             <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
             </menupopup>
           </rule>
         </template>
         <menupopup>
         </menupopup>
       </menu>
-      <menu label="&charsetMenuMore3.label;" accesskey="&charsetMenuMore3.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore3CharsetMenuRoot">
+      <menu label="&charsetMenuMore3.label;"
+#ifndef OMIT_ACCESSKEYS
+            accesskey="&charsetMenuMore3.accesskey;"
+#endif
+            datasources="rdf:charset-menu" ref="NC:BrowserMore3CharsetMenuRoot">
         <template>
           <rule>
             <menupopup>
             <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
             </menupopup>
           </rule>
         </template>
         <menupopup>
         </menupopup>
       </menu>
-      <menu label="&charsetMenuMore4.label;" accesskey="&charsetMenuMore4.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore4CharsetMenuRoot">
+      <menu label="&charsetMenuMore4.label;"
+#ifndef OMIT_ACCESSKEYS
+            accesskey="&charsetMenuMore4.accesskey;"
+#endif
+            datasources="rdf:charset-menu" ref="NC:BrowserMore4CharsetMenuRoot">
         <template>
           <rule>
             <menupopup>
             <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
             </menupopup>
           </rule>
         </template>
         <menupopup>
         </menupopup>
       </menu>
-      <menu label="&charsetMenuMore5.label;" accesskey="&charsetMenuMore5.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore5CharsetMenuRoot">
+      <menu label="&charsetMenuMore5.label;"
+#ifndef OMIT_ACCESSKEYS
+            accesskey="&charsetMenuMore5.accesskey;"
+#endif
+            datasources="rdf:charset-menu" ref="NC:BrowserMore5CharsetMenuRoot">
         <template>
           <rule>
             <menupopup>
             <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
             </menupopup>
           </rule>
         </template>
         <menupopup>
         </menupopup>
       </menu>
-      <menu label="&charsetMenuUnicode.label;" accesskey="&charsetMenuUnicode.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserUnicodeCharsetMenuRoot">
+      <menu label="&charsetMenuUnicode.label;"
+#ifndef OMIT_ACCESSKEYS
+            accesskey="&charsetMenuUnicode.accesskey;"
+#endif
+            datasources="rdf:charset-menu" ref="NC:BrowserUnicodeCharsetMenuRoot">
         <template>
           <rule>
             <menupopup>
             <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
             </menupopup>
           </rule>
         </template>
         <menupopup>
         </menupopup>
       </menu>
       <menuseparator />
     </menupopup>
   </menu>
-  <menuitem name="charsetCustomize" accesskey="&charsetCustomize.accesskey;" label="&charsetCustomize.label;" oncommand="window.openDialog('chrome://global/content/customizeCharset.xul','PrefWindow', 'chrome,modal=yes,resizable=yes', 'browser')"/>
+  <menuitem name="charsetCustomize"
+#ifndef OMIT_ACCESSKEYS
+            accesskey="&charsetCustomize.accesskey;"
+#endif
+            label="&charsetCustomize.label;"
+            oncommand="window.openDialog('chrome://global/content/customizeCharset.xul', 'PrefWindow', 'chrome,modal=yes,resizable=yes', 'browser');"/>
   </menupopup>
 </menu>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -250,20 +250,29 @@ html|*.urlbar-input {
 }
 
 .urlbar-over-link-box:not([overlinkstate]) {
   opacity: 0;
 }
 
 /* For results that are actions, their description text is shown instead of
    the URL - this needs to follow the locale's direction, unlike URLs. */
-richlistitem[type~="action"]:-moz-locale-dir(rtl) > .ac-url-box {
+panel:not([noactions]) > richlistbox > richlistitem[type~="action"]:-moz-locale-dir(rtl) > .ac-url-box {
   direction: rtl;
 }
 
+panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .ac-url > .ac-action-text,
+panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .ac-action-icon {
+  visibility: collapse;
+}
+
+panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .ac-url > .ac-url-text {
+  visibility: visible;
+}
+
 #urlbar:not([actiontype]) > #urlbar-display {
   display: none;
 }
 
 #wrapper-urlbar-container > #urlbar-container > #urlbar {
   -moz-user-input: disabled;
   cursor: -moz-grab;
 }
@@ -470,8 +479,12 @@ window[chromehidden~="toolbar"] toolbar:
 #status-bar {
   display: -moz-box;
 }
 
 /* Remove the resizer from the statusbar compatibility shim */
 #status-bar > .statusbar-resizerpanel {
   display: none;
 }
+
+browser[tabmodalPromptShowing] {
+  -moz-user-focus: none !important;
+}
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1534,42 +1534,39 @@ function delayedStartup(isLoadingBlank, 
   gSyncUI.init();
 #endif
 
   TabView.init();
 
   // Enable Inspector?
   let enabled = gPrefService.getBoolPref(InspectorUI.prefEnabledName);
   if (enabled) {
-    document.getElementById("menu_pageinspect").setAttribute("hidden", false);
+    document.getElementById("menu_pageinspect").hidden = false;
     document.getElementById("Tools:Inspect").removeAttribute("disabled");
-    let appMenuInspect = document.getElementById("appmenu_pageInspect");
-    if (appMenuInspect)
-      appMenuInspect.setAttribute("hidden", false);
+#ifdef MENUBAR_CAN_AUTOHIDE
+    document.getElementById("appmenu_pageInspect").hidden = false;
+#endif
   }
 
   // Enable Error Console?
   // XXX Temporarily always-enabled, see bug 601201
   let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled");
   if (consoleEnabled) {
     document.getElementById("javascriptConsole").hidden = false;
     document.getElementById("key_errorConsole").removeAttribute("disabled");
   }
 
+#ifdef MENUBAR_CAN_AUTOHIDE
   // If the user (or the locale) hasn't enabled the top-level "Character
   // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
   // hide it.
-  const showCharacterEncodingPref = "browser.menu.showCharacterEncoding";
-  let extraCharacterEncodingMenuEnabled = gPrefService.
-    getComplexValue(showCharacterEncodingPref, Ci.nsIPrefLocalizedString).data;
-  if (extraCharacterEncodingMenuEnabled !== "true") {
-    let charsetMenu = document.getElementById("appmenu_charsetMenu");
-    if (charsetMenu)
-      charsetMenu.setAttribute("hidden", "true");
-  }
+  if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
+                                             Ci.nsIPrefLocalizedString).data)
+    document.getElementById("appmenu_charsetMenu").hidden = true;
+#endif
 
   Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
 }
 
 function BrowserShutdown()
 {
   if (Win7Features)
     Win7Features.onCloseWindow();
@@ -2855,44 +2852,39 @@ function FillInHTMLTooltip(tipElement)
   });
 
   return retVal;
 }
 
 var browserDragAndDrop = {
   canDropLink: function (aEvent) Services.droppedLinkHandler.canDropLink(aEvent, true),
 
-  dragOver: function (aEvent, statusString)
+  dragOver: function (aEvent)
   {
     if (this.canDropLink(aEvent)) {
       aEvent.preventDefault();
-
-      if (statusString) {
-        XULBrowserWindow.setStatusText(gNavigatorBundle.getString(statusString));
-      }
     }
   },
 
   drop: function (aEvent, aName) Services.droppedLinkHandler.dropLink(aEvent, aName)
 };
 
 var homeButtonObserver = {
   onDrop: function (aEvent)
     {
       setTimeout(openHomeDialog, 0, browserDragAndDrop.drop(aEvent, { }));
     },
 
   onDragOver: function (aEvent)
     {
-      browserDragAndDrop.dragOver(aEvent, "droponhomebutton");
+      browserDragAndDrop.dragOver(aEvent);
       aEvent.dropEffect = "link";
     },
   onDragExit: function (aEvent)
     {
-      XULWindowBrowser.setStatusText("");
     }
 }
 
 function openHomeDialog(aURL)
 {
   var promptTitle = gNavigatorBundle.getString("droponhometitle");
   var promptMsg   = gNavigatorBundle.getString("droponhomemsg");
   var pressedVal  = Services.prompt.confirmEx(window, promptTitle, promptMsg,
@@ -2919,35 +2911,33 @@ var bookmarksButtonObserver = {
     let url = browserDragAndDrop.drop(aEvent, name);
     try {
       PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(url), name);
     } catch(ex) { }
   },
 
   onDragOver: function (aEvent)
   {
-    browserDragAndDrop.dragOver(aEvent, "droponbookmarksbutton");
+    browserDragAndDrop.dragOver(aEvent);
     aEvent.dropEffect = "link";
   },
 
   onDragExit: function (aEvent)
   {
-    XULWindowBrowser.setStatusText("");
   }
 }
 
 var newTabButtonObserver = {
   onDragOver: function (aEvent)
   {
-    browserDragAndDrop.dragOver(aEvent, "droponnewtabbutton");
+    browserDragAndDrop.dragOver(aEvent);
   },
 
   onDragExit: function (aEvent)
   {
-    XULWindowBrowser.setStatusText("");
   },
 
   onDrop: function (aEvent)
   {
     let url = browserDragAndDrop.drop(aEvent, { });
     var postData = {};
     url = getShortcutOrURI(url, postData);
     if (url) {
@@ -2955,48 +2945,45 @@ var newTabButtonObserver = {
       openNewTabWith(url, null, postData.value, aEvent, true);
     }
   }
 }
 
 var newWindowButtonObserver = {
   onDragOver: function (aEvent)
   {
-    browserDragAndDrop.dragOver(aEvent, "droponnewwindowbutton");
+    browserDragAndDrop.dragOver(aEvent);
   },
   onDragExit: function (aEvent)
   {
-    XULWindowBrowser.setStatusText("");
   },
   onDrop: function (aEvent)
   {
     let url = browserDragAndDrop.drop(aEvent, { });
     var postData = {};
     url = getShortcutOrURI(url, postData);
     if (url) {
       // allow third-party services to fixup this URL
       openNewWindowWith(url, null, postData.value, true);
     }
   }
 }
 
 var DownloadsButtonDNDObserver = {
   onDragOver: function (aEvent)
   {
-    XULWindowBrowser.setStatusText(gNavigatorBundle.getString("dropondownloadsbutton"));
     var types = aEvent.dataTransfer.types;
     if (types.contains("text/x-moz-url") ||
         types.contains("text/uri-list") ||
         types.contains("text/plain"))
       aEvent.preventDefault();
   },
 
   onDragExit: function (aEvent)
   {
-    XULWindowBrowser.setStatusText("");
   },
 
   onDrop: function (aEvent)
   {
     let name = { };
     let url = browserDragAndDrop.drop(aEvent, name);
     if (url)
       saveURL(url, name, null, true, true);
@@ -8083,18 +8070,27 @@ function duplicateTabIn(aTab, where, his
  * - If an add-on was installed, incrementing the count, show the bar.
  * - If an add-on was uninstalled, and no more items are left, hide the bar.
  */
 let AddonsMgrListener = {
   get addonBar() document.getElementById("addon-bar"),
   get statusBar() document.getElementById("status-bar"),
   getAddonBarItemCount: function() {
     // Take into account the contents of the status bar shim for the count.
-    return this.addonBar.childNodes.length - 1 +
-           this.statusBar.childNodes.length;
+    var itemCount = this.statusBar.childNodes.length;
+
+    var defaultOrNoninteractive = this.addonBar.getAttribute("defaultset")
+                                      .split(",")
+                                      .concat(["separator", "spacer", "spring"]);
+    this.addonBar.currentSet.split(",").forEach(function (item) {
+      if (defaultOrNoninteractive.indexOf(item) == -1)
+        itemCount++;
+    });
+
+    return itemCount;
   },
   onInstalling: function(aAddon) {
     this.lastAddonBarCount = this.getAddonBarItemCount();
   },
   onInstalled: function(aAddon) {
     if (this.getAddonBarItemCount() > this.lastAddonBarCount)
       setToolbarVisibility(this.addonBar, true);
   },
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -85,17 +85,17 @@
         titlemodifier_privatebrowsing="&mainWindow.titlemodifier;@PRE_RELEASE_SUFFIX@ &mainWindow.titlePrivateBrowsingSuffix;"
 #endif
         titlemenuseparator="&mainWindow.titlemodifiermenuseparator;"
         lightweightthemes="true"
         lightweightthemesfooter="browser-bottombox"
         windowtype="navigator:browser"
         screenX="4" screenY="4"
         browsingmode="normal"
-        persist="screenX screenY width height sizemode"> 
+        persist="screenX screenY width height sizemode">
 
 # All JS files which are not content (only) dependent that browser.xul
 # wishes to include *must* go into the global-scripts.inc file
 # so that they can be shared by macBrowserOverlay.xul.
 #include global-scripts.inc
 <script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
 
 #ifdef MOZ_SAFE_BROWSING
@@ -122,17 +122,17 @@
       <menuitem id="context_unpinTab" label="&unpinAppTab.label;" hidden="true"
                 accesskey="&unpinAppTab.accesskey;"
                 oncommand="gBrowser.unpinTab(TabContextMenu.contextTab);"/>
       <menu id="context_tabViewMenu" label="&moveToGroup.label;"
             accesskey="&moveToGroup.accesskey;">
         <menupopup id="context_tabViewMenuPopup"
                    onpopupshowing="if (event.target == this) TabView.updateContextMenu(TabContextMenu.contextTab, this);">
           <menuseparator id="context_tabViewNamedGroups" hidden="true"/>
-          <menuitem label="&moveToNewGroup.label;"
+          <menuitem id="context_tabViewNewGroup" label="&moveToNewGroup.label;"
                     oncommand="TabView.moveTabTo(TabContextMenu.contextTab, null);"/>
         </menupopup>
       </menu>
       <menuitem id="context_openTabInWindow" label="&moveToNewWindow.label;"
                 accesskey="&moveToNewWindow.accesskey;"
                 tbattr="tabbrowser-multiple"
                 oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
       <menuseparator/>
@@ -297,17 +297,17 @@
                 label="&viewTabsOnTop.label;"
                 accesskey="&viewTabsOnTop.accesskey;"/>
       <menuseparator/>
       <menuitem command="cmd_CustomizeToolbars"
                 label="&viewCustomizeToolbar.label;"
                 accesskey="&viewCustomizeToolbar.accesskey;"/>
     </menupopup>
 
-    <menupopup id="blockedPopupOptions" 
+    <menupopup id="blockedPopupOptions"
                onpopupshowing="gPopupBlockerObserver.fillPopupList(event);">
       <menuitem observes="blockedPopupAllowSite"/>
       <menuitem observes="blockedPopupEditSettings"/>
       <menuitem observes="blockedPopupDontShowMessage"/>
       <menuseparator observes="blockedPopupsSeparator"/>
     </menupopup>
 
     <menupopup id="autohide-context"
@@ -475,31 +475,31 @@
              mode="icons" iconsize="small" defaulticonsize="small"
              lockiconsize="true"
 #ifdef MENUBAR_CAN_AUTOHIDE
              toolbarname="&menubarCmd.label;"
              accesskey="&menubarCmd.accesskey;"
 #endif
              context="toolbar-context-menu">
       <toolbaritem id="menubar-items" align="center">
-# The entire main menubar is placed into browser-menubar.inc, so that it can be shared by 
+# The entire main menubar is placed into browser-menubar.inc, so that it can be shared by
 # hiddenWindow.xul.
 #include browser-menubar.inc
       </toolbaritem>
     </toolbar>
 
     <toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
              toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
              fullscreentoolbar="true" mode="icons" customizable="true"
 #ifdef WINCE
              iconsize="small" defaulticonsize="small"
-             defaultset="unified-back-forward-button,home-button,urlbar-container,reload-button,stop-button,search-container,bookmarks-menu-button-container,navigator-throbber,fullscreenflex,window-controls"
+             defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,home-button,bookmarks-menu-button-container,navigator-throbber,fullscreenflex,window-controls"
 #else
              iconsize="large"
-             defaultset="unified-back-forward-button,home-button,urlbar-container,reload-button,stop-button,search-container,bookmarks-menu-button-container,fullscreenflex,window-controls"
+             defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,home-button,bookmarks-menu-button-container,fullscreenflex,window-controls"
 #endif
              context="toolbar-context-menu">
 
       <toolbaritem id="unified-back-forward-button" class="chromeclass-toolbar-additional"
                    context="backForwardMenu" removable="true"
                    title="&backForwardItem.title;">
         <toolbarbutton id="back-button" class="toolbarbutton-1"
                        label="&backCmd.label;"
@@ -508,26 +508,16 @@
                        tooltiptext="&backButton.tooltip;"/>
         <toolbarbutton id="forward-button" class="toolbarbutton-1"
                        label="&forwardCmd.label;"
                        command="Browser:ForwardOrForwardDuplicate"
                        onclick="checkForMiddleClick(this, event);"
                        tooltiptext="&forwardButton.tooltip;"/>
       </toolbaritem>
 
-      <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     persist="class" removable="true"
-                     label="&homeButton.label;"
-                     ondragover="homeButtonObserver.onDragOver(event)"
-                     ondragenter="homeButtonObserver.onDragOver(event)"
-                     ondrop="homeButtonObserver.onDrop(event)"
-                     ondragexit="homeButtonObserver.onDragExit(event)"
-                     onclick="BrowserGoHome(event);"
-                     aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
-
       <toolbaritem id="urlbar-container" align="center" flex="400" persist="width" combined="true"
                    title="&locationItem.title;" class="chromeclass-location" removable="true">
         <textbox id="urlbar" flex="1"
                  placeholder="&urlbar.placeholder;"
                  type="autocomplete"
                  autocompletesearch="history"
                  autocompletesearchparam="enable-actions"
                  autocompletepopup="PopupAutoCompleteRichResult"
@@ -613,16 +603,26 @@
                      tooltiptext="&stopButton.tooltip;"/>
 
       <toolbaritem id="search-container" title="&searchItem.title;"
                    align="center" class="chromeclass-toolbar-additional"
                    flex="100" persist="width" removable="true">
         <searchbar id="searchbar" flex="1"/>
       </toolbaritem>
 
+      <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     persist="class" removable="true"
+                     label="&homeButton.label;"
+                     ondragover="homeButtonObserver.onDragOver(event)"
+                     ondragenter="homeButtonObserver.onDragOver(event)"
+                     ondrop="homeButtonObserver.onDrop(event)"
+                     ondragexit="homeButtonObserver.onDragExit(event)"
+                     onclick="BrowserGoHome(event);"
+                     aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
+
       <toolbaritem id="bookmarks-menu-button-container"
                    class="chromeclass-toolbar-additional"
                    removable="true"
                    title="&bookmarksMenuButton.label;">
         <toolbarbutton id="bookmarks-menu-button"
                        type="menu"
                        class="toolbarbutton-1"
                        label="&bookmarksMenuButton.label;"
@@ -940,16 +940,17 @@
                    onpopupshowing="return FeedHandler.buildFeedList(this);"
                    oncommand="return FeedHandler.subscribeToFeed(null, event);"
                    onclick="checkForMiddleClick(this, event);"/>
       </toolbarbutton>
     </toolbarpalette>
   </toolbox>
 
   <hbox flex="1" id="browser">
+    <vbox id="browser-border-start" hidden="true" layer="true"/>
     <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
       <sidebarheader id="sidebar-header" align="center">
         <label id="sidebar-title" persist="value" flex="1" crop="end" control="sidebar"/>
         <image id="sidebar-throbber"/>
         <toolbarbutton class="tabs-closebutton" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="toggleSidebar();"/>
       </sidebarheader>
       <browser id="sidebar" flex="1" autoscroll="false" disablehistory="true"
                 style="min-width: 14em; width: 18em; max-width: 36em;"/>
@@ -959,28 +960,32 @@
     <vbox id="appcontent" flex="1">
       <tabbrowser id="content" disablehistory="true"
                   flex="1" contenttooltip="aHTMLTooltip"
                   tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   onclick="return contentAreaClick(event, false);"/>
     </vbox>
+    <vbox id="browser-border-end" hidden="true" layer="true"/>
   </hbox>
 
   <vbox id="browser-bottombox" layer="true">
     <toolbar id="addon-bar"
              toolbarname="&addonBarCmd.label;" accesskey="&addonBarCmd.accesskey;"
              collapsed="true"
              class="toolbar-primary chromeclass-toolbar"
              context="toolbar-context-menu" toolboxid="navigator-toolbox"
              mode="icons" iconsize="small" defaulticonsize="small"
              lockiconsize="true"
-             defaultset="status-bar"
-             customizable="true" align="right">
+             defaultset="addonbar-closebutton,spring,status-bar"
+             customizable="true">
+      <toolbarbutton id="addonbar-closebutton"
+                     tooltiptext="&addonBarCloseButton.tooltip;"
+                     oncommand="setToolbarVisibility(this.parentNode, false);"/>
       <statusbar id="status-bar"/>
     </toolbar>
   </vbox>
 
 #ifndef XP_UNIX
   <svg:svg height="0">
     <svg:mask id="winstripe-keyhole-forward-mask" maskContentUnits="objectBoundingBox">
       <svg:rect x="0" y="0" width="1" height="1" fill="white"/>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/syncAddDevice.js
@@ -0,0 +1,174 @@
+/* ***** 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 Firefox Sync.
+ *
+ * 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):
+ * Philipp von Weitershausen <philipp@weitershausen.de>
+ *
+ * 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 ***** */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+Cu.import("resource://services-sync/main.js");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const PIN_PART_LENGTH = 4;
+
+const ADD_DEVICE_PAGE       = 0;
+const DEVICE_CONNECTED_PAGE = 1;
+const SYNC_KEY_PAGE         = 2;
+
+let gSyncAddDevice = {
+
+  init: function init() {
+    this.pin1.setAttribute("maxlength", PIN_PART_LENGTH);
+    this.pin2.setAttribute("maxlength", PIN_PART_LENGTH);
+    this.pin3.setAttribute("maxlength", PIN_PART_LENGTH);
+
+    this.nextFocusEl = {pin1: this.pin2,
+                        pin2: this.pin3,
+                        pin3: this.wizard.getButton("next")};
+
+    this.throbber = document.getElementById("add-device-throbber");
+    this.errorRow = document.getElementById("errorRow");
+  },
+
+  onPageShow: function onPageShow() {
+    this.wizard.getButton("back").hidden = true;
+
+    switch (this.wizard.pageIndex) {
+      case ADD_DEVICE_PAGE:
+        this.wizard.canAdvance = false;
+        this.wizard.canRewind = false;
+        this.pin1.focus();
+        break;
+      case DEVICE_CONNECTED_PAGE:
+        this.wizard.canAdvance = true;
+        this.wizard.canRewind = false;
+        this.wizard.getButton("next").hidden = true;
+        this.wizard.getButton("cancel").hidden = true;
+        this.wizard.getButton("finish").hidden = false;
+        break;
+      case SYNC_KEY_PAGE:
+        this.wizard.canAdvance = true;
+        this.wizard.canRewind = true;
+        this.wizard.getButton("back").hidden = false;
+        document.getElementById("weavePassphrase").value =
+          Weave.Utils.hyphenatePassphrase(Weave.Service.passphrase);
+        break;
+    }
+  },
+
+  onWizardAdvance: function onWizardAdvance() {
+    switch (this.wizard.pageIndex) {
+      case ADD_DEVICE_PAGE:
+        this.startTransfer();
+        return false;
+      case DEVICE_CONNECTED_PAGE:
+        window.close();
+        return false;
+    }
+    return true;
+  },
+
+  startTransfer: function startTransfer() {
+    this.errorRow.hidden = true;
+    let self = this;
+    this._jpakeclient = new Weave.JPAKEClient({
+      onComplete: function onComplete() {
+        delete self._jpakeclient;
+        self.wizard.pageIndex = DEVICE_CONNECTED_PAGE;
+      },
+      onAbort: function onAbort(error) {
+        delete self._jpakeclient;
+
+        // Aborted by user, ignore.
+        if (!error)
+          return;
+
+        self.errorRow.hidden = false;
+        self.throbber.hidden = true;
+        self.pin1.value = self.pin2.value = self.pin3.value = "";
+        self.pin1.disabled = self.pin2.disabled = self.pin3.disabled = false;
+        self.pin1.focus();
+      }
+    });
+    this.throbber.hidden = false;
+    this.pin1.disabled = this.pin2.disabled = this.pin3.disabled = true;
+    this.wizard.canAdvance = false;
+
+    let pin = this.pin1.value + this.pin2.value + this.pin3.value;
+    let credentials = {account: Weave.Service.account,
+                       password: Weave.Service.password,
+                       synckey: Weave.Service.passphrase,
+                       serverURL: Weave.Service.serverURL};
+    this._jpakeclient.sendWithPIN(pin, credentials);
+  },
+
+  onWizardBack: function onWizardBack() {
+    if (this.wizard.pageIndex != SYNC_KEY_PAGE)
+      return true;
+
+    this.wizard.pageIndex = ADD_DEVICE_PAGE;
+    return false;
+  },
+
+  onWizardCancel: function onWizardCancel() {
+    if (this._jpakeclient) {
+      this._jpakeclient.abort();
+      delete this._jpakeclient;
+    }
+    return true;
+  },
+
+  onTextBoxInput: function onTextBoxInput(textbox) {
+    if (textbox.value.length == PIN_PART_LENGTH)
+      this.nextFocusEl[textbox.id].focus();
+
+    this.wizard.canAdvance = (this.pin1.value.length == PIN_PART_LENGTH
+                              && this.pin2.value.length == PIN_PART_LENGTH
+                              && this.pin3.value.length == PIN_PART_LENGTH);
+  },
+
+  goToSyncKeyPage: function goToSyncKeyPage() {
+    this.wizard.pageIndex = SYNC_KEY_PAGE;
+  }
+
+};
+// onWizardAdvance() and onPageShow() are run before init() so we'll set
+// these up as lazy getters.
+["wizard", "pin1", "pin2", "pin3"].forEach(function (id) {
+  XPCOMUtils.defineLazyGetter(gSyncAddDevice, id, function() {
+    return document.getElementById(id);
+  });
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/syncAddDevice.xul
@@ -0,0 +1,161 @@
+<?xml version="1.0"?>
+
+# ***** 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 Firefox Sync.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Philipp von Weitershausen <philipp@weitershausen.de>
+#
+# 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 *****
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
+<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
+%brandDTD;
+%syncBrandDTD;
+%syncSetupDTD;
+]>
+<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        xmlns:html="http://www.w3.org/1999/xhtml"
+        id="wizard"
+        title="&addDevice.title.label;"
+        windowtype="Sync:AddDevice"
+        onwizardnext="return gSyncAddDevice.onWizardAdvance();"
+        onwizardback="return gSyncAddDevice.onWizardBack();"
+        onwizardcancel="gSyncAddDevice.onWizardCancel();"
+        onload="gSyncAddDevice.init();">
+
+  <script type="application/javascript"
+          src="chrome://browser/content/syncAddDevice.js"/>
+  <script type="application/javascript"
+          src="chrome://browser/content/syncUtils.js"/>
+  <script type="application/javascript"
+          src="chrome://browser/content/utilityOverlay.js"/>
+  <script type="application/javascript"
+          src="chrome://global/content/printUtils.js"/>
+
+  <wizardpage id="addDevicePage"
+              label="&addDevice.title.label;"
+              onpageshow="gSyncAddDevice.onPageShow();">
+    <description>
+      &addDevice.dialog.description.label;
+      <label class="text-link"
+             value="&addDevice.showMeHow.label;"
+             href="https://services.mozilla.com/sync/help/add-device"/>
+    </description>
+    <spacer flex="1"/>
+    <description>
+      &addDevice.dialog.enterCode.label;
+    </description>
+    <spacer flex="1"/>
+    <vbox align="center">
+      <textbox id="pin1"
+               class="pin"
+               oninput="gSyncAddDevice.onTextBoxInput(this);"
+               onfocus="this.select();"
+               />
+      <textbox id="pin2"
+               class="pin"
+               oninput="gSyncAddDevice.onTextBoxInput(this);"
+               onfocus="this.select();"
+               />
+      <textbox id="pin3"
+               class="pin"
+               oninput="gSyncAddDevice.onTextBoxInput(this);"
+               onfocus="this.select();" 
+              />
+    </vbox>
+    <spacer flex="1"/>
+    <vbox id="add-device-throbber" align="center" hidden="true">
+      <image/>
+    </vbox>
+    <hbox id="errorRow" pack="center" hidden="true">
+      <image class="statusIcon" status="error"/>
+      <label class="status"
+             value="&addDevice.dialog.tryAgain.label;"/>
+    </hbox>
+    <spacer flex="3"/>
+    <label class="text-link"
+           value="&addDevice.dontHaveDevice.label;"
+           onclick="gSyncAddDevice.goToSyncKeyPage();"/>
+  </wizardpage>
+
+  <wizardpage id="deviceConnectedPage"
+              label="&addDevice.dialog.connected.label;"
+              onpageshow="gSyncAddDevice.onPageShow();">
+    <vbox align="center">
+      <image id="successPageIcon"/>
+    </vbox>
+    <separator/>
+    <description class="normal">
+      &addDevice.dialog.successful.label;
+    </description>
+  </wizardpage>
+
+  <!-- Need a non-empty label here, otherwise we get a default label on Mac -->
+  <wizardpage id="syncKeyPage"
+              label=" "
+              onpageshow="gSyncAddDevice.onPageShow();">
+    <description>
+      &addDevice.dialog.syncKey.label;
+    </description>
+    <spacer/>
+
+    <groupbox>
+      <label value="&syncKeyEntry.label;"
+             accesskey="&syncKeyEntry.accesskey;"
+             control="weavePassphrase"/>
+      <textbox id="weavePassphrase"
+               disabled="true"/>
+    </groupbox>
+
+    <groupbox align="center">
+      <description>&syncKeyBackup.description;</description>
+      <hbox>
+        <button id="printSyncKeyButton"
+                label="&button.syncKeyBackup.print.label;"
+                accesskey="&button.syncKeyBackup.print.accesskey;"
+                oncommand="gSyncUtils.passphrasePrint('weavePassphrase');"/>
+        <button id="saveSyncKeyButton"
+                label="&button.syncKeyBackup.save.label;"
+                accesskey="&button.syncKeyBackup.save.accesskey;"
+                oncommand="gSyncUtils.passphraseSave('weavePassphrase');"/>
+      </hbox>
+    </groupbox>
+  </wizardpage>
+
+</wizard>
--- a/browser/base/content/syncGenericChange.js
+++ b/browser/base/content/syncGenericChange.js
@@ -237,21 +237,17 @@ let Change = {
         [valid, errorString] = gSyncUtils.validatePassword(this._firstBox);
       else
         [valid, errorString] = gSyncUtils.validatePassword(this._firstBox, this._secondBox);
     }
     else {
       if (!this._updatingPassphrase)
         return;
 
-      if (event.keyCode != event.DOM_VK_BACK_SPACE) {
-        this._passphraseBox.value = Weave.Utils.hyphenatePartialPassphrase(
-          this._passphraseBox.value);
-       }
-      valid = Weave.Utils.isPassphrase(this._passphraseBox.value);
+      valid = this._passphraseBox.value != "";
     }
 
     if (errorString == "")
       this._clearStatus();
     else
       this._updateStatusWithString(errorString, "error");
 
     this._statusRow.hidden = valid;
--- a/browser/base/content/syncSetup.js
+++ b/browser/base/content/syncSetup.js
@@ -44,18 +44,18 @@ const Cr = Components.results;
 const Cu = Components.utils;
 
 // page consts
 
 const INTRO_PAGE                    = 0;
 const NEW_ACCOUNT_START_PAGE        = 1;
 const NEW_ACCOUNT_PP_PAGE           = 2;
 const NEW_ACCOUNT_CAPTCHA_PAGE      = 3;
-const EXISTING_ACCOUNT_LOGIN_PAGE   = 4;
-const EXISTING_ACCOUNT_PP_PAGE      = 5;
+const EXISTING_ACCOUNT_CONNECT_PAGE = 4;
+const EXISTING_ACCOUNT_LOGIN_PAGE   = 5;
 const OPTIONS_PAGE                  = 6;
 const OPTIONS_CONFIRM_PAGE          = 7;
 const SETUP_SUCCESS_PAGE            = 8;
 
 Cu.import("resource://services-sync/main.js");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PluralForm.jsm");
@@ -73,19 +73,18 @@ var gSyncSetup = {
   status: {
     password: false,
     email: false,
     server: false
   },
 
   get _usingMainServers() {
     if (this._settingUpNew)
-      return document.getElementById("serverType").selectedItem.value == "main";
-
-    return document.getElementById("existingServerType").selectedItem.value == "main";
+      return document.getElementById("server").selectedIndex == 0;
+    return document.getElementById("existingServer").selectedIndex == 0;
   },
 
   init: function () {
     let obs = [
       ["weave:service:changepph:finish", "onResetPassphrase"],
       ["weave:service:verify-login:start",  "onLoginStart"],
       ["weave:service:verify-login:error",  "onLoginEnd"],
       ["weave:service:verify-login:finish", "onLoginEnd"]];
@@ -133,17 +132,17 @@ var gSyncSetup = {
 
   startNewAccountSetup: function () {
     this._settingUpNew = true;
     this.wizard.pageIndex = NEW_ACCOUNT_START_PAGE;
   },
 
   useExistingAccount: function () {
     this._settingUpNew = false;
-    this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
+    this.wizard.pageIndex = EXISTING_ACCOUNT_CONNECT_PAGE;
   },
 
   onResetPassphrase: function () {
     document.getElementById("existingPassphrase").value = 
       Weave.Utils.hyphenatePassphrase(Weave.Service.passphrase);
     this.wizard.advance();
   },
 
@@ -151,41 +150,40 @@ var gSyncSetup = {
     this.toggleLoginFeedback(false);
   },
 
   onLoginEnd: function () {
     this.toggleLoginFeedback(true);
   },
 
   toggleLoginFeedback: function (stop) {
-    switch (this.wizard.pageIndex) {
-      case EXISTING_ACCOUNT_LOGIN_PAGE:
-        document.getElementById("connect-throbber").hidden = stop;
-        let feedback = document.getElementById("existingPasswordFeedbackRow");
-        if (stop) {
-          let success = Weave.Status.login == Weave.LOGIN_SUCCEEDED ||
-                        Weave.Status.login == Weave.LOGIN_FAILED_INVALID_PASSPHRASE;
-          this._setFeedbackMessage(feedback, success, Weave.Status.login);
-        }
-        else
-          this._setFeedbackMessage(feedback, true);
+    document.getElementById("login-throbber").hidden = stop;
+    let password = document.getElementById("existingPasswordFeedbackRow");
+    let server = document.getElementById("existingServerFeedbackRow");
+    let passphrase = document.getElementById("existingPassphraseFeedbackRow");
+
+    if (!stop || (Weave.Status.login == Weave.LOGIN_SUCCEEDED)) {
+      password.hidden = server.hidden = passphrase.hidden = true;
+      return;
+    }
+
+    let feedback;
+    switch (Weave.Status.login) {
+      case Weave.LOGIN_FAILED_NETWORK_ERROR:
+      case Weave.LOGIN_FAILED_SERVER_ERROR:
+        feedback = server;
         break;
-      case EXISTING_ACCOUNT_PP_PAGE:
-        document.getElementById("passphrase-throbber").hidden = stop;
-        feedback = document.getElementById("existingPassphraseFeedbackBox");
-        if (stop) {
-          let success = Weave.Status.login == Weave.LOGIN_SUCCEEDED;
-          this._setFeedbackMessage(feedback, success, Weave.Status.login);
-          document.getElementById("passphraseHelpBox").hidden = success;
-        }
-        else
-          this._setFeedbackMessage(feedback, true);
-
+      case Weave.LOGIN_FAILED_LOGIN_REJECTED:
+        feedback = password;
+        break;
+      case Weave.LOGIN_FAILED_INVALID_PASSPHRASE:
+        feedback = passphrase;
         break;
     }
+    this._setFeedbackMessage(feedback, false, Weave.Status.login);
   },
 
   setupInitialSync: function () {
     let action = document.getElementById("mergeChoiceRadio").selectedItem.id;
     switch (action) {
       case "resetClient":
         // if we're not resetting sync, we don't need to explicitly
         // call resetClient
@@ -195,20 +193,16 @@ var gSyncSetup = {
       case "wipeClient":
       case "wipeRemote":
         Weave.Svc.Prefs.set("firstSync", action);
         break;
     }
   },
 
   onPassphraseKeyUp: function (event) {
-    if (event.keyCode != event.DOM_VK_BACK_SPACE) {
-      let el = event.target;
-      el.value = Weave.Utils.hyphenatePartialPassphrase(el.value);
-    }
     this.checkFields();
   },
 
   // fun with validation!
   checkFields: function () {
     this.wizard.canAdvance = this.readyToAdvance();
   },
 
@@ -223,29 +217,28 @@ var gSyncSetup = {
         }
         if (this._usingMainServers)
           return document.getElementById("tos").checked;
 
         return true;
       case EXISTING_ACCOUNT_LOGIN_PAGE:
         let hasUser = document.getElementById("existingAccountName").value != "";
         let hasPass = document.getElementById("existingPassword").value != "";
-        if (hasUser && hasPass) {
+        let hasKey = document.getElementById("existingPassphrase").value != "";
+
+        if (hasUser && hasPass && hasKey) {
           if (this._usingMainServers)
             return true;
 
-          if (this._validateServer(document.getElementById("existingServerURL"), false))
+          if (this._validateServer(document.getElementById("existingServer"), false))
             return true;
         }
         return false;
-      case EXISTING_ACCOUNT_PP_PAGE:
-        let el = document.getElementById("existingPassphrase");
-        return Weave.Utils.isPassphrase(el.value);
     }
-    // we probably shouldn't get here
+    // Default, e.g. wizard's special page -1 etc.
     return true;
   },
 
   onEmailInput: function () {
     // Check account validity when the user stops typing for 1 second.
     if (this._checkAccountTimer)
       window.clearTimeout(this._checkAccountTimer);
     this._checkAccountTimer = window.setTimeout(function () {
@@ -325,30 +318,36 @@ var gSyncSetup = {
         document.getElementById("saveSyncKeyButton").focus();
         let el = document.getElementById("weavePassphrase");
         if (!el.value)
           this.onPassphraseGenerate();
         this.checkFields();
         break;
       case NEW_ACCOUNT_START_PAGE:
         this.wizard.getButton("extra1").hidden = false;
-        this.onServerChange();
-        // fall through
-      case EXISTING_ACCOUNT_LOGIN_PAGE:
+        this.wizard.getButton("next").hidden = false;
+        this.wizard.getButton("back").hidden = false;
+        this.onServerCommand();
+        this.wizard.canRewind = true;
         this.checkFields();
+        break;
+      case EXISTING_ACCOUNT_CONNECT_PAGE:
         this.wizard.getButton("next").hidden = false;
         this.wizard.getButton("back").hidden = false;
         this.wizard.getButton("extra1").hidden = false;
         this.wizard.canRewind = true;
+        this.startEasySetup();
         break;
-      case EXISTING_ACCOUNT_PP_PAGE:
+      case EXISTING_ACCOUNT_LOGIN_PAGE:
+        this.wizard.canRewind = true;
         this.checkFields();
         break;
       case SETUP_SUCCESS_PAGE:
         this.wizard.canRewind = false;
+        this.wizard.canAdvance = true;
         this.wizard.getButton("back").hidden = true;
         this.wizard.getButton("next").hidden = true;
         this.wizard.getButton("cancel").hidden = true;
         this.wizard.getButton("finish").hidden = false;
         this._handleSuccess();
         break;
       case OPTIONS_PAGE:
         this.wizard.canRewind = false;
@@ -430,31 +429,16 @@ var gSyncSetup = {
         // Time to load the captcha.
         // First check for NoScript and whitelist the right sites.
         this._handleNoScript(true);
         this.captchaBrowser.loadURI(Weave.Service.miscAPI + "captcha_html");
         break;
       case EXISTING_ACCOUNT_LOGIN_PAGE:
         Weave.Service.account = document.getElementById("existingAccountName").value;
         Weave.Service.password = document.getElementById("existingPassword").value;
-        Weave.Service.passphrase = Weave.Utils.normalizePassphrase(
-            document.getElementById("existingPassphrase").value);
-
-        // verifyLogin() will likely return false because we probably don't
-        // have a passphrase yet (unless the user already entered it
-        // and hit the back button).
-        if (!Weave.Service.verifyLogin()
-            && Weave.Status.login != Weave.LOGIN_FAILED_NO_PASSPHRASE
-            && Weave.Status.login != Weave.LOGIN_FAILED_INVALID_PASSPHRASE) {
-          let feedback = document.getElementById("existingPasswordFeedbackRow");
-          this._setFeedbackMessage(feedback, false, Weave.Status.login);
-          return false;
-        }
-        break;
-      case EXISTING_ACCOUNT_PP_PAGE:
         let pp = document.getElementById("existingPassphrase").value;
         Weave.Service.passphrase = Weave.Utils.normalizePassphrase(pp);
         if (Weave.Service.login())
           this.wizard.pageIndex = SETUP_SUCCESS_PAGE;
         return false;
       case OPTIONS_PAGE:
         let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
         // No confirmation needed on new account setup or merge option
@@ -474,18 +458,19 @@ var gSyncSetup = {
   },
 
   onWizardBack: function () {
     switch (this.wizard.pageIndex) {
       case NEW_ACCOUNT_START_PAGE:
       case EXISTING_ACCOUNT_LOGIN_PAGE:
         this.wizard.pageIndex = INTRO_PAGE;
         return false;
-      case EXISTING_ACCOUNT_PP_PAGE: // no idea wtf is up here, but meh!
-        this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
+      case EXISTING_ACCOUNT_CONNECT_PAGE:
+        this.abortEasySetup();
+        this.wizard.pageIndex = INTRO_PAGE;
         return false;
       case OPTIONS_CONFIRM_PAGE:
         // Backing up from the confirmation page = resetting first sync to merge.
         document.getElementById("mergeChoiceRadio").selectedIndex = 0;
         return this.returnFromOptions();
     }
     return true;
   },
@@ -523,16 +508,17 @@ var gSyncSetup = {
   onWizardCancel: function () {
     if (this._resettingSync)
       return;
 
     if (this.wizard.pageIndex == SETUP_SUCCESS_PAGE) {
       this.onWizardFinish();
       return;
     }
+    this.abortEasySetup();
     this._handleNoScript(false);
     Weave.Service.startOver();
   },
 
   onSyncOptions: function () {
     this._beforeOptionsPage = this.wizard.pageIndex;
     this.wizard.pageIndex = OPTIONS_PAGE;
   },
@@ -545,16 +531,74 @@ var gSyncSetup = {
     this.wizard.getButton("back").setAttribute("accesskey",
                                                this._backButtonAccesskey);
     this.wizard.getButton("cancel").hidden = false;
     this.wizard.getButton("extra1").hidden = false;
     this.wizard.pageIndex = this._beforeOptionsPage;
     return false;
   },
 
+  startEasySetup: function () {
+    // Don't do anything if we have a client already (e.g. we went to
+    // Sync Options and just came back).
+    if (this._jpakeclient)
+      return;
+
+    let self = this;
+    this._jpakeclient = new Weave.JPAKEClient({
+      displayPIN: function displayPIN(pin) {
+        document.getElementById("easySetupPIN1").value = pin.slice(0, 4);
+        document.getElementById("easySetupPIN2").value = pin.slice(4, 8);
+        document.getElementById("easySetupPIN3").value = pin.slice(8);
+      },
+
+      onComplete: function onComplete(credentials) {
+        Weave.Service.account = credentials.account;
+        Weave.Service.password = credentials.password;
+        Weave.Service.passphrase = credentials.synckey;
+        Weave.Service.serverURL = credentials.serverURL;
+        self.wizard.pageIndex = SETUP_SUCCESS_PAGE;
+      },
+
+      onAbort: function onAbort(error) {
+        delete self._jpakeclient;
+
+        // No error means manual abort, e.g. wizard is aborted. Ignore.
+        if (!error)
+          return;
+
+        // Automatically go to manual setup if we couldn't acquire a channel.
+        if (error == Weave.JPAKE_ERROR_CHANNEL) {
+          self.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
+          return;
+        }
+
+        // Restart on all other errors.
+        self.startEasySetup();
+      }
+    });
+    this._jpakeclient.receiveNoPIN();
+  },
+
+  abortEasySetup: function () {
+    document.getElementById("easySetupPIN1").value = "";
+    document.getElementById("easySetupPIN2").value = "";
+    document.getElementById("easySetupPIN3").value = "";
+    if (!this._jpakeclient)
+      return;
+
+    this._jpakeclient.abort();
+    delete this._jpakeclient;
+  },
+
+  manualSetup: function () {
+    this.abortEasySetup();
+    this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
+  },
+
   // _handleNoScript is needed because it blocks the captcha. So we temporarily
   // allow the necessary sites so that we can verify the user is in fact a human.
   // This was done with the help of Giorgio (NoScript author). See bug 508112.
   _handleNoScript: function (addExceptions) {
     // if NoScript isn't installed, or is disabled, bail out.
     let ns = Cc["@maone.net/noscript-service;1"];
     if (ns == null)
       return;
@@ -572,33 +616,55 @@ var gSyncSetup = {
     else {
       this._disabledSites.forEach(function(site) {
         ns.setJSEnabled(site, false);
       });
       this._disabledSites = [];
     }
   },
 
-  onServerChange: function () {
-    if (this.wizard.pageIndex == EXISTING_ACCOUNT_LOGIN_PAGE) {
-      if (this._usingMainServers)
-        Weave.Svc.Prefs.reset("serverURL");
-      document.getElementById("existingServerRow").hidden = this._usingMainServers;
-      this.checkFields();
-      return;
+  onExistingServerCommand: function () {
+    let control = document.getElementById("existingServer");
+    if (control.selectedIndex == 0) {
+      control.removeAttribute("editable");
+      Weave.Svc.Prefs.reset("serverURL");
+    } else {
+      control.setAttribute("editable", "true");
+      // Force a style flush to ensure that the binding is attached.
+      control.clientTop;
+      control.value = "";
+      control.inputField.focus();
     }
+    document.getElementById("existingServerFeedbackRow").hidden = true;
+    this.checkFields();
+  },
 
-    document.getElementById("serverRow").hidden = this._usingMainServers;
+  onExistingServerInput: function () {
+    // Check custom server validity when the user stops typing for 1 second.
+    if (this._existingServerTimer)
+      window.clearTimeout(this._existingServerTimer);
+    this._existingServerTimer = window.setTimeout(function () {
+      gSyncSetup.checkFields();
+    }, 1000);
+  },
+
+  onServerCommand: function () {
     document.getElementById("TOSRow").hidden = !this._usingMainServers;
-
+    let control = document.getElementById("server");
     if (!this._usingMainServers) {
+      control.setAttribute("editable", "true");
+      // Force a style flush to ensure that the binding is attached.
+      control.clientTop;
+      control.value = "";
+      control.inputField.focus();
+      // checkServer() will call checkAccount() and checkFields().
       this.checkServer();
       return;
     }
-
+    control.removeAttribute("editable");
     Weave.Svc.Prefs.reset("serverURL");
     this.checkAccount();
     this.status.server = true;
     document.getElementById("serverFeedbackRow").hidden = true;
     this.checkFields();
   },
 
   onServerInput: function () {
@@ -607,17 +673,17 @@ var gSyncSetup = {
       window.clearTimeout(this._checkServerTimer);
     this._checkServerTimer = window.setTimeout(function () {
       gSyncSetup.checkServer();
     }, 1000);
   },
 
   checkServer: function () {
     delete this._checkServerTimer;
-    let el = document.getElementById("weaveServerURL");
+    let el = document.getElementById("server");
     let valid = false;
     let feedback = document.getElementById("serverFeedbackRow");
     let str = "";
     if (el.value) {
       valid = this._validateServer(el, true);
       let str = valid ? "" : "serverInvalid.label";
       this._setFeedbackMessage(feedback, valid, str);
     }
@@ -804,19 +870,19 @@ var gSyncSetup = {
     return true;
   },
 
   // sets class and string on a feedback element
   // if no property string is passed in, we clear label/style
   _setFeedback: function (element, success, string) {
     element.hidden = success || !string;
     let class = success ? "success" : "error";
-    let image = element.firstChild.nextSibling.firstChild;
+    let image = element.getElementsByAttribute("class", "statusIcon")[0];
     image.setAttribute("status", class);
-    let label = image.nextSibling;
+    let label = element.getElementsByAttribute("class", "status")[0];
     label.value = string;
   },
 
   // shim
   _setFeedbackMessage: function (element, success, string) {
     let str = "";
     if (string) {
       try {
--- a/browser/base/content/syncSetup.xul
+++ b/browser/base/content/syncSetup.xul
@@ -84,19 +84,21 @@
               label="&button.createNewAccount.label;"
               oncommand="gSyncSetup.startNewAccountSetup()"
               align="center"/>
       <spacer flex="3"/>
     </vbox>
     <separator class="groove"/>
     <vbox align="center" flex="1">
       <spacer flex="3"/>
+      <label value="&setup.haveAccount.label;" />
+      <spacer flex="1"/>
       <button id="existingAccount"
               class="accountChoiceButton"
-              label="&button.haveAccount.label;"
+              label="&button.connect.label;"
               oncommand="gSyncSetup.useExistingAccount()"/>
       <spacer flex="3"/>
     </vbox>
   </wizardpage>
 
   <wizardpage label="&setup.newAccountDetailsPage.title.label;"
               id="newAccountStart"
               onextra1="gSyncSetup.onSyncOptions()"
@@ -140,33 +142,29 @@
         <row id="passwordFeedbackRow" align="center" hidden="true">
           <spacer/>
           <hbox>
             <image class="statusIcon"/>
             <label class="status" value=" "/>
           </hbox>
         </row>
         <row align="center">
-          <label control="serverType"
+          <label control="server"
                  value="&server.label;"/>
-          <menulist id="serverType" oncommand="gSyncSetup.onServerChange()">
+          <menulist id="server"
+                    oncommand="gSyncSetup.onServerCommand()"
+                    oninput="gSyncSetup.onServerInput()">
             <menupopup>
               <menuitem label="&serverType.main.label;"
                         value="main"/>
-              <menuitem label="&serverType.custom.label;"
+              <menuitem label="&serverType.custom2.label;"
                         value="custom"/>
             </menupopup>
           </menulist>
         </row>
-        <row id="serverRow" hidden="true" align="center">
-          <label value="&signIn.serverURL.label;"
-                 accesskey="&signIn.serverURL.accesskey;"
-                 control="weaveServerURL"/>
-          <textbox id="weaveServerURL" oninput="gSyncSetup.onServerInput()"/>
-        </row>
         <row id="serverFeedbackRow" align="center" hidden="true">
           <spacer/>
           <hbox>
             <image class="statusIcon"/>
             <label class="status" value=" "/>
           </hbox>
         </row>
         <row id="TOSRow" align="center">
@@ -238,114 +236,150 @@
       <hbox id="captchaFeedback" hidden="true">
         <image class="statusIcon"/>
         <label class="status" value=" "/>
       </hbox>
       <spacer flex="3"/>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="useExisting"
-              label="&setup.existingAccount.title.label;"
+  <wizardpage id="addDevice"
+              label="&addDevice.title.label;"
+              onextra1="gSyncSetup.onSyncOptions()"
+              onpageshow="gSyncSetup.onPageShow()">
+    <description>
+      &addDevice.setup.description.label;
+      <label class="text-link"
+             value="&addDevice.showMeHow.label;"
+             href="https://services.mozilla.com/sync/help/easy-setup"/>
+    </description>
+    <description>&addDevice.setup.enterCode.label;</description>
+    <spacer flex="1"/>
+    <vbox align="center" flex="1">
+      <textbox id="easySetupPIN1"
+               class="pin"
+               value=""
+               disabled="true"
+               />
+      <textbox id="easySetupPIN2"
+               class="pin"
+               value=""
+               disabled="true"
+               />
+      <textbox id="easySetupPIN3"
+               class="pin"
+               value=""
+               disabled="true"
+               />
+    </vbox>
+    <spacer flex="3"/>
+    <label class="text-link"
+           value="&addDevice.dontHaveDevice.label;"
+           onclick="gSyncSetup.manualSetup();"/>
+  </wizardpage>
+
+  <wizardpage id="existingAccount"
+              label="&setup.signInPage.title.label;"
               onextra1="gSyncSetup.onSyncOptions()"
               onpageshow="gSyncSetup.onPageShow()">
       <grid>
         <columns>
           <column/>
           <column class="inputColumn" flex="1"/>
         </columns>
         <rows>
-          <row align="center">
-            <label control="existingServerType"
-                   value="&server.label;"/>
-            <menulist id="existingServerType" oncommand="gSyncSetup.onServerChange()">
-              <menupopup>
-                <menuitem label="&serverType.main.label;"
-                          value="main"/>
-                <menuitem label="&serverType.custom.label;"
-                          value="custom"/>
-              </menupopup>
-            </menulist>
-          </row>
-          <row id="existingServerRow" hidden="true" align="center">
-            <label id="existingServerURLLabel"
-                   value="&signIn.serverURL.label;"
-                   accesskey="&signIn.serverURL.accesskey;"
-                   control="existingServerURL"/>
-            <textbox id="existingServerURL"
-                     onchange="gSyncSetup.checkFields(event)"/>
-          </row>
           <row id="existingAccountRow" align="center">
             <label id="existingAccountLabel"
-                   value="&signIn.account.label;"
-                   accesskey="&signIn.account.accesskey;"
+                   value="&signIn.account2.label;"
+                   accesskey="&signIn.account2.accesskey;"
                    control="existingAccount"/>
             <textbox id="existingAccountName"
                      oninput="gSyncSetup.checkFields(event)"
                      onchange="gSyncSetup.checkFields(event)"/>
           </row>
-          <row id="existingAccountFeedbackRow" align="center" hidden="true">
-            <spacer/>
-            <hbox>
-              <image class="statusIcon"/>
-              <label class="status" value=" "/>
-            </hbox>
-          </row>
           <row id="existingPasswordRow" align="center">
             <label id="existingPasswordLabel"
                    value="&signIn.password.label;"
                    accesskey="&signIn.password.accesskey;"
                    control="existingPassword"/>
             <textbox id="existingPassword"
                      type="password"
                      onkeyup="gSyncSetup.checkFields(event)"
                      onchange="gSyncSetup.checkFields(event)"/>
           </row>
-          <row id="existingPasswordFeedbackRow"  align="center" hidden="true">
-            <label class="text-link small" value="&resetPassword.label;"
-                   onclick="gSyncUtils.resetPassword(); return false;"/>
+          <row id="existingPasswordFeedbackRow" align="center" hidden="true">
+            <spacer/>
             <hbox>
               <image class="statusIcon"/>
-              <label class="status" value=" "/>
+              <vbox>
+                <label class="status" value=" "/>
+                <label class="text-link"
+                       value="&resetPassword.label;"
+                       onclick="gSyncUtils.resetPassword(); return false;"/>
+              </vbox>
             </hbox>
           </row>
-          <row id="existingLoginFeedbackRow">
+          <row align="center">
+            <label control="existingServer"
+                   value="&server.label;"/>
+            <menulist id="existingServer"
+                      oncommand="gSyncSetup.onExistingServerCommand()"
+                      oninput="gSyncSetup.onExistingServerInput()">
+              <menupopup>
+                <menuitem label="&serverType.main.label;"
+                          value="main"/>
+                <menuitem label="&serverType.custom2.label;"
+                          value="custom"/>
+              </menupopup>
+            </menulist>
+          </row>
+          <row id="existingServerFeedbackRow" align="center" hidden="true">
             <spacer/>
-            <hbox id="connect-throbber" hidden="true">
-              <image/>
-              <label value="&connecting.label;"/>
-          </hbox>
+            <hbox>
+              <image class="statusIcon"/>
+              <vbox>
+                <label class="status" value=" "/>
+              </vbox>
+            </hbox>
           </row>
         </rows>
       </grid>
-  </wizardpage>
 
-  <wizardpage id="existingPassphraseEntry"
-              label="&setup.existingSyncKeyPage.title;"
-              onextra1="gSyncSetup.onSyncOptions()"
-              onpageshow="gSyncSetup.onPageShow()">
-    <description>&setup.existingSyncKeyPage.description;</description>
-    <textbox id="existingPassphrase"
-             onkeyup="gSyncSetup.onPassphraseKeyUp(event)"
-             onchange="gSyncSetup.checkFields()"/>
-    <hbox id="passphrase-throbber" hidden="true">
-      <image/>
-      <label value="&verifying.label;"/>
-    </hbox>
-    <hbox align="left" id="existingPassphraseFeedbackBox">
-      <spacer/>
-      <hbox>
-        <image class="statusIcon"/>
-        <label class="status" value=" "/>
+    <groupbox>
+      <label id="existingPassphraseLabel"
+             value="&signIn.syncKey.label;"
+             accesskey="&signIn.syncKey.accesskey;"
+             control="existingPassphrase"/>
+      <textbox id="existingPassphrase"
+               onkeyup="gSyncSetup.onPassphraseKeyUp(event)"
+               onchange="gSyncSetup.checkFields()"/>
+      <hbox id="login-throbber" hidden="true">
+        <image/>
+        <label value="&verifying.label;"/>
       </hbox>
-    </hbox>
-    <vbox class="small" id="passphraseHelpBox" hidden="true">
-      <description class="small">&existingSyncKeyHelp.description;</description>
-      <label class="text-link small" value="&lostSyncKey.label;"
-             onclick="gSyncUtils.resetPassphrase(); return false;"/>
+      <vbox align="left" id="existingPassphraseFeedbackRow" hidden="true">
+        <hbox>
+          <image class="statusIcon"/>
+          <vbox>
+            <label class="status" value=" "/>
+            <label class="text-link"
+                   value="&lostSyncKey.label;"
+                   onclick="gSyncUtils.resetPassphrase(); return false;"/>
+          </vbox>
+        </hbox>
+      </vbox>
+    </groupbox>
+
+    <vbox id="passphraseHelpBox">
+      <description>
+        &existingSyncKey.description;
+        <label class="text-link"
+               value="&addDevice.showMeHow.label;"
+               href="https://services.mozilla.com/sync/help/manual-setup"/>
+      </description>
     </vbox>
   </wizardpage>
 
   <wizardpage id="syncOptionsPage"
               label="&setup.optionsPage.title;"
               onpageshow="gSyncSetup.onPageShow()">
     <groupbox id="syncOptions">
     <grid>
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -159,16 +159,17 @@ endif
                  browser_bug592338.js \
                  browser_bug594131.js \
                  browser_bug595507.js \
                  browser_bug596687.js \
                  browser_bug597218.js \
                  browser_bug598923.js \
                  browser_bug599325.js \
                  browser_bug609700.js \
+                 browser_bug616836.js \
                  browser_contextSearchTabPosition.js \
                  browser_ctrlTab.js \
                  browser_disablechrome.js \
                  browser_discovery.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
                  browser_hide_removing.js \
@@ -226,16 +227,17 @@ endif
                  browser_tabMatchesInAwesomebar.js \
                  file_bug550565_popup.html \
                  file_bug550565_favicon.ico \
                  browser_overLinkInLocationBar.js \
                  browser_aboutHome.js \
                  app_bug575561.html \
                  app_subframe_bug575561.html \
                  browser_contentAreaClick.js \
+                 browser_addon_bar_close_button.js \
                  $(NULL)
 
 # compartment-disabled
 #                 browser_popupUI.js \
 #                 browser_tab_dragdrop.js \
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _BROWSER_FILES += \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_addon_bar_close_button.js
@@ -0,0 +1,51 @@
+/* ***** 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 browser add-on bar test 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):
+ * Dietrich Ayala <dietrich@mozilla.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 ***** */
+
+function test() {
+  let addonbar = document.getElementById("addon-bar");
+  ok(addonbar.collapsed, "addon bar is collapsed by default");
+
+  // make add-on bar visible
+  setToolbarVisibility(addonbar, true);
+  ok(!addonbar.collapsed, "addon bar is not collapsed after toggle");
+
+  // click the close button
+  let closeButton = document.getElementById("addonbar-closebutton");
+  EventUtils.synthesizeMouseAtCenter(closeButton, {});
+
+  // confirm addon bar is closed
+  ok(addonbar.collapsed, "addon bar is collapsed after clicking close button");
+}
--- a/browser/base/content/test/browser_bug598923.js
+++ b/browser/base/content/test/browser_bug598923.js
@@ -6,27 +6,28 @@
 // * if add-on is installed to the add-on bar, the bar is made visible.
 // * if add-on is uninstalled from the add-on bar, and no more add-ons there,
 //   the bar is hidden.
 
 function test() {
   let aml = AddonsMgrListener;
   ok(aml, "AddonsMgrListener exists");
   // check is hidden
-  is(aml.addonBar.collapsed, true, "aob is hidden");
+  is(aml.addonBar.collapsed, true, "add-on bar is hidden initially");
   // aob gets the count
   AddonsMgrListener.onInstalling();
   // add an item
   let element = document.createElement("toolbaritem");
+  element.id = "bug598923-addon-item";
   aml.addonBar.appendChild(element);
   // aob checks the count, makes visible
   AddonsMgrListener.onInstalled();
   // check is visible
-  is(aml.addonBar.collapsed, false, "aob is visible");
+  is(aml.addonBar.collapsed, false, "add-on bar has been made visible");
   // aob gets the count
   AddonsMgrListener.onUninstalling();
   // remove an item
   aml.addonBar.removeChild(element);
   // aob checks the count, makes hidden
   AddonsMgrListener.onUninstalled();
   // check is hidden
-  is(aml.addonBar.collapsed, true, "aob is hidden");
+  is(aml.addonBar.collapsed, true, "add-on bar is hidden again");
 }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug616836.js
@@ -0,0 +1,4 @@
+function test() {
+  is(document.querySelectorAll("#appmenu-popup [accesskey]").length, 0,
+     "there should be no items with access keys in the app menu popup");
+}
--- a/browser/base/content/test/tabview/browser_tabview_bug597248.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug597248.js
@@ -60,76 +60,85 @@ function setupOne() {
     if (++loadedCount == 2) {
       newWin.addEventListener("tabviewshown", setupTwo, false);
       newWin.TabView.toggle();
     }
   }
   
   newTabOne = newWin.gBrowser.tabs[0];
   newTabTwo = newWin.gBrowser.addTab();
-  load(newTabOne, "http://mochi.test:8888/", allLoaded);
+  load(newTabOne, "http://mochi.test:8888/browser/browser/base/content/test/tabview/search1.html", allLoaded);
   load(newTabTwo, "http://mochi.test:8888/browser/browser/base/content/test/tabview/dummy_page.html", allLoaded);
 }
 
 function setupTwo() {
   newWin.removeEventListener("tabviewshown", setupTwo, false);
 
   let contentWindow = newWin.document.getElementById("tab-view").contentWindow;
 
   let tabItems = contentWindow.TabItems.getItems();
   is(tabItems.length, 2, "There should be 2 tab items before closing");
 
   // force all canvas to update
   tabItems.forEach(function(tabItem) {
     contentWindow.TabItems._update(tabItem.tab);
   });
 
-  let checkDataAndCloseWindow = function() {
-    // check the storage for stored image data.
-    tabItems.forEach(function(tabItem) {
-      let tabData = contentWindow.Storage.getTabData(tabItem.tab);
-      ok(tabData && tabData.imageData, "TabItem has stored image data before closing");
-    });
+  // after the window is closed, restore it.
+  let xulWindowDestory = function() {
+    Services.obs.removeObserver(
+       xulWindowDestory, "xul-window-destroyed", false);
 
-    // close the new window and restore it.
-    newWin.addEventListener("unload", function(event) {
-      newWin.removeEventListener("unload", arguments.callee, false);
-      newWin = null;
-
-      // restore window and test it
+    newWin = null;
+    // "xul-window-destroyed" is just fired just before a XUL window is
+    // destroyed so restore window and test it after a delay
+    executeSoon(function() {
       restoredWin = undoCloseWindow();
       restoredWin.addEventListener("load", function(event) {
         restoredWin.removeEventListener("load", arguments.callee, false);
 
+        // execute code when the frame isninitialized.
+        restoredWin.addEventListener("tabviewshown", onTabViewShown, false);
+
         // setup tab variables and listen to the load progress.
         newTabOne = restoredWin.gBrowser.tabs[0];
         newTabTwo = restoredWin.gBrowser.tabs[1];
         restoredWin.gBrowser.addTabsProgressListener(gTabsProgressListener);
+      }, false);
+    });
+  };
 
-        // execute code when the frame isninitialized.
-        restoredWin.addEventListener("tabviewframeinitialized", onTabViewFrameInitialized, false);
-      }, false);
-    }, false);
+  // check the storage for stored image data
+  let checkDataAndCloseWindow = function() {
+    tabItems.forEach(function(tabItem) {
+      let tabData = contentWindow.Storage.getTabData(tabItem.tab);
+      ok(tabData && tabData.imageData,
+        "TabItem has stored image data before closing");
+    });
 
+    Services.obs.addObserver(
+      xulWindowDestory, "xul-window-destroyed", false);
     newWin.close();
   }
 
-  // stimulate a quit application requested so the image data gets stored.
+  // stimulate a quit application requested so the image data gets stored
   let quitRequestObserver = function(aSubject, aTopic, aData) {
     ok(aTopic == "quit-application-requested" &&
         aSubject instanceof Ci.nsISupportsPRBool,
         "Received a quit request and going to deny it");
-    Services.obs.removeObserver(quitRequestObserver, "quit-application-requested", false);
-
+    Services.obs.removeObserver(
+      quitRequestObserver, "quit-application-requested", false);
+    // cancel the shut down
     aSubject.data = true;
     // save all images is execuated when "quit-application-requested" topic is 
     // announced so executeSoon is used to avoid racing condition.
     executeSoon(checkDataAndCloseWindow);
   }
-  Services.obs.addObserver(quitRequestObserver, "quit-application-requested", false);
+  Services.obs.addObserver(
+    quitRequestObserver, "quit-application-requested", false);
   ok(!Application.quit(), "Tried to quit and canceled it");
 }
 
 let gTabsProgressListener = {
   onStateChange: function(browser, webProgress, request, stateFlags, status) {
     if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
          stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
       if (newTabOne.linkedBrowser == browser)
@@ -147,61 +156,71 @@ let gTabsProgressListener = {
           executeSoon(updateAndCheck); 
         }
         restoredWin.gBrowser.removeTabsProgressListener(gTabsProgressListener);
       }
     }
   }
 };
 
-function onTabViewFrameInitialized() {
-  restoredWin.removeEventListener("tabviewframeinitialized", onTabViewFrameInitialized, false);
+function onTabViewShown() {
+  restoredWin.removeEventListener("tabviewshown", onTabViewShown, false);
 
-  let contentWindow = restoredWin.document.getElementById("tab-view").contentWindow;
+  let contentWindow = 
+    restoredWin.document.getElementById("tab-view").contentWindow;
 
   let nextStep = function() {
     // since we are not sure whether the frame is initialized first or two tabs
     // compete loading first so we need this.
     if (restoredNewTabOneLoaded && restoredNewTabTwoLoaded) {
       // executeSoon is used to ensure tabItem.shouldHideCachedData is set
-      // because tabs progress listener might run at the same time as this test code.
+      // because tabs progress listener might run at the same time as this test 
+      // code.
       executeSoon(updateAndCheck);
     } else
       frameInitialized = true;
   }
 
   let tabItems = contentWindow.TabItems.getItems();
   let count = tabItems.length;
   tabItems.forEach(function(tabItem) {
     // tabitem might not be connected so use subscriber for those which are not
     // connected.
     if (tabItem.reconnected) {
-      ok(tabItem.isShowingCachedData(), "Tab item is showing cached data");
+      ok(tabItem.isShowingCachedData(), 
+         "Tab item is showing cached data and is already connected. " +
+         tabItem.tab.linkedBrowser.currentURI.spec);
       count--;
       if (count == 0)
         nextStep();
     } else {
       tabItem.addSubscriber(tabItem, "reconnected", function() {
         tabItem.removeSubscriber(tabItem, "reconnected");
+        ok(tabItem.isShowingCachedData(), 
+           "Tab item is showing cached data and is just connected. "  +
+           tabItem.tab.linkedBrowser.currentURI.spec);
         count--;
         if (count == 0)
           nextStep();
       });
     }
   });
 }
 
 function updateAndCheck() {
   // force all canvas to update
-  let contentWindow = restoredWin.document.getElementById("tab-view").contentWindow;
+  let contentWindow = 
+    restoredWin.document.getElementById("tab-view").contentWindow;
 
   let tabItems = contentWindow.TabItems.getItems();
   tabItems.forEach(function(tabItem) {
     contentWindow.TabItems._update(tabItem.tab);
-    ok(!tabItem.isShowingCachedData(), "Tab item is not showing cached data anymore");
+    ok(!tabItem.isShowingCachedData(), 
+      "Tab item is not showing cached data anymore. " +
+      tabItem.tab.linkedBrowser.currentURI.spec);
   });
 
   // clean up and finish
   restoredWin.close();
   finish();
 }
 
 function load(tab, url, callback) {
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -230,17 +230,17 @@
             return; // Do nothing for right clicks
 
           var url = this.value;
           var postData = null;
 
           var action = this._parseActionUrl(url);
           if (action) {
             url = action.param;
-            if (!(aTriggeringEvent && aTriggeringEvent.altKey)) {
+            if (this.hasAttribute("actiontype")) {
               if (action.type == "switchtab") {
                 this.handleRevert();
                 let prevTab = gBrowser.selectedTab;
                 if (switchToTabHavingURI(url) &&
                     isTabEmpty(prevTab))
                   gBrowser.removeTab(prevTab);
               }
               return;
@@ -272,16 +272,19 @@
             }
             openUILinkIn(url, where,
                         { allowThirdPartyFixup: true, postData: postData });
             return;
           }
 
           if (aTriggeringEvent && aTriggeringEvent.altKey) {
             this.handleRevert();
+            let prevTab = gBrowser.selectedTab;
+            if (isTabEmpty(prevTab))
+              gBrowser.removeTab(prevTab);
             content.focus();
             gBrowser.loadOneTab(url, {
                                 postData: postData,
                                 inBackground: false,
                                 allowThirdPartyFixup: true});
             aTriggeringEvent.preventDefault();
             aTriggeringEvent.stopPropagation();
           }
@@ -774,19 +777,58 @@
             overLinkPath.crop = host ? "start" : "end";
             overLink.style.minWidth = maxWidth + "px";
             overLink.style.maxWidth = maxWidth + "px";
           }
 
           this._originLabel.value = this.value;
         ]]></body>
       </method>
+
+      <field name="_numNoActionsKeys"><![CDATA[
+        0
+      ]]></field>
+
+      <method name="_clearNoActions">
+        <parameter name="aURL"/>
+        <body><![CDATA[
+          this._numNoActionsKeys = 0;
+          this.popup.removeAttribute("noactions");
+          let action = this._parseActionUrl(this._value);
+          if (action)
+            this.setAttribute("actiontype", action.type);
+        ]]></body>
+      </method>
     </implementation>
 
     <handlers>
+      <handler event="keydown"><![CDATA[
+        if ((event.keyCode === KeyEvent.DOM_VK_ALT ||
+             event.keyCode === KeyEvent.DOM_VK_SHIFT) &&
+            this.popup.selectedIndex >= 0) {
+          this._numNoActionsKeys++;
+          this.popup.setAttribute("noactions", "true");
+          this.removeAttribute("actiontype");
+        }
+      ]]></handler>
+
+      <handler event="keyup"><![CDATA[
+        if ((event.keyCode === KeyEvent.DOM_VK_ALT ||
+             event.keyCode === KeyEvent.DOM_VK_SHIFT) &&
+            this._numNoActionsKeys > 0) {
+          this._numNoActionsKeys--;
+          if (this._numNoActionsKeys == 0)
+            this._clearNoActions();
+        }
+      ]]></handler>
+
+      <handler event="blur"><![CDATA[
+        this._clearNoActions();
+      ]]></handler>
+
       <handler event="draggesture" phase="capturing"><![CDATA[
         // TODO: This should use dragstart but editor code is still using
         //       the old drag & drop APIs, so we have to handle draggesture.
         //       This can be changed once editor code is updated to the new API.
         //       See bug 499008 for details.
 
         // Drag only if the gesture starts from the input field.
         if (event.originalTarget != this.inputField)
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -56,16 +56,18 @@ browser.jar:
 *       content/browser/baseMenuOverlay.xul           (content/baseMenuOverlay.xul)
 *       content/browser/nsContextMenu.js              (content/nsContextMenu.js)
 #ifdef MOZ_SERVICES_SYNC
 *       content/browser/aboutSyncTabs.xul             (content/aboutSyncTabs.xul)
         content/browser/aboutSyncTabs.js              (content/aboutSyncTabs.js)
         content/browser/aboutSyncTabs.css             (content/aboutSyncTabs.css)
 *       content/browser/aboutSyncTabs-bindings.xml    (content/aboutSyncTabs-bindings.xml)
 *       content/browser/syncSetup.xul                 (content/syncSetup.xul)
+        content/browser/syncAddDevice.js              (content/syncAddDevice.js)
+*       content/browser/syncAddDevice.xul             (content/syncAddDevice.xul)
         content/browser/syncSetup.js                  (content/syncSetup.js)
 *       content/browser/syncGenericChange.xul         (content/syncGenericChange.xul)
         content/browser/syncGenericChange.js          (content/syncGenericChange.js)
 *       content/browser/syncKey.xhtml                 (content/syncKey.xhtml)
 *       content/browser/syncNotification.xml          (content/syncNotification.xml)
 *       content/browser/syncQuota.xul                 (content/syncQuota.xul)
         content/browser/syncQuota.js                  (content/syncQuota.js)
         content/browser/syncUtils.js                  (content/syncUtils.js)
--- a/browser/build.mk
+++ b/browser/build.mk
@@ -45,17 +45,16 @@ ifdef MOZ_EXTENSIONS
 tier_app_dirs += extensions
 endif
 
 tier_app_dirs += $(MOZ_BRANDING_DIRECTORY)
 
 tier_app_dirs += toolkit/components/console/hudservice
 
 ifdef MOZ_SERVICES_SYNC
-tier_app_dirs += services/crypto
 tier_app_dirs += services/sync
 endif
 
 tier_app_dirs += browser
 # Never add other tier_app_dirs after browser. They won't get packaged
 # properly on mac.
 
 installer:
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -992,17 +992,17 @@ BrowserGlue.prototype = {
     var notifyBox = browser.getNotificationBox();
     var box = notifyBox.appendNotification(text, title, null,
                                            notifyBox.PRIORITY_CRITICAL_MEDIUM,
                                            buttons);
     box.persistence = -1; // Until user closes it
   },
 
   _migrateUI: function BG__migrateUI() {
-    const UI_VERSION = 3;
+    const UI_VERSION = 4;
     let currentUIVersion = 0;
     try {
       currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
     } catch(ex) {}
     if (currentUIVersion >= UI_VERSION)
       return;
 
     this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
@@ -1061,24 +1061,40 @@ BrowserGlue.prototype = {
       let toolbarResource = this._rdf.GetResource("chrome://browser/content/browser.xul#nav-bar");
       let currentset = this._getPersist(toolbarResource, currentsetResource);
       // Need to migrate only if toolbar is customized and all 3 elements are found.
       if (currentset &&
           currentset.indexOf("reload-button") != -1 &&
           currentset.indexOf("stop-button") != -1 &&
           currentset.indexOf("urlbar-container") != -1 &&
           currentset.indexOf("urlbar-container,reload-button,stop-button") == -1) {
-        currentset = currentset.replace(/(^|,)reload-button($|,)/, "$1$2").
-                                replace(/(^|,)stop-button($|,)/, "$1$2").
-                                replace(/(^|,)urlbar-container($|,)/,
+        currentset = currentset.replace(/(^|,)reload-button($|,)/, "$1$2")
+                               .replace(/(^|,)stop-button($|,)/, "$1$2")
+                               .replace(/(^|,)urlbar-container($|,)/,
                                         "$1urlbar-container,reload-button,stop-button$2");
         this._setPersist(toolbarResource, currentsetResource, currentset);
       }
     }
 
+    if (currentUIVersion < 4) {
+      // This code moves the home button to the immediate left of the bookmarks menu button.
+      let currentsetResource = this._rdf.GetResource("currentset");
+      let toolbarResource = this._rdf.GetResource("chrome://browser/content/browser.xul#nav-bar");
+      let currentset = this._getPersist(toolbarResource, currentsetResource);
+      // Need to migrate only if toolbar is customized and the elements are found.
+      if (currentset &&
+          currentset.indexOf("home-button") != -1 &&
+          currentset.indexOf("bookmarks-menu-button-container") != -1) {
+        currentset = currentset.replace(/(^|,)home-button($|,)/, "$1$2")
+                               .replace(/(^|,)bookmarks-menu-button-container($|,)/,
+                                        "$1home-button,bookmarks-menu-button-container$2");
+        this._setPersist(toolbarResource, currentsetResource, currentset);
+      }
+    }
+
     if (this._dirty)
       this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
 
     delete this._rdf;
     delete this._dataSource;
 
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
--- a/browser/components/places/tests/unit/test_browserGlue_distribution.js
+++ b/browser/components/places/tests/unit/test_browserGlue_distribution.js
@@ -74,17 +74,17 @@ function run_test() {
   testDistributionFile.copyTo(distroDir, "distribution.ini");
   do_check_true(testDistributionFile.exists());
 
   // Disable Smart Bookmarks creation.
   let ps = Cc["@mozilla.org/preferences-service;1"].
            getService(Ci.nsIPrefBranch);
   ps.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
   // Avoid migrateUI, we are just simulating a partial startup.
-  ps.setIntPref("browser.migration.version", 1);
+  ps.setIntPref("browser.migration.version", 4);
 
   // Initialize Places through the History Service.
   let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
            getService(Ci.nsINavHistoryService);
   // Check a new database has been created.
   // nsBrowserGlue will use databaseStatus to manage initialization.
   do_check_eq(hs.databaseStatus, hs.DATABASE_STATUS_CREATE);
 
--- a/browser/components/preferences/sync.js
+++ b/browser/components/preferences/sync.js
@@ -210,13 +210,22 @@ let gSyncPane = {
     let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
     if (win)
       win.focus();
     else 
       window.openDialog("chrome://browser/content/syncQuota.xul", "",
                         "centerscreen,chrome,dialog,modal");
   },
 
+  openAddDevice: function () {
+    let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
+    if (win)
+      win.focus();
+    else 
+      window.openDialog("chrome://browser/content/syncAddDevice.xul",
+                        "syncAddDevice", "centerscreen,chrome,resizable=no");
+  },
+
   resetSync: function () {
     this.openSetup(true);
   }
 }
 
--- a/browser/components/preferences/sync.xul
+++ b/browser/components/preferences/sync.xul
@@ -142,16 +142,19 @@
                           label="&manageAccount.label;"
                           accesskey="&manageAccount.accesskey;"
                           align="left"
                           oncommand="gSyncPane.handleExpanderClick()"/>
                   <spacer/>
                 </row>
               </rows>
             </grid>
+            <label class="text-link"
+                   onclick="gSyncPane.openAddDevice(); return false;"
+                   value="&addDevice.label;"/>
           </groupbox>
           <groupbox>
             <caption label="&syncPrefsCaption.label;"/>
             <grid>
               <columns>
                 <column/>
                 <column flex="1"/>
               </columns>
--- a/browser/locales/en-US/chrome/browser/baseMenuOverlay.dtd
+++ b/browser/locales/en-US/chrome/browser/baseMenuOverlay.dtd
@@ -24,18 +24,16 @@
 <!ENTITY helpReleaseNotes.accesskey     "N">
 
 <!ENTITY helpTroubleshootingInfo.label      "Troubleshooting Information">
 <!ENTITY helpTroubleshootingInfo.accesskey  "T">
 
 <!ENTITY helpFeedbackPage.label      "Submit Feedback…">
 <!ENTITY helpFeedbackPage.accesskey  "S">
 
-<!ENTITY updateCmd.label                "Check for Updates…">
-
 <!ENTITY preferencesCmdMac.label        "Preferences…">
 <!ENTITY preferencesCmdMac.commandkey   ",">
 
 <!ENTITY servicesMenuMac.label          "Services">
 
 <!ENTITY hideThisAppCmdMac.label        "Hide &brandShortName;">
 <!ENTITY hideThisAppCmdMac.commandkey   "H">
 
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -543,8 +543,10 @@ just addresses the organization to follo
 
 <!ENTITY syncBrand.shortName.label    "Sync">
 
 <!ENTITY syncSetup.label              "Set Up &syncBrand.shortName.label;…">
 <!ENTITY syncSetup.accesskey          "Y">
 <!ENTITY syncSyncNowItem.label        "Sync Now">
 <!ENTITY syncSyncNowItem.accesskey    "S">
 <!ENTITY syncToolbarButton.label      "Sync">
+
+<!ENTITY addonBarCloseButton.tooltip  "Close Add-on Bar">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -1,18 +1,13 @@
 nv_done=Done
 nv_timeout=Timed Out
 nv_stopped=Stopped
 openFile=Open File
 
-droponbookmarksbutton=Drop a link to bookmark it
-dropondownloadsbutton=Drop a link or file to download it
-droponnewtabbutton=Drop a link or file to open it in a new tab
-droponnewwindowbutton=Drop a link or file to open it in a new window
-droponhomebutton=Drop a link or file to make it your home page
 droponhometitle=Set Home Page
 droponhomemsg=Do you want this document to be your new home page?
 
 # context menu strings
 
 # LOCALIZATION NOTE (contextMenuSearchText): %1$S is the search engine,
 # %2$S is the selection string.
 contextMenuSearchText=Search %1$S for "%2$S"
@@ -129,32 +124,16 @@ sanitizeButtonOK=Clear Now
 # "Time range to clear" is set to "Everything" in Clear Recent History dialog,
 # provided that the user has not modified the default set of history items to clear.
 sanitizeEverythingWarning2=All history will be cleared.
 # LOCALIZATION NOTE (sanitizeSelectedWarning): Warning that appears when
 # "Time range to clear" is set to "Everything" in Clear Recent History dialog,
 # provided that the user has modified the default set of history items to clear.
 sanitizeSelectedWarning=All selected items will be cleared.
 
-# Check for Updates
-# LOCALIZATION NOTE (updatesItem_*): these are alternative labels for Check for Update item in Help menu.
-# Which one is used depends on Update process state.
-updatesItem_default=Check for Updates…
-updatesItem_defaultFallback=Check for Updates…
-updatesItem_default.accesskey=C
-updatesItem_downloading=Downloading %S…
-updatesItem_downloadingFallback=Downloading Update…
-updatesItem_downloading.accesskey=D
-updatesItem_resume=Resume Downloading %S…
-updatesItem_resumeFallback=Resume Downloading Update…
-updatesItem_resume.accesskey=D
-updatesItem_pending=Apply Downloaded Update Now…
-updatesItem_pendingFallback=Apply Downloaded Update Now…
-updatesItem_pending.accesskey=D
-
 # Check for Updates in the About Dialog - button labels and accesskeys
 # LOCALIZATION NOTE - all of the following update buttons labels will only be
 # displayed one at a time. So, if a button is displayed nothing else will
 # be displayed alongside of the button. The button when displayed is located
 # directly under the Firefox version in the about dialog (see bug 596813 for
 # screenshots).
 update.checkInsideButton.label=Check for Updates
 update.checkInsideButton.accesskey=C
--- a/browser/locales/en-US/chrome/browser/preferences/sync.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/sync.dtd
@@ -14,16 +14,17 @@
 <!-- Manage Account -->
 <!ENTITY manageAccount.label          "Manage Account">
 <!ENTITY manageAccount.accesskey      "A">
 <!ENTITY viewQuota.label              "View Quota">
 <!ENTITY changePassword.label         "Change Password">
 <!ENTITY mySyncKey.label              "My Sync Key">
 <!ENTITY resetSync.label              "Reset Sync">
 <!ENTITY stopUsingAccount.label       "Stop Using This Account">
+<!ENTITY addDevice.label              "Add a Device">
 
 <!-- Sync Settings -->
 <!ENTITY syncPrefsCaption.label       "Browser Sync">
 <!ENTITY syncComputerName.label       "Computer Name:">
 <!ENTITY syncComputerName.accesskey   "c">
 
 <!ENTITY syncMy.label               "Sync My">
 <!ENTITY engine.bookmarks.label     "Bookmarks">
--- a/browser/locales/en-US/chrome/browser/syncSetup.dtd
+++ b/browser/locales/en-US/chrome/browser/syncSetup.dtd
@@ -1,30 +1,31 @@
 <!ENTITY accountSetupTitle.label    "&syncBrand.fullName.label; Setup">
 
 <!-- First page of the wizard -->
 
 <!ENTITY setup.pickSetupType.description "Welcome, if you've never used &syncBrand.fullName.label; before, you will need to create a new account.">
 <!ENTITY button.createNewAccount.label "Create a New Account">
-<!ENTITY button.haveAccount.label "I Have a &syncBrand.fullName.label; Account">
+<!ENTITY setup.haveAccount.label "I already have a &syncBrand.fullName.label; account.">
+<!ENTITY button.connect.label    "Connect">
 
 <!ENTITY setup.choicePage.title.label     "Have you used &syncBrand.fullName.label; before?">
 <!ENTITY setup.choicePage.new.label       "I've never used &syncBrand.shortName.label; before">
 <!ENTITY setup.choicePage.existing.label  "I'm already using &syncBrand.shortName.label; on another computer">
 
 <!-- New Account AND Existing Account -->
 <!ENTITY server.label               "Server">
 <!ENTITY serverType.main.label      "&syncBrand.fullName.label; Server">
-<!ENTITY serverType.custom.label    "Use a custom server">
-<!ENTITY signIn.account.label       "Email Address / User Name">
-<!ENTITY signIn.account.accesskey   "E">
+<!ENTITY serverType.custom2.label   "Use a custom server…">
+<!ENTITY signIn.account2.label      "Account">
+<!ENTITY signIn.account2.accesskey  "A">
 <!ENTITY signIn.password.label      "Password">
 <!ENTITY signIn.password.accesskey  "P">
-<!ENTITY signIn.serverURL.label     "Server URL">
-<!ENTITY signIn.serverURL.accesskey "L">
+<!ENTITY signIn.syncKey.label       "Sync Key">
+<!ENTITY signIn.syncKey.accesskey   "K">
 
 <!-- New Account Page 1: Basic Account Info -->
 <!ENTITY setup.newAccountDetailsPage.title.label "Account Details">
 <!ENTITY setup.confirmPassword.label  "Confirm Password">
 <!ENTITY setup.confirmPassword.accesskey  "m">
 <!ENTITY setup.emailAddress.label     "Email Address">
 <!ENTITY setup.emailAddress.accesskey "E">
 <!-- LOCALIZATION NOTE: tosAgree1, tosLink, tosAgree2, ppLink, tosAgree3 are
@@ -47,28 +48,35 @@
 
 <!ENTITY button.syncKeyBackup.print.label     "Print…">
 <!ENTITY button.syncKeyBackup.print.accesskey "P">
 <!ENTITY button.syncKeyBackup.save.label      "Save…">
 <!ENTITY button.syncKeyBackup.save.accesskey  "S">
 
 <!-- New Account Page 3: Captcha -->
 <!ENTITY setup.captchaPage2.title.label     "Please Confirm You're Not a Robot">
-
-<!-- Existing Account Page 1: Login -->
-<!ENTITY setup.existingAccount.title.label "Enter Account Information">
-<!ENTITY resetPassword.label          "Reset Password">
-<!ENTITY connecting.label             "Connecting…">
+<!-- Existing Account Page 1: Add Device (incl. Add a Device dialog strings) -->
+<!ENTITY addDevice.title.label              "Add a Device">
+<!ENTITY addDevice.showMeHow.label          "Show me how.">
+<!ENTITY addDevice.dontHaveDevice.label     "I don't have the device with me">
+<!ENTITY addDevice.setup.description.label  "To activate, go to &syncBrand.shortName.label; Options on your other device and select &#x0022;Add a Device&#x0022;.">
+<!ENTITY addDevice.setup.enterCode.label    "Then, enter this code:">
+<!ENTITY addDevice.dialog.description.label "To activate your new device, go to &syncBrand.shortName.label; Options on the device and select &#x0022;Connect.&#x0022;">
+<!ENTITY addDevice.dialog.enterCode.label   "Enter the code that the device provides:">
+<!ENTITY addDevice.dialog.tryAgain.label    "Please try again.">
+<!ENTITY addDevice.dialog.successful.label  "The device has been successfully added. The initial synchronization can take several minutes and will finish in the background.">
+<!ENTITY addDevice.dialog.syncKey.label     "To activate your device you will need to enter your Sync Key. Please print or save this key and take it with you.">
+<!ENTITY addDevice.dialog.connected.label   "Device Connected">
 
-<!-- Existing Account Page 2: Sync Key -->
-<!ENTITY setup.existingSyncKeyPage.title "Please Enter Your Sync Key">
-<!ENTITY setup.existingSyncKeyPage.description "The Sync Key was generated for you, or you entered your own, when you first signed up for &syncBrand.fullName.label;. It is not the same as your password.">
-<!ENTITY existingSyncKeyHelp.description "You can find your saved Sync Key by going to your other computer and checking the Saved Passwords under Security. If you still cannot get the correct Sync Key, you can choose to reset it, but you will lose any data stored on the server.">
+<!-- Existing Account Page 2: Manual Login -->
+<!ENTITY setup.signInPage.title.label "Sign In">
+<!ENTITY existingSyncKey.description "You can get a copy of your Sync Key by going to &syncBrand.shortName.label; Options on your other device, and selecting  &#x0022;My Sync Key&#x0022; under &#x0022;Manage Account&#x0022;.">
+<!ENTITY verifying.label              "Verifying…">
+<!ENTITY resetPassword.label          "Reset Password">
 <!ENTITY lostSyncKey.label            "I have lost my Sync Key">
-<!ENTITY verifying.label              "Verifying…">
 
 <!-- Sync Options -->
 <!ENTITY setup.optionsPage.title      "Sync Options">
 <!ENTITY syncComputerName.label       "Computer Name:">
 <!ENTITY syncComputerName.accesskey   "c">
 
 <!ENTITY syncMy.label               "Sync My">
 <!ENTITY engine.bookmarks.label     "Bookmarks">
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -1218,29 +1218,32 @@ toolbar[iconsize="small"] #feed-button {
 .ac-comment {
   font-size: 1.15em;
 }
 
 .ac-extra > .ac-comment {
   font-size: inherit;
 }
 
-.ac-url-text {
+.ac-url-text,
+.ac-action-text {
   color: -moz-nativehyperlinktext;
 }
 
 richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
   list-style-image: url("chrome://browser/skin/actionicon-tab.png");
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
   color: GrayText;
 }
 
-.ac-comment[selected="true"], .ac-url-text[selected="true"] {
+.ac-comment[selected="true"],
+.ac-url-text[selected="true"],
+.ac-action-text[selected="true"] {
   color: inherit !important;
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(suggesthint, treecolAutoCompleteComment), 
 .autocomplete-treebody::-moz-tree-cell-text(suggestfirst, treecolAutoCompleteComment) {
   color: GrayText;
   font-size: smaller;
 }
@@ -1772,8 +1775,13 @@ panel[dimmed="true"] {
 #status-bar > statusbarpanel {
   border-width: 0;
   -moz-appearance: none;
 }
 
 browser[tabmodalPromptShowing] {
   filter: url("chrome://browser/skin/effects.svg#blurAndDesaturate");
 }
+
+/* Add-on bar close button */
+#addonbar-closebutton {
+  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
+}
--- a/browser/themes/gnomestripe/browser/syncSetup.css
+++ b/browser/themes/gnomestripe/browser/syncSetup.css
@@ -52,18 +52,18 @@ wizardpage {
 
 .confirm {
   border: 1px solid black;
   padding: 1em;
   border-radius: 5px;
 }
 
 /* Override the text-link style from global.css */
-.text-link,
-.text-link:focus {
+description > .text-link,
+description > .text-link:focus {
   margin: 0px;
   padding: 0px;
   border: 0px;
 }
 
 
 .success,
 .error {
@@ -94,17 +94,23 @@ wizardpage {
 .normal {
   font-size: 100%;
 }
 
 .inputColumn {
   -moz-margin-end: 2px
 }
 
-#connect-throbber image,
-#passphrase-throbber image {
+.pin {
+  font-size: 18pt;
+  width: 4em;
+  text-align: center; 
+}
+
+#add-device-throbber > image,
+#login-throbber > image {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
 #successPageIcon {
   /* TODO replace this with a 128px version (bug 591122) */
   list-style-image: url("chrome://browser/skin/sync-32.png");
 }
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -917,30 +917,33 @@ richlistitem[selected="true"][current="t
 .ac-comment {
   font-size: 1.1em;
 }
 
 .ac-extra > .ac-comment {
   font-size: inherit;
 }
 
-.ac-url-text {
+.ac-url-text,
+.ac-action-text {
   color: -moz-nativehyperlinktext;
   font-size: 0.95em;
 }
 
 richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
   list-style-image: url("chrome://browser/skin/actionicon-tab.png");
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
   color: GrayText;
 }
 
-.ac-comment[selected="true"], .ac-url-text[selected="true"] {
+.ac-comment[selected="true"],
+.ac-url-text[selected="true"],
+.ac-action-text[selected="true"] {
   color: inherit !important;
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(suggesthint, treecolAutoCompleteComment), 
 .autocomplete-treebody::-moz-tree-cell-text(suggestfirst, treecolAutoCompleteComment) 
 {
   color: GrayText;
   font-size: smaller;
@@ -2257,11 +2260,31 @@ panel[dimmed="true"] {
 
 /* Remove all borders from statusbarpanel children of
    the statusbar. */
 #status-bar > statusbarpanel {
   border-width: 0;
   -moz-appearance: none;
 }
 
+#addon-bar {
+  -moz-appearance: statusbar;
+}
+
+/* Add-on bar close button */
+#addonbar-closebutton {
+  padding: 0;
+  margin: 0 4px;
+  list-style-image: url("chrome://global/skin/icons/closetab.png");
+  border: none;
+}
+
+#addonbar-closebutton:hover {
+  list-style-image: url("chrome://global/skin/icons/closetab-hover.png");
+}
+
+#addonbar-closebutton:hover:active {
+  list-style-image: url("chrome://global/skin/icons/closetab-active.png");
+}
+
 browser[tabmodalPromptShowing] {
   filter: url("chrome://browser/skin/effects.svg#blurAndDesaturate");
 }
--- a/browser/themes/pinstripe/browser/syncSetup.css
+++ b/browser/themes/pinstripe/browser/syncSetup.css
@@ -52,24 +52,23 @@ wizardpage {
 
 .confirm {
   border: 1px solid black;
   padding: 1em;
   border-radius: 5px;
 }
 
 /* Override the text-link style from global.css */
-.text-link,
-.text-link:focus {
+description > .text-link,
+description > .text-link:focus {
   margin: 0px;
   padding: 0px;
   border: 0px;
 }
 
-
 .success,
 .error {
   padding: 2px;
   border-radius: 2px;
 }
 
 .error {
   background-color: #FF0000 !important;
@@ -94,17 +93,23 @@ wizardpage {
 .normal {
   font-size: 100%;
 }
 
 .inputColumn {
   -moz-margin-end: 2px
 }
 
-#connect-throbber image,
-#passphrase-throbber image {
+.pin {
+  font-size: 18pt;
+  width: 4em;
+  text-align: center; 
+}
+
+#add-device-throbber > image,
+#login-throbber > image {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
 #successPageIcon {
   /* TODO replace this with a 128px version (bug 591122) */
   list-style-image: url("chrome://browser/skin/sync-32.png");
 }
--- a/browser/themes/pinstripe/browser/tabview/tabview.css
+++ b/browser/themes/pinstripe/browser/tabview/tabview.css
@@ -524,17 +524,17 @@ html[dir=rtl] .iq-resizable-se {
 }
 
 #exit-button[groups="3"] {
   background-image: -moz-image-rect(url(chrome://browser/skin/tabview/tabview.png), 0, 80, 20, 60);
 }
 
 html[dir=rtl] #exit-button {
   right: auto;
-  left: 0;
+  left: 1px;
 }
 
 /* Search
 ----------------------------------*/
 #searchshade{
   background-color: rgba(0,0,0,.42);
   width: 100%;
   height: 100%;
--- a/browser/themes/winstripe/browser/browser-aero.css
+++ b/browser/themes/winstripe/browser/browser-aero.css
@@ -115,16 +115,26 @@
     border-left: 1px solid @glassToolbarBorderColor@;
     border-right: 1px solid @glassToolbarBorderColor@;
   }
   #navigator-toolbox,
   #navigator-toolbox > toolbar {
     border-color: @glassToolbarBorderColor@ !important;
     background-clip: padding-box;
   }
+  #main-window[sizemode="normal"] #browser-border-start,
+  #main-window[sizemode="normal"] #browser-border-end {
+    display: -moz-box;
+    background-color: @glassToolbarBorderColor@;
+    width: 1px;
+  }
+  #main-window[sizemode="normal"] #browser-bottombox {
+    border: 1px solid @glassToolbarBorderColor@;
+    border-top-style: none;
+  }
 
   #main-window[sizemode="normal"] #navigator-toolbox[tabsontop="true"] > #nav-bar:not([inFullscreen="true"]):not(:-moz-lwtheme),
   #main-window[sizemode="normal"] #navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + toolbar:not([inFullscreen="true"]):not(:-moz-lwtheme),
   #main-window[sizemode="normal"] #navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme),
   #main-window[sizemode="normal"] #navigator-toolbox:not([tabsontop="true"]) > #PersonalToolbar:not(:-moz-lwtheme) {
     border-top-left-radius: 3.5px;
     border-top-right-radius: 3.5px;
   }
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -1245,35 +1245,39 @@ html|*.urlbar-input:-moz-lwtheme:-moz-pl
 .ac-comment {
   font-size: 1.15em;
 }
 
 .ac-extra > .ac-comment {
   font-size: inherit;
 }
 
-.ac-url-text {
+.ac-url-text,
+.ac-action-text {
   color: -moz-nativehyperlinktext;
 }
 
 %ifndef WINSTRIPE_AERO
-.ac-url-text:-moz-system-metric(windows-default-theme) {
+.ac-url-text:-moz-system-metric(windows-default-theme),
+.ac-action-text:-moz-system-metric(windows-default-theme) {
   color: #006600;
 }
 %endif
 
 richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
   list-style-image: url("chrome://browser/skin/actionicon-tab.png");
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
   color: GrayText;
 }
 
-.ac-comment[selected="true"], .ac-url-text[selected="true"] {
+.ac-comment[selected="true"],
+.ac-url-text[selected="true"],
+.ac-action-text[selected="true"] {
   color: inherit !important;
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(suggesthint, treecolAutoCompleteComment), 
 .autocomplete-treebody::-moz-tree-cell-text(suggestfirst, treecolAutoCompleteComment) 
 {
   color: GrayText;
   font-size: smaller;
@@ -2121,8 +2125,25 @@ panel[dimmed="true"] {
 #status-bar > statusbarpanel {
   border-width: 0;
   -moz-appearance: none;
 }
 
 browser[tabmodalPromptShowing] {
   filter: url("chrome://browser/skin/effects.svg#blurAndDesaturate");
 }
+
+/* Add-on bar close button */
+#addonbar-closebutton {
+  border: none;
+  padding: 3px 5px;
+  list-style-image: url("chrome://global/skin/icons/close.png");
+  -moz-appearance: none;
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+#addonbar-closebutton:hover {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+#addonbar-closebutton:hover:active {
+  -moz-image-region: rect(0, 48px, 16px, 32px);
+}
--- a/browser/themes/winstripe/browser/syncSetup.css
+++ b/browser/themes/winstripe/browser/syncSetup.css
@@ -52,18 +52,18 @@ wizardpage {
 
 .confirm {
   border: 1px solid black;
   padding: 1em;
   border-radius: 5px;
 }
 
 /* Override the text-link style from global.css */
-.text-link,
-.text-link:focus {
+description > .text-link,
+description > .text-link:focus {
   margin: 0px;
   padding: 0px;
   border: 0px;
 }
 
 
 .success,
 .error {
@@ -94,17 +94,23 @@ wizardpage {
 .normal {
   font-size: 100%;
 }
 
 .inputColumn {
   -moz-margin-end: 2px
 }
 
-#connect-throbber image,
-#passphrase-throbber image {
+.pin {
+  font-size: 18pt;
+  width: 4em;
+  text-align: center; 
+}
+
+#add-device-throbber > image,
+#login-throbber > image {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
 #successPageIcon {
   /* TODO replace this with a 128px version (bug 591122) */
   list-style-image: url("chrome://browser/skin/sync-32.png");
 }
--- a/build/automation-build.mk
+++ b/build/automation-build.mk
@@ -1,29 +1,11 @@
-ifneq (,$(filter OS2 WINCE WINNT,$(OS_ARCH)))
-PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
-else
-PROGRAM = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
-endif
-
-TARGET_DIST = $(TARGET_DEPTH)/dist
+include $(MOZILLA_DIR)/build/binary-location.mk
 
-ifeq ($(MOZ_BUILD_APP),camino)
-browser_path = \"$(TARGET_DIST)/Camino.app/Contents/MacOS/Camino\"
-else
-ifeq ($(OS_ARCH),Darwin)
-ifdef MOZ_DEBUG
-browser_path = \"$(TARGET_DIST)/$(MOZ_APP_DISPLAYNAME)Debug.app/Contents/MacOS/$(PROGRAM)\"
-else
-browser_path = \"$(TARGET_DIST)/$(MOZ_APP_DISPLAYNAME).app/Contents/MacOS/$(PROGRAM)\"
-endif
-else
-browser_path = \"$(TARGET_DIST)/bin/$(PROGRAM)\"
-endif
-endif
+browser_path := \"$(browser_path)\"
 
 _PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo
 
 ABSOLUTE_TOPSRCDIR = $(call core_abspath,$(MOZILLA_DIR))
 _CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs
 
 AUTOMATION_PPARGS = 	\
 			-DBROWSER_PATH=$(browser_path) \
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -327,16 +327,17 @@ class Automation(object):
     os.mkdir(profileDir)
 
     # Set up permissions database
     locations = self.readLocations()
     self.setupPermissionsDatabase(profileDir,
       {'allowXULXBL':[(l.host, 'noxul' not in l.options) for l in locations]});
 
     part = """\
+user_pref("browser.console.showInPanel", true);
 user_pref("browser.dom.window.dump.enabled", true);
 user_pref("dom.allow_scripts_to_close_windows", true);
 user_pref("dom.disable_open_during_load", false);
 user_pref("dom.max_script_run_time", 0); // no slow script dialogs
 user_pref("dom.max_chrome_script_run_time", 0);
 user_pref("dom.popup_maximum", -1);
 user_pref("dom.successive_dialog_time_limit", 0);
 user_pref("signed.applets.codebase_principal_support", true);
new file mode 100644
--- /dev/null
+++ b/build/binary-location.mk
@@ -0,0 +1,23 @@
+# finds the location of the browser and puts it in the variable $(browser_path)
+
+ifneq (,$(filter OS2 WINCE WINNT,$(OS_ARCH)))
+PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
+else
+PROGRAM = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
+endif
+
+TARGET_DIST = $(TARGET_DEPTH)/dist
+
+ifeq ($(MOZ_BUILD_APP),camino)
+browser_path = $(TARGET_DIST)/Camino.app/Contents/MacOS/Camino
+else
+ifeq ($(OS_ARCH),Darwin)
+ifdef MOZ_DEBUG
+browser_path = $(TARGET_DIST)/$(MOZ_APP_DISPLAYNAME)Debug.app/Contents/MacOS/$(PROGRAM)
+else
+browser_path = $(TARGET_DIST)/$(MOZ_APP_DISPLAYNAME).app/Contents/MacOS/$(PROGRAM)
+endif
+else
+browser_path = $(TARGET_DIST)/bin/$(PROGRAM)
+endif
+endif
--- a/configure.in
+++ b/configure.in
@@ -4599,17 +4599,17 @@ dnl ====================================
 dnl = If NSPR was not detected in the system, 
 dnl = use the one in the source tree (mozilla/nsprpub)
 dnl ========================================================
 MOZ_ARG_WITH_BOOL(system-nspr,
 [  --with-system-nspr      Use system installed NSPR],
     _USE_SYSTEM_NSPR=1 )
 
 if test -n "$_USE_SYSTEM_NSPR"; then
-    AM_PATH_NSPR(4.8.6, [MOZ_NATIVE_NSPR=1], [MOZ_NATIVE_NSPR=])
+    AM_PATH_NSPR(4.8.7, [MOZ_NATIVE_NSPR=1], [MOZ_NATIVE_NSPR=])
 fi
 
 if test -n "$MOZ_NATIVE_NSPR"; then
     _SAVE_CFLAGS=$CFLAGS
     CFLAGS="$CFLAGS $NSPR_CFLAGS"
     AC_TRY_COMPILE([#include "prtypes.h"],
                 [#ifndef PR_STATIC_ASSERT
                  #error PR_STATIC_ASSERT not defined or requires including prlog.h
@@ -4676,17 +4676,17 @@ dnl = If NSS was not detected in the sys
 dnl = use the one in the source tree (mozilla/security/nss)
 dnl ========================================================
 
 MOZ_ARG_WITH_BOOL(system-nss,
 [  --with-system-nss       Use system installed NSS],
     _USE_SYSTEM_NSS=1 )
 
 if test -n "$_USE_SYSTEM_NSS"; then
-    AM_PATH_NSS(3.12.8, [MOZ_NATIVE_NSS=1], [MOZ_NATIVE_NSS=])
+    AM_PATH_NSS(3.12.9, [MOZ_NATIVE_NSS=1], [MOZ_NATIVE_NSS=])
 fi
 
 if test -n "$MOZ_NATIVE_NSS"; then
    NSS_LIBS="$NSS_LIBS -lcrmf"
 else
    NSS_CFLAGS='-I$(LIBXUL_DIST)/include/nss'
    NSS_DEP_LIBS="\
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)crmf.\$(LIB_SUFFIX) \
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/411882-1.xhtml
@@ -0,0 +1,1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><body></body><textarea></textarea></html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/509536-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+  var frame = document.createElement("iframe");
+  frame.setAttribute("src", "1");
+  document.body.appendChild(frame);
+  frame.setAttribute("src", "2");
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/561981-1-iframe.xhtml
@@ -0,0 +1,56 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+<![CDATA[
+
+var i = 0;
+
+function init()
+{
+  targetWindow = window.frames[0];
+  targetDocument = targetWindow.document;
+  targetDocument.body.appendChild(targetDocument.importNode(document.getElementById('rootish'), true));
+  targetDocument.designMode = 'on';
+  setTimeout(boom, 30);
+}
+
+function boom()
+{
+  var r = targetDocument.createRange();
+  r.setStart(targetDocument.getElementById("bar"), 0);
+  r.setEnd(targetDocument.getElementById("baz").firstChild, 0);
+  targetWindow.getSelection().addRange(r);
+  targetDocument.execCommand("indent", false, null);
+  setTimeout(whack, 300);
+}
+
+function whack()
+{
+  if (++i > 100) return;
+  document.documentElement.style.MozBinding = 'url("data:text/xml,' + encodeURIComponent("<bindings xmlns=\"http://www.mozilla.org/xbl\"><binding id=\"foo\" g=\""+Math.random()+"\"><content>\n<\/content><\/binding><\/bindings>\n") + '")';
+  setTimeout(bonk, 10);
+}
+
+function bonk()
+{
+  document.getElementById("i").style.MozBinding = 'url("data:text/xml,' + encodeURIComponent("<bindings xmlns=\"http://www.mozilla.org/xbl\"><binding id=\"foo\" g=\""+Math.random()+"\"><content>\n\n<\/content><\/binding><\/bindings>\n") + '")';
+  document.documentElement.style.MozBinding = 'url("data:text/xml,' + encodeURIComponent("<bindings xmlns=\"http://www.mozilla.org/xbl\"><binding id=\"foo\" g=\""+Math.random()+"\"><content><iframe xmlns=\"http://www.w3.org/1999/xhtml\" src=\"data:text/html,\" style=\"width: 95%; height: 500px;\"><\/iframe><\/content><\/binding><\/bindings>\n") + '")';
+  setTimeout(whack, 10);
+}
+
+]]>
+</script>
+</head>
+
+<body onload="init()">
+
+<iframe id="i" src="data:text/html," style="width: 95%; height: 500px;"/>
+
+<div id="rootish">
+  <div>Foo</div>
+  <div id="bar">Bar</div>
+  <div><select><option id="baz">baz</option></select></div>
+</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/561981-1.html
@@ -0,0 +1,12 @@
+<html class="reftest-wait">
+<head>
+<script>
+function finish() {
+  document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body onload="setTimeout(finish, 5000)">
+<iframe src="561981-1-iframe.xhtml"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/561981-2-iframe.xhtml
@@ -0,0 +1,38 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+<![CDATA[
+
+var i = 0;
+
+function init()
+{
+  targetWindow = window.frames[0];
+  targetDocument = targetWindow.document;
+  targetDocument.designMode = 'on';
+  setTimeout(whack, 30);
+}
+
+function whack()
+{
+  if (++i > 100) return;
+  document.documentElement.style.MozBinding = 'url("data:text/xml,' + encodeURIComponent("<bindings xmlns=\"http://www.mozilla.org/xbl\"><binding id=\"foo\" g=\""+Math.random()+"\"><content>\n<\/content><\/binding><\/bindings>\n") + '")';
+  setTimeout(bonk, 10);
+}
+
+function bonk()
+{
+  document.getElementById("i").style.MozBinding = 'url("data:text/xml,' + encodeURIComponent("<bindings xmlns=\"http://www.mozilla.org/xbl\"><binding id=\"foo\" g=\""+Math.random()+"\"><content>\n\n<\/content><\/binding><\/bindings>\n") + '")';
+  setTimeout(whack, 10);
+}
+
+]]>
+</script>
+</head>
+
+<body onload="init()">
+
+<iframe id="i" src="data:text/html," style="width: 95%; height: 500px;"/>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/561981-2.html
@@ -0,0 +1,12 @@
+<html class="reftest-wait">
+<head>
+<script>
+function finish() {
+  document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body onload="setTimeout(finish, 5000)">
+<iframe src="561981-2-iframe.xhtml"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/590395-1.html
@@ -0,0 +1,5 @@
+<html>
+<body onload="document.createElement('div').appendChild(document.getElementById('i').contentDocument.getElementById('j'));">
+<iframe id="i" src="data:text/html,<img id='j' src='about:blank'>">
+</body>
+</html>
--- a/content/base/crashtests/crashtests.list
+++ b/content/base/crashtests/crashtests.list
@@ -38,16 +38,17 @@ load 387460-1.html
 load 395469-1.xhtml
 load 395469-2.xhtml
 skip load 399712-1.html # sporadically times out (bug 473680)
 load 398088-1.xul
 load 400763-1.html
 load 401993-1.html
 load 407818.html
 load 410860-1.xml
+load 411882-1.xhtml
 load 416734-1.html
 load 418928-1.html
 load 420620-1.html
 load 424276-1.html
 load 426987-1.html
 load 443538-1.svg
 load 448615-1.html
 load 450383-1.html
@@ -55,23 +56,27 @@ load 450385-1.html
 skip load 458637-1.html # sporadically times out (bug 473680)
 load 472593-1.html
 load 474041-1.svg
 load 483818-1.html
 load 493281-1.html
 load 493281-2.html
 load 490760-1.xhtml
 load 494810-1.html
+load 509536-1.html
 load 522516-1.html
 load 529670.html
 load 535926-1.html
 load 551631-1.html
 load 554230-1.xhtml
 load 552651.html
 load 558973.html
+load 561981-1.html
+load 561981-2.html
 load 564079-1.html 
 load 564114.html
 load 565125-1.html
 load 582601.html
 load 575462.svg
+load 590395-1.html
 load 595606-1.html
 load 595606-2.html
 load 606729-1.html
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -186,28 +186,30 @@ struct nsShortcutCandidate {
 class nsContentUtils
 {
   typedef mozilla::dom::Element Element;
 
 public:
   static nsresult Init();
 
   /**
-   * Get a scope from aNewDocument. Also get a context through the scope of one
-   * of the documents, from the stack or the safe context.
+   * Get a scope from aOldDocument and one from aNewDocument. Also get a
+   * context through one of the scopes, from the stack or the safe context.
    *
-   * @param aOldDocument The document to try to get a context from. May be null.
+   * @param aOldDocument The document to get aOldScope from.
    * @param aNewDocument The document to get aNewScope from.
    * @param aCx [out] Context gotten through one of the scopes, from the stack
    *                  or the safe context.
+   * @param aOldScope [out] Scope gotten from aOldDocument.
    * @param aNewScope [out] Scope gotten from aNewDocument.
    */
-  static nsresult GetContextAndScope(nsIDocument *aOldDocument,
-                                     nsIDocument *aNewDocument,
-                                     JSContext **aCx, JSObject **aNewScope);
+  static nsresult GetContextAndScopes(nsIDocument *aOldDocument,
+                                      nsIDocument *aNewDocument,
+                                      JSContext **aCx, JSObject **aOldScope,
+                                      JSObject **aNewScope);
 
   /**
    * When a document's scope changes (e.g., from document.open(), call this
    * function to move all content wrappers from the old scope to the new one.
    */
   static nsresult ReparentContentWrappersInScope(nsIScriptGlobalObject *aOldScope,
                                                  nsIScriptGlobalObject *aNewScope);
 
@@ -1672,16 +1674,33 @@ public:
    * If one can't be found, a BasicLayerManager is created and returned.
    *
    * @param aDoc the document for which to return a layer manager.
    */
   static already_AddRefed<mozilla::layers::LayerManager>
   LayerManagerForDocument(nsIDocument *aDoc);
 
   /**
+   * Returns a layer manager to use for the given document. Basically we
+   * look up the document hierarchy for the first document which has
+   * a presentation with an associated widget, and use that widget's
+   * layer manager. In addition to the normal layer manager lookup this will
+   * specifically request a persistent layer manager. This means that the layer
+   * manager is expected to remain the layer manager for the document in the
+   * forseeable future. This function should be used carefully as it may change
+   * the document's layer manager.
+   *
+   * If one can't be found, a BasicLayerManager is created and returned.
+   *
+   * @param aDoc the document for which to return a layer manager.
+   */
+  static already_AddRefed<mozilla::layers::LayerManager>
+  PersistentLayerManagerForDocument(nsIDocument *aDoc);
+
+  /**
    * Determine whether a content node is focused or not,
    *
    * @param aContent the content node to check
    * @return true if the content node is focused, false otherwise.
    */
   static PRBool IsFocusedContent(const nsIContent *aContent);
 
   /**
@@ -1704,17 +1723,16 @@ public:
 
   /**
    * Returns true if content with the given principal is allowed to use XUL
    * and XBL and false otherwise.
    */
   static bool AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal);
 
 private:
-
   static PRBool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static nsIDOMScriptObjectFactory *GetDOMScriptObjectFactory();
 
   static nsresult HoldScriptObject(PRUint32 aLangID, void* aObject);
   static void DropScriptObject(PRUint32 aLangID, void *aObject, void *aClosure);
@@ -1834,58 +1852,16 @@ private:
   nsCOMPtr<nsIScriptContext> mScx;
   PRBool mScriptIsRunning;
   PRBool mPushedSomething;
 #ifdef DEBUG
   JSContext* mPushedContext;
 #endif
 };
 
-class NS_STACK_CLASS nsAutoGCRoot {
-public:
-  // aPtr should be the pointer to the jsval we want to protect
-  nsAutoGCRoot(jsval* aPtr, nsresult* aResult
-               MOZILLA_GUARD_OBJECT_NOTIFIER_PARAM) :
-    mPtr(aPtr), mRootType(RootType_JSVal)
-  {
-    MOZILLA_GUARD_OBJECT_NOTIFIER_INIT;
-    mResult = *aResult = AddJSGCRoot(aPtr, RootType_JSVal, "nsAutoGCRoot");
-  }
-
-  // aPtr should be the pointer to the JSObject* we want to protect
-  nsAutoGCRoot(JSObject** aPtr, nsresult* aResult
-               MOZILLA_GUARD_OBJECT_NOTIFIER_PARAM) :
-    mPtr(aPtr), mRootType(RootType_Object)
-  {
-    MOZILLA_GUARD_OBJECT_NOTIFIER_INIT;
-    mResult = *aResult = AddJSGCRoot(aPtr, RootType_Object, "nsAutoGCRoot");
-  }
-
-  ~nsAutoGCRoot() {
-    if (NS_SUCCEEDED(mResult)) {
-      RemoveJSGCRoot((jsval *)mPtr, mRootType);
-    }
-  }
-
-  static void Shutdown();
-
-private:
-  enum RootType { RootType_JSVal, RootType_Object };
-  static nsresult AddJSGCRoot(void *aPtr, RootType aRootType, const char* aName);
-  static nsresult RemoveJSGCRoot(void *aPtr, RootType aRootType);
-
-  static nsIJSRuntimeService* sJSRuntimeService;
-  static JSRuntime* sJSScriptRuntime;
-
-  void* mPtr;
-  RootType mRootType;
-  nsresult mResult;
-  MOZILLA_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 class NS_STACK_CLASS nsAutoScriptBlocker {
 public:
   nsAutoScriptBlocker(MOZILLA_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
     MOZILLA_GUARD_OBJECT_NOTIFIER_INIT;
     nsContentUtils::AddScriptBlocker();
   }
   ~nsAutoScriptBlocker() {
     nsContentUtils::RemoveScriptBlocker();
@@ -1903,23 +1879,16 @@ public:
 
 private:
   PRUint32 mNestingLevel;
   nsCOMPtr<nsIDocument> mDocument;
   nsCOMPtr<nsIDocumentObserver> mObserver;
   MOZILLA_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-#define NS_AUTO_GCROOT_PASTE2(tok,line) tok##line
-#define NS_AUTO_GCROOT_PASTE(tok,line) \
-  NS_AUTO_GCROOT_PASTE2(tok,line)
-#define NS_AUTO_GCROOT(ptr, result) \ \
-  nsAutoGCRoot NS_AUTO_GCROOT_PASTE(_autoGCRoot_, __LINE__) \
-  (ptr, result)
-
 #define NS_INTERFACE_MAP_ENTRY_TEAROFF(_interface, _allocator)                \
   if (aIID.Equals(NS_GET_IID(_interface))) {                                  \
     foundInterface = static_cast<_interface *>(_allocator);                   \
     if (!foundInterface) {                                                    \
       *aInstancePtr = nsnull;                                                 \
       return NS_ERROR_OUT_OF_MEMORY;                                          \
     }                                                                         \
   } else
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -253,19 +253,16 @@ nsIBidiKeyboard *nsContentUtils::sBidiKe
 #endif
 PRUint32 nsContentUtils::sScriptBlockerCount = 0;
 PRUint32 nsContentUtils::sRemovableScriptBlockerCount = 0;
 nsCOMArray<nsIRunnable>* nsContentUtils::sBlockedScriptRunners = nsnull;
 PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
 PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0;
 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
 
-nsIJSRuntimeService *nsAutoGCRoot::sJSRuntimeService;
-JSRuntime *nsAutoGCRoot::sJSScriptRuntime;
-
 PRBool nsContentUtils::sIsHandlingKeyBoardEvent = PR_FALSE;
 PRBool nsContentUtils::sAllowXULXBL_for_file = PR_FALSE;
 
 PRBool nsContentUtils::sInitialized = PR_FALSE;
 
 nsRefPtrHashtable<nsPrefObserverHashKey, nsPrefOldCallback>
   *nsContentUtils::sPrefCallbackTable = nsnull;
 
@@ -1197,18 +1194,16 @@ nsContentUtils::Shutdown()
   NS_ASSERTION(!sBlockedScriptRunners ||
                sBlockedScriptRunners->Count() == 0,
                "How'd this happen?");
   delete sBlockedScriptRunners;
   sBlockedScriptRunners = nsnull;
 
   NS_IF_RELEASE(sSameOriginChecker);
   
-  nsAutoGCRoot::Shutdown();
-
   nsTextEditorState::ShutDown();
 }
 
 // static
 PRBool
 nsContentUtils::IsCallerTrustedForCapability(const char* aCapability)
 {
   // The secman really should handle UniversalXPConnect case, since that
@@ -1357,57 +1352,74 @@ nsContentUtils::InProlog(nsINode *aNode)
 
   nsIDocument* doc = static_cast<nsIDocument*>(parent);
   nsIContent* root = doc->GetRootElement();
 
   return !root || doc->IndexOf(aNode) < doc->IndexOf(root);
 }
 
 static JSContext *
-GetContextFromDocument(nsIDocument *aDocument)
+GetContextFromDocument(nsIDocument *aDocument, JSObject** aGlobalObject)
 {
   nsIScriptGlobalObject *sgo = aDocument->GetScopeObject();
   if (!sgo) {
     // No script global, no context.
+
+    *aGlobalObject = nsnull;
+
     return nsnull;
   }
 
+  *aGlobalObject = sgo->GetGlobalJSObject();
+
   nsIScriptContext *scx = sgo->GetContext();
   if (!scx) {
-    // No context left in the scope...
+    // No context left in the old scope...
 
     return nsnull;
   }
 
   return (JSContext *)scx->GetNativeContext();
 }
 
 // static
 nsresult
-nsContentUtils::GetContextAndScope(nsIDocument *aOldDocument,
-                                   nsIDocument *aNewDocument, JSContext **aCx,
-                                   JSObject **aNewScope)
+nsContentUtils::GetContextAndScopes(nsIDocument *aOldDocument,
+                                    nsIDocument *aNewDocument, JSContext **aCx,
+                                    JSObject **aOldScope, JSObject **aNewScope)
 {
   *aCx = nsnull;
+  *aOldScope = nsnull;
   *aNewScope = nsnull;
 
-  JSObject *newScope = aNewDocument->GetWrapper();
-  JSObject *global;
-  if (!newScope) {
-    nsIScriptGlobalObject *newSGO = aNewDocument->GetScopeObject();
-    if (!newSGO || !(global = newSGO->GetGlobalJSObject())) {
-      return NS_OK;
-    }
+  JSObject *newScope = nsnull;
+  nsIScriptGlobalObject *newSGO = aNewDocument->GetScopeObject();
+  if (!newSGO || !(newScope = newSGO->GetGlobalJSObject())) {
+    return NS_OK;
   }
 
   NS_ENSURE_TRUE(sXPConnect, NS_ERROR_NOT_INITIALIZED);
 
-  JSContext *cx = aOldDocument ? GetContextFromDocument(aOldDocument) : nsnull;
+  // Make sure to get our hands on the right scope object, since
+  // GetWrappedNativeOfNativeObject doesn't call PreCreate and hence won't get
+  // the right scope if we pass in something bogus.  The right scope lives on
+  // the script global of the old document.
+  // XXXbz note that if GetWrappedNativeOfNativeObject did call PreCreate it
+  // would get the wrong scope (that of the _new_ document), so we should be
+  // glad it doesn't!
+  JSObject *oldScope = nsnull;
+  JSContext *cx = GetContextFromDocument(aOldDocument, &oldScope);
+
+  if (!oldScope) {
+    return NS_OK;
+  }
+
   if (!cx) {
-    cx = GetContextFromDocument(aNewDocument);
+    JSObject *dummy;
+    cx = GetContextFromDocument(aNewDocument, &dummy);
 
     if (!cx) {
       // No context reachable from the old or new document, use the
       // calling context, or the safe context if no caller can be
       // found.
 
       sThreadJSContextStack->Peek(&cx);
 
@@ -1419,25 +1431,18 @@ nsContentUtils::GetContextAndScope(nsIDo
           NS_WARNING("No context reachable in GetContextAndScopes()!");
 
           return NS_ERROR_NOT_AVAILABLE;
         }
       }
     }
   }
 
-  if (!newScope && cx) {
-    jsval v;
-    nsresult rv = WrapNative(cx, global, aNewDocument, aNewDocument, &v);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    newScope = JSVAL_TO_OBJECT(v);
-  }
-
   *aCx = cx;
+  *aOldScope = oldScope;
   *aNewScope = newScope;
 
   return NS_OK;
 }
 
 nsresult
 nsContentUtils::ReparentContentWrappersInScope(nsIScriptGlobalObject *aOldScope,
                                                nsIScriptGlobalObject *aNewScope)
@@ -3262,63 +3267,16 @@ nsContentUtils::GetContentPolicy()
     // It's OK to not have a content policy service
     sTriedToGetContentPolicy = PR_TRUE;
   }
 
   return sContentPolicyService;
 }
 
 // static
-nsresult
-nsAutoGCRoot::AddJSGCRoot(void* aPtr, RootType aRootType, const char* aName)
-{
-  if (!sJSScriptRuntime) {
-    nsresult rv = CallGetService("@mozilla.org/js/xpc/RuntimeService;1",
-                                 &sJSRuntimeService);
-    NS_ENSURE_TRUE(sJSRuntimeService, rv);
-
-    sJSRuntimeService->GetRuntime(&sJSScriptRuntime);
-    if (!sJSScriptRuntime) {
-      NS_RELEASE(sJSRuntimeService);
-      NS_WARNING("Unable to get JS runtime from JS runtime service");
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  PRBool ok;
-  if (aRootType == RootType_JSVal)
-    ok = ::js_AddRootRT(sJSScriptRuntime, (jsval *)aPtr, aName);
-  else
-    ok = ::js_AddGCThingRootRT(sJSScriptRuntime, (void **)aPtr, aName);
-  if (!ok) {
-    NS_WARNING("JS_AddNamedRootRT failed");
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return NS_OK;
-}
-
-/* static */
-nsresult
-nsAutoGCRoot::RemoveJSGCRoot(void* aPtr, RootType aRootType)
-{
-  if (!sJSScriptRuntime) {
-    NS_NOTREACHED("Trying to remove a JS GC root when none were added");
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  if (aRootType == RootType_JSVal)
-    ::js_RemoveRoot(sJSScriptRuntime, (jsval *)aPtr);
-  else
-    ::js_RemoveRoot(sJSScriptRuntime, (JSObject **)aPtr);
-
-  return NS_OK;
-}
-
-// static
 PRBool
 nsContentUtils::IsEventAttributeName(nsIAtom* aName, PRInt32 aType)
 {
   const PRUnichar* name = aName->GetUTF16String();
   if (name[0] != 'o' || name[1] != 'n')
     return PR_FALSE;
 
   EventNameMapping mapping;
@@ -5171,23 +5129,16 @@ nsContentUtils::EqualsIgnoreASCIICase(co
       }
     }
   }
 
   return PR_TRUE;
 }
 
 /* static */
-void
-nsAutoGCRoot::Shutdown()
-{
-  NS_IF_RELEASE(sJSRuntimeService);
-}
-
-/* static */
 nsIInterfaceRequestor*
 nsContentUtils::GetSameOriginChecker()
 {
   if (!sSameOriginChecker) {
     sSameOriginChecker = new nsSameOriginChecker();
     NS_IF_ADDREF(sSameOriginChecker);
   }
   return sSameOriginChecker;
@@ -6356,18 +6307,18 @@ nsContentUtils::PlatformToDOMLineBreaks(
                              NS_LITERAL_STRING("\n").get());
 
     // Mac linebreaks: Map any remaining CR to LF:
     aString.ReplaceSubstring(NS_LITERAL_STRING("\r").get(),
                              NS_LITERAL_STRING("\n").get());
   }
 }
 
-already_AddRefed<LayerManager>
-nsContentUtils::LayerManagerForDocument(nsIDocument *aDoc)
+static already_AddRefed<LayerManager>
+LayerManagerForDocumentInternal(nsIDocument *aDoc, bool aRequirePersistent)
 {
   nsIDocument* doc = aDoc;
   nsIDocument* displayDoc = doc->GetDisplayDocument();
   if (displayDoc) {
     doc = displayDoc;
   }
 
   nsIPresShell* shell = doc->GetShell();
@@ -6391,26 +6342,41 @@ nsContentUtils::LayerManagerForDocument(
   }
 
   if (shell) {
     nsIFrame* rootFrame = shell->FrameManager()->GetRootFrame();
     if (rootFrame) {
       nsIWidget* widget =
         nsLayoutUtils::GetDisplayRootFrame(rootFrame)->GetNearestWidget();
       if (widget) {
-        nsRefPtr<LayerManager> manager = widget->GetLayerManager();
+        nsRefPtr<LayerManager> manager =
+          static_cast<nsIWidget_MOZILLA_2_0_BRANCH*>(widget)->
+            GetLayerManager(aRequirePersistent ? nsIWidget_MOZILLA_2_0_BRANCH::LAYER_MANAGER_PERSISTENT : 
+                                                 nsIWidget_MOZILLA_2_0_BRANCH::LAYER_MANAGER_CURRENT);
         return manager.forget();
       }
     }
   }
 
   nsRefPtr<LayerManager> manager = new BasicLayerManager();
   return manager.forget();
 }
 
+already_AddRefed<LayerManager>
+nsContentUtils::LayerManagerForDocument(nsIDocument *aDoc)
+{
+  return LayerManagerForDocumentInternal(aDoc, false);
+}
+
+already_AddRefed<LayerManager>
+nsContentUtils::PersistentLayerManagerForDocument(nsIDocument *aDoc)
+{
+  return LayerManagerForDocumentInternal(aDoc, true);
+}
+
 bool
 nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal)
 {
   if (IsSystemPrincipal(aPrincipal)) {
     return true;
   }
   
   nsCOMPtr<nsIURI> princURI;
--- a/content/base/src/nsDOMAttribute.cpp
+++ b/content/base/src/nsDOMAttribute.cpp
@@ -443,17 +443,17 @@ nsDOMAttribute::Clone(nsINodeInfo *aNode
   NS_ADDREF(*aResult);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMAttribute::CloneNode(PRBool aDeep, nsIDOMNode** aResult)
 {
-  return nsNodeUtils::CloneNodeImpl(this, aDeep, aResult);
+  return nsNodeUtils::CloneNodeImpl(this, aDeep, PR_TRUE, aResult);
 }
 
 NS_IMETHODIMP
 nsDOMAttribute::GetOwnerDocument(nsIDOMDocument** aOwnerDocument)
 {
   return nsINode::GetOwnerDocument(aOwnerDocument);
 }
 
--- a/content/base/src/nsDOMDocumentType.cpp
+++ b/content/base/src/nsDOMDocumentType.cpp
@@ -263,25 +263,26 @@ nsDOMDocumentType::BindToTree(nsIDocumen
     nsCOMPtr<nsINodeInfo> newNodeInfo;
     newNodeInfo = nimgr->GetNodeInfo(mNodeInfo->NameAtom(),
                                      mNodeInfo->GetPrefixAtom(),
                                      mNodeInfo->NamespaceID());
     NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
     mNodeInfo.swap(newNodeInfo);
 
-    JSObject *oldScope = GetWrapper();
-    if (oldScope) {
+    nsCOMPtr<nsIDocument> oldOwnerDoc =
+      do_QueryInterface(nsContentUtils::GetDocumentFromContext());
+    nsIDocument *newOwnerDoc = nimgr->GetDocument();
+    if (oldOwnerDoc && newOwnerDoc) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
 
       JSContext *cx = nsnull;
-      JSObject *newScope = nsnull;
-      nsresult rv = nsContentUtils::GetContextAndScope(nsnull,
-                                                       nimgr->GetDocument(),
-                                                       &cx, &newScope);
+      JSObject *oldScope = nsnull, *newScope = nsnull;
+      nsresult rv = nsContentUtils::GetContextAndScopes(oldOwnerDoc, newOwnerDoc, &cx,
+                                                        &oldScope, &newScope);
       if (cx && xpc) {
         nsISupports *node = NS_ISUPPORTS_CAST(nsIContent*, this);
         nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
         rv = xpc->ReparentWrappedNativeIfFound(cx, oldScope, newScope, node,
                                                getter_AddRefs(oldWrapper));
       }
 
       if (NS_FAILED(rv)) {
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5773,17 +5773,17 @@ NS_IMETHODIMP
 nsDocument::AppendChild(nsIDOMNode* aNewChild, nsIDOMNode** aReturn)
 {
   return nsDocument::InsertBefore(aNewChild, nsnull, aReturn);
 }
 
 NS_IMETHODIMP
 nsDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
 {
-  return nsNodeUtils::CloneNodeImpl(this, aDeep, aReturn);
+  return nsNodeUtils::CloneNodeImpl(this, aDeep, !mCreatingStaticClone, aReturn);
 }
 
 NS_IMETHODIMP
 nsDocument::Normalize()
 {
   for (PRUint32 i = 0; i < mChildren.ChildCount(); ++i) {
     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mChildren.ChildAt(i)));
     node->Normalize();
@@ -6077,25 +6077,27 @@ nsDocument::AdoptNode(nsIDOMNode *aAdopt
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     }
   }
 
   nsIDocument *oldDocument = adoptedNode->GetOwnerDoc();
   PRBool sameDocument = oldDocument == this;
 
   JSContext *cx = nsnull;
+  JSObject *oldScope = nsnull;
   JSObject *newScope = nsnull;
-  if (!sameDocument) {
-    rv = nsContentUtils::GetContextAndScope(oldDocument, this, &cx, &newScope);
+  if (!sameDocument && oldDocument) {
+    rv = nsContentUtils::GetContextAndScopes(oldDocument, this, &cx, &oldScope,
+                                             &newScope);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMArray<nsINode> nodesWithProperties;
   rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nsnull : mNodeInfoManager,
-                          cx, newScope, nodesWithProperties);
+                          cx, oldScope, newScope, nodesWithProperties);
   if (NS_FAILED(rv)) {
     // Disconnect all nodes from their parents, since some have the old document
     // as their ownerDocument and some have this as their ownerDocument.
     BlastSubtreeToPieces(adoptedNode);
 
     if (!sameDocument && oldDocument) {
       PRUint32 count = nodesWithProperties.Count();
       for (PRUint32 j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -208,18 +208,16 @@ nsFrameMessageManager::GetParamsForMessa
   JSAutoRequest ar(ctx);
   JSString* str;
   if (argc && (str = JS_ValueToString(ctx, argv[0])) && str) {
     aMessageName.Assign(nsDependentJSString(str));
   }
 
   if (argc >= 2) {
     jsval v = argv[1];
-    nsAutoGCRoot root(&v, &rv);
-    NS_ENSURE_SUCCESS(rv, JS_FALSE);
     if (JS_TryJSON(ctx, &v)) {
       JS_Stringify(ctx, &v, nsnull, JSVAL_NULL, JSONCreator, &aJSON);
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -359,43 +357,34 @@ nsFrameMessageManager::ReceiveMessage(ns
         NS_ENSURE_STATE(pusher.Push(ctx, PR_FALSE));
 
         JSAutoRequest ar(ctx);
 
         // The parameter for the listener function.
         JSObject* param = JS_NewObject(ctx, NULL, NULL, NULL);
         NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
 
-        nsresult rv;
-        nsAutoGCRoot resultGCRoot(&param, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
-
         jsval targetv;
-        nsAutoGCRoot resultGCRoot2(&targetv, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
         nsContentUtils::WrapNative(ctx,
                                    JS_GetGlobalObject(ctx),
                                    aTarget, &targetv);
 
         // To keep compatibility with e10s message manager,
         // define empty objects array.
         if (!aObjectsArray) {
           // Because we want JS messages to have always the same properties,
           // create array even if len == 0.
           aObjectsArray = JS_NewArrayObject(ctx, 0, NULL);
           if (!aObjectsArray) {
             return false;
           }
         }
-        nsAutoGCRoot arrayGCRoot(&aObjectsArray, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
 
         jsval json = JSVAL_NULL;
-        nsAutoGCRoot root(&json, &rv);
-        if (NS_SUCCEEDED(rv) && !aJSON.IsEmpty()) {
+        if (!aJSON.IsEmpty()) {
           JSONParser* parser = JS_BeginJSONParse(ctx, &json);
           if (parser) {
             JSBool ok = JS_ConsumeJSONText(ctx, parser,
                                            (jschar*)nsString(aJSON).get(),
                                            (uint32)aJSON.Length());
             ok = JS_FinishJSONParse(ctx, parser, JSVAL_NULL) && ok;
             if (!ok) {
               json = JSVAL_NULL;
@@ -412,18 +401,16 @@ nsFrameMessageManager::ReceiveMessage(ns
                           STRING_TO_JSVAL(jsMessage), NULL, NULL, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "sync",
                           BOOLEAN_TO_JSVAL(aSync), NULL, NULL, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "json", json, NULL, NULL, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "objects", OBJECT_TO_JSVAL(aObjectsArray),
                           NULL, NULL, JSPROP_ENUMERATE);
 
         jsval thisValue = JSVAL_VOID;
-        nsAutoGCRoot resultGCRoot3(&thisValue, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
 
         JSAutoEnterCompartment ac;
 
         if (!ac.enter(ctx, object))
           return PR_FALSE;
 
         jsval funval = JSVAL_VOID;
         if (JS_ObjectIsFunction(ctx, object)) {
@@ -449,18 +436,16 @@ nsFrameMessageManager::ReceiveMessage(ns
                           JSVAL_IS_OBJECT(funval) &&
                           !JSVAL_IS_NULL(funval));
           JSObject* funobject = JSVAL_TO_OBJECT(funval);
           NS_ENSURE_STATE(JS_ObjectIsFunction(ctx, funobject));
           thisValue = OBJECT_TO_JSVAL(object);
         }
 
         jsval rval = JSVAL_VOID;
-        nsAutoGCRoot resultGCRoot4(&rval, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
 
         js::AutoValueRooter argv(ctx);
         argv.set(OBJECT_TO_JSVAL(param));
 
         {
           JSAutoEnterCompartment tac;
 
           JSObject* thisObject = JSVAL_TO_OBJECT(thisValue);
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -480,14 +480,14 @@ private:
     return NS_OK;                                                           \
   }                                                                         \
   NS_IMETHOD IsSupported(const nsAString& aFeature,                         \
                       const nsAString& aVersion,                            \
                       PRBool* aReturn) {                                    \
     return nsGenericDOMDataNode::IsSupported(aFeature, aVersion, aReturn);  \
   }                                                                         \
   NS_IMETHOD CloneNode(PRBool aDeep, nsIDOMNode** aReturn) {                \
-    return nsNodeUtils::CloneNodeImpl(this, aDeep, aReturn);                \
+    return nsNodeUtils::CloneNodeImpl(this, aDeep, PR_TRUE, aReturn);       \
   }                                                                         \
   virtual nsGenericDOMDataNode *CloneDataNode(nsINodeInfo *aNodeInfo,       \
                                               PRBool aCloneText) const;
 
 #endif /* nsGenericDOMDataNode_h___ */
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -539,17 +539,17 @@ public:
                                     const nsAString& aLocalName,
                                     nsIDOMNodeList** aReturn);
   NS_IMETHOD HasAttribute(const nsAString& aName, PRBool* aReturn);
   NS_IMETHOD HasAttributeNS(const nsAString& aNamespaceURI,
                             const nsAString& aLocalName,
                             PRBool* aReturn);
   nsresult CloneNode(PRBool aDeep, nsIDOMNode **aResult)
   {
-    return nsNodeUtils::CloneNodeImpl(this, aDeep, aResult);
+    return nsNodeUtils::CloneNodeImpl(this, aDeep, PR_TRUE, aResult);
   }
 
   //----------------------------------------
 
   /**
    * Add a script event listener with the given event handler name
    * (like onclick) and with the value as JS
    * @param aEventName the event listener name
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1784,8 +1784,9 @@ GK_ATOM(_moz_windows_default_theme, "-mo
 GK_ATOM(_moz_mac_graphite_theme, "-moz-mac-graphite-theme")
 GK_ATOM(_moz_windows_compositor, "-moz-windows-compositor")
 GK_ATOM(_moz_windows_classic, "-moz-windows-classic")
 GK_ATOM(_moz_windows_theme, "-moz-windows-theme")
 GK_ATOM(_moz_touch_enabled, "-moz-touch-enabled")
 GK_ATOM(_moz_maemo_classic, "-moz-maemo-classic")
 GK_ATOM(_moz_menubar_drag, "-moz-menubar-drag")
 GK_ATOM(_moz_device_pixel_ratio, "-moz-device-pixel-ratio")
+GK_ATOM(_moz_device_orientation, "-moz-device-orientation")
--- a/content/base/src/nsNameSpaceManager.cpp
+++ b/content/base/src/nsNameSpaceManager.cpp
@@ -54,20 +54,16 @@
 #include "nsIServiceManager.h"
 #include "nsIXTFService.h"
 #include "nsContentUtils.h"
 static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
 #endif
 
 using namespace mozilla::dom;
 
-#ifdef MOZ_SVG
-PRBool NS_SVGEnabled();
-#endif
-
 #define kXMLNSNameSpaceURI "http://www.w3.org/2000/xmlns/"
 #define kXMLNameSpaceURI "http://www.w3.org/XML/1998/namespace"
 #define kXHTMLNameSpaceURI "http://www.w3.org/1999/xhtml"
 #define kXLinkNameSpaceURI "http://www.w3.org/1999/xlink"
 #define kXSLTNameSpaceURI "http://www.w3.org/1999/XSL/Transform"
 #define kXBLNameSpaceURI "http://www.mozilla.org/xbl"
 #define kMathMLNameSpaceURI "http://www.w3.org/1998/Math/MathML"
 #define kRDFNameSpaceURI "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
@@ -239,21 +235,19 @@ NS_NewElement(nsIContent** aResult, PRIn
     return NS_NewXULElement(aResult, aNodeInfo);
   }
 #endif
 #ifdef MOZ_MATHML
   if (aElementType == kNameSpaceID_MathML) {
     return NS_NewMathMLElement(aResult, aNodeInfo);
   }
 #endif
-#ifdef MOZ_SVG
-  if (aElementType == kNameSpaceID_SVG && NS_SVGEnabled()) {
+  if (aElementType == kNameSpaceID_SVG) {
     return NS_NewSVGElement(aResult, aNodeInfo, aFromParser);
   }
-#endif
   if (aElementType == kNameSpaceID_XMLEvents) {
     return NS_NewXMLEventsElement(aResult, aNodeInfo);
   }
 #ifdef MOZ_XTF
   if (aElementType > kNameSpaceID_LastBuiltin) {
     nsIXTFService* xtfService = nsContentUtils::GetXTFService();
     NS_ASSERTION(xtfService, "could not get xtf service");
     if (xtfService &&
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -413,62 +413,64 @@ nsNodeUtils::TraverseUserData(nsINode* a
   }
 
   ownerDoc->PropertyTable(DOM_USER_DATA)->Enumerate(aNode, NoteUserData, &aCb);
   ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->Enumerate(aNode, NoteUserData, &aCb);
 }
 
 /* static */
 nsresult
-nsNodeUtils::CloneNodeImpl(nsINode *aNode, PRBool aDeep, nsIDOMNode **aResult)
+nsNodeUtils::CloneNodeImpl(nsINode *aNode, PRBool aDeep,
+                           PRBool aCallUserDataHandlers,
+                           nsIDOMNode **aResult)
 {
   *aResult = nsnull;
 
   nsCOMPtr<nsIDOMNode> newNode;
   nsCOMArray<nsINode> nodesWithProperties;
   nsresult rv = Clone(aNode, aDeep, nsnull, nodesWithProperties,
                       getter_AddRefs(newNode));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIDocument *ownerDoc = aNode->GetOwnerDoc();
-  if (ownerDoc) {
+  if (ownerDoc && aCallUserDataHandlers) {
     rv = CallUserDataHandlers(nodesWithProperties, ownerDoc,
                               nsIDOMUserDataHandler::NODE_CLONED, PR_TRUE);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   newNode.swap(*aResult);
 
   return NS_OK;
 }
 
 /* static */
 nsresult
 nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
                            nsNodeInfoManager *aNewNodeInfoManager,
-                           JSContext *aCx, JSObject *aNewScope,
+                           JSContext *aCx, JSObject *aOldScope,
+                           JSObject *aNewScope,
                            nsCOMArray<nsINode> &aNodesWithProperties,
                            nsINode *aParent, nsINode **aResult)
 {
   NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aCx,
                   "If cloning or not getting a new nodeinfo we shouldn't "
                   "rewrap");
-  NS_PRECONDITION(!aCx || aNewScope, "Must have new scope");
+  NS_PRECONDITION(!aCx || (aOldScope && aNewScope), "Must have scopes");
   NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT),
                   "Can't insert document or attribute nodes into a parent");
 
   *aResult = nsnull;
 
   // First deal with aNode and walk its attributes (and their children). Then,
   // if aDeep is PR_TRUE, deal with aNode's children (and recurse into their
   // attributes and children).
 
   nsresult rv;
-  JSObject *wrapper;
-  if (aCx && (wrapper = aNode->GetWrapper())) {
+  if (aCx) {
       rv = xpc_MorphSlimWrapper(aCx, aNode);
       NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager;
 
   // aNode.
   nsINodeInfo *nodeInfo = aNode->mNodeInfo;
@@ -577,37 +579,39 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
       if (imageContent)
         imageContent->NotifyOwnerDocumentChanged(oldDoc);
     }
 
     if (elem) {
       elem->RecompileScriptEventListeners();
     }
 
-    if (aCx && wrapper) {
+    if (aCx) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
       if (xpc) {
+        nsWrapperCache *cache;
+        CallQueryInterface(aNode, &cache);
         JSObject *preservedWrapper = nsnull;
 
         // If reparenting moves us to a new compartment, preserving causes
         // problems. In that case, we release ourselves and re-preserve after
         // reparenting so we're sure to have the right JS object preserved.
         // We use a JSObject stack copy of the wrapper to protect it from GC
         // under ReparentWrappedNativeIfFound.
-        if (aNode->PreservingWrapper()) {
-          preservedWrapper = wrapper;
-          nsContentUtils::ReleaseWrapper(aNode, aNode);
+        if (cache && cache->PreservingWrapper()) {
+          preservedWrapper = cache->GetWrapper();
+          nsContentUtils::ReleaseWrapper(aNode, cache);
         }
 
         nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
-        rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode,
+        rv = xpc->ReparentWrappedNativeIfFound(aCx, aOldScope, aNewScope, aNode,
                                                getter_AddRefs(oldWrapper));
 
         if (preservedWrapper) {
-          nsContentUtils::PreserveWrapper(aNode, aNode);
+          nsContentUtils::PreserveWrapper(aNode, cache);
         }
 
         if (NS_FAILED(rv)) {
           aNode->mNodeInfo.swap(nodeInfo);
 
           return rv;
         }
       }
@@ -641,18 +645,18 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
   }
   // XXX End of workaround for broken attribute nodes.
   else if (aDeep || aNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
     // aNode's children.
     PRUint32 i, length = aNode->GetChildCount();
     for (i = 0; i < length; ++i) {
       nsCOMPtr<nsINode> child;
       rv = CloneAndAdopt(aNode->GetChildAt(i), aClone, PR_TRUE, nodeInfoManager,
-                         aCx, aNewScope, aNodesWithProperties, clone,
-                         getter_AddRefs(child));
+                         aCx, aOldScope, aNewScope, aNodesWithProperties,
+                         clone, getter_AddRefs(child));
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // XXX setting document on some nodes not in a document so XBL will bind
   // and chrome won't break. Make XBL bind to document-less nodes!
   // XXXbz Once this is fixed, fix up the asserts in all implementations of
   // BindToTree to assert what they would like to assert, and fix the
--- a/content/base/src/nsNodeUtils.h
+++ b/content/base/src/nsNodeUtils.h
@@ -167,17 +167,17 @@ public:
    * @param aResult *aResult will contain the cloned node.
    */
   static nsresult Clone(nsINode *aNode, PRBool aDeep,
                         nsNodeInfoManager *aNewNodeInfoManager,
                         nsCOMArray<nsINode> &aNodesWithProperties,
                         nsIDOMNode **aResult)
   {
     return CloneAndAdopt(aNode, PR_TRUE, aDeep, aNewNodeInfoManager, nsnull,
-                         nsnull, aNodesWithProperties, aResult);
+                         nsnull, nsnull, aNodesWithProperties, aResult);
   }
 
   /**
    * Walks aNode, its attributes and descendant nodes. If aNewNodeInfoManager is
    * not null, it is used to create new nodeinfos for the nodes. Also reparents
    * the XPConnect wrappers for the nodes in aNewScope if aCx is not null.
    * aNodesWithProperties will be filled with all the nodes that have
    * properties.
@@ -185,26 +185,28 @@ public:
    * @param aNode Node to adopt.
    * @param aNewNodeInfoManager The nodeinfo manager to use to create new
    *                            nodeinfos for aNode and its attributes and
    *                            descendants. May be null if the nodeinfos
    *                            shouldn't be changed.
    * @param aCx Context to use for reparenting the wrappers, or null if no
    *            reparenting should be done. Must be null if aNewNodeInfoManager
    *            is null.
+   * @param aOldScope Old scope for the wrappers. May be null if aCx is null.
    * @param aNewScope New scope for the wrappers. May be null if aCx is null.
    * @param aNodesWithProperties All nodes (from amongst aNode and its
    *                             descendants) with properties.
    */
   static nsresult Adopt(nsINode *aNode, nsNodeInfoManager *aNewNodeInfoManager,
-                        JSContext *aCx, JSObject *aNewScope,
+                        JSContext *aCx, JSObject *aOldScope,
+                        JSObject *aNewScope,
                         nsCOMArray<nsINode> &aNodesWithProperties)
   {
     nsresult rv = CloneAndAdopt(aNode, PR_FALSE, PR_TRUE, aNewNodeInfoManager,
-                                aCx, aNewScope, aNodesWithProperties,
+                                aCx, aOldScope, aNewScope, aNodesWithProperties,
                                 nsnull);
 
     nsMutationGuard::DidMutate();
 
     return rv;
   }
 
   /**
@@ -237,19 +239,21 @@ public:
                                nsCycleCollectionTraversalCallback &aCb);
 
   /**
    * A basic implementation of the DOM cloneNode method. Calls nsINode::Clone to
    * do the actual cloning of the node.
    *
    * @param aNode the node to clone
    * @param aDeep if true all descendants will be cloned too
+   * @param aCallUserDataHandlers if true, user data handlers will be called
    * @param aResult the clone
    */
   static nsresult CloneNodeImpl(nsINode *aNode, PRBool aDeep,
+                                PRBool aCallUserDataHandlers,
                                 nsIDOMNode **aResult);
 
   /**
    * Release the UserData and UserDataHandlers for aNode.
    *
    * @param aNode the node to release the UserData and UserDataHandlers for
    */
   static void UnlinkUserData(nsINode *aNode);
@@ -270,37 +274,39 @@ private:
    *              descendants of the node
    * @param aNewNodeInfoManager The nodeinfo manager to use to create new
    *                            nodeinfos for aNode and its attributes and
    *                            descendants. May be null if the nodeinfos
    *                            shouldn't be changed.
    * @param aCx Context to use for reparenting the wrappers, or null if no
    *            reparenting should be done. Must be null if aClone is PR_TRUE or
    *            if aNewNodeInfoManager is null.
+   * @param aOldScope Old scope for the wrappers. May be null if aCx is null.
    * @param aNewScope New scope for the wrappers. May be null if aCx is null.
    * @param aNodesWithProperties All nodes (from amongst aNode and its
    *                             descendants) with properties. If aClone is
    *                             PR_TRUE every node will be followed by its
    *                             clone.
    * @param aResult If aClone is PR_FALSE then aResult must be null, else
    *                *aResult will contain the cloned node.
    */
   static nsresult CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
                                 nsNodeInfoManager *aNewNodeInfoManager,
-                                JSContext *aCx, JSObject *aNewScope,
+                                JSContext *aCx, JSObject *aOldScope,
+                                JSObject *aNewScope,
                                 nsCOMArray<nsINode> &aNodesWithProperties,
                                 nsIDOMNode **aResult)
   {
     NS_ASSERTION(!aClone == !aResult,
                  "aResult must be null when adopting and non-null when "
                  "cloning");
 
     nsCOMPtr<nsINode> clone;
     nsresult rv = CloneAndAdopt(aNode, aClone, aDeep, aNewNodeInfoManager,
-                                aCx, aNewScope, aNodesWithProperties,
+                                aCx, aOldScope, aNewScope, aNodesWithProperties,
                                 nsnull, getter_AddRefs(clone));
     NS_ENSURE_SUCCESS(rv, rv);
 
     return clone ? CallQueryInterface(clone, aResult) : NS_OK;
   }
 
   /**
    * See above for arguments that aren't described here.
@@ -308,14 +314,15 @@ private:
    * @param aParent If aClone is PR_TRUE the cloned node will be appended to
    *                aParent's children. May be null. If not null then aNode
    *                must be an nsIContent.
    * @param aResult If aClone is PR_TRUE then *aResult will contain the cloned
    *                node.
    */
   static nsresult CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
                                 nsNodeInfoManager *aNewNodeInfoManager,
-                                JSContext *aCx, JSObject *aNewScope,
+                                JSContext *aCx, JSObject *aOldScope,
+                                JSObject *aNewScope,
                                 nsCOMArray<nsINode> &aNodesWithProperties,
                                 nsINode *aParent, nsINode **aResult);
 };
 
 #endif // nsNodeUtils_h___
--- a/content/base/src/nsScriptElement.cpp
+++ b/content/base/src/nsScriptElement.cpp
@@ -41,16 +41,17 @@
 #include "nsContentUtils.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsPresContext.h"
 #include "nsScriptLoader.h"
 #include "nsIParser.h"
 #include "nsAutoPtr.h"
 #include "nsGkAtoms.h"
+#include "nsContentSink.h"
 
 using namespace mozilla::dom;
 
 NS_IMETHODIMP
 nsScriptElement::ScriptAvailable(nsresult aResult,
                                  nsIScriptElement *aElement,
                                  PRBool aIsInline,
                                  nsIURI *aURI,
@@ -157,18 +158,30 @@ nsScriptElement::MaybeProcessScript()
 
   if (mAlreadyStarted || !mDoneAddingChildren || !cont->IsInDoc() ||
       mMalformed || !HasScriptContent()) {
     return NS_OK;
   }
 
   FreezeUriAsyncDefer();
 
-  nsRefPtr<nsScriptLoader> loader = cont->GetOwnerDoc()->ScriptLoader();
   mAlreadyStarted = PR_TRUE;
+
+  nsIDocument* ownerDoc = cont->GetOwnerDoc();
+  nsCOMPtr<nsIParser> parser = ((nsIScriptElement*)this)->GetCreatorParser();
+  if (parser) {
+    nsCOMPtr<nsIDocument> parserDoc =
+        do_QueryInterface(parser->GetContentSink()->GetTarget());
+    if (ownerDoc != parserDoc) {
+      // Willful violation of HTML5 as of 2010-12-01
+      return NS_OK;
+    }
+  }
+
+  nsRefPtr<nsScriptLoader> loader = ownerDoc->ScriptLoader();
   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/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -834,25 +834,31 @@ nsScriptLoader::EvaluateScript(nsScriptL
 {
   nsresult rv = NS_OK;
 
   // We need a document to evaluate scripts.
   if (!mDocument) {
     return NS_ERROR_FAILURE;
   }
 
+  nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->mElement));
+  nsIDocument* ownerDoc = scriptContent->GetOwnerDoc();
+  if (ownerDoc != mDocument) {
+    // Willful violation of HTML5 as of 2010-12-01
+    return NS_ERROR_FAILURE;
+  }
+
   nsPIDOMWindow *pwin = mDocument->GetInnerWindow();
   if (!pwin || !pwin->IsInnerWindow()) {
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(pwin);
   NS_ASSERTION(globalObject, "windows must be global objects");
 
   // Get the script-type to be used by this element.
-  nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->mElement));
   NS_ASSERTION(scriptContent, "no content - what is default script-type?");
   PRUint32 stid = scriptContent ? scriptContent->GetScriptTypeID() :
                                   nsIProgrammingLanguage::JAVASCRIPT;
   // and make sure we are setup for this type of script.
   rv = globalObject->EnsureScriptEnvironment(stid);
   if (NS_FAILED(rv))
     return rv;
 
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -2919,19 +2919,17 @@ NS_IMETHODIMP
 nsWebSocket::Initialize(nsISupports* aOwner,
                         JSContext* aContext,
                         JSObject* aObject,
                         PRUint32 aArgc,
                         jsval* aArgv)
 {
   nsAutoString urlParam, protocolParam;
 
-  PRBool prefEnabled =
-    nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE);
-  if (!prefEnabled) {
+  if (!PrefEnabled()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   if (aArgc != 1 && aArgc != 2) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   JSAutoRequest ar(aContext);
@@ -3099,16 +3097,24 @@ nsWebSocket::CreateAndDispatchCloseEvent
 
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
   rv = privateEvent->SetTrusted(PR_TRUE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
 }
 
+PRBool
+nsWebSocket::PrefEnabled()
+{
+  return nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE) &&
+    nsContentUtils::GetBoolPref("network.websocket.override-security-block",
+                                PR_FALSE);
+}
+
 void
 nsWebSocket::SetReadyState(PRUint16 aNewReadyState)
 {
   nsresult rv;
 
   if (mReadyState == aNewReadyState) {
     return;
   }
@@ -3497,19 +3503,17 @@ nsWebSocket::Init(nsIPrincipal* aPrincip
                   nsPIDOMWindow* aOwnerWindow,
                   const nsAString& aURL,
                   const nsAString& aProtocol)
 {
   nsresult rv;
 
   NS_ENSURE_ARG(aPrincipal);
 
-  PRBool prefEnabled =
-    nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE);
-  if (!prefEnabled) {
+  if (!PrefEnabled()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   mPrincipal = aPrincipal;
   mScriptContext = aScriptContext;
   if (aOwnerWindow) {
     mOwner = aOwnerWindow->IsOuterWindow() ?
       aOwnerWindow->GetCurrentInnerWindow() : aOwnerWindow;
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -98,16 +98,19 @@ public:
   NS_IMETHOD AddEventListener(const nsAString& aType,
                               nsIDOMEventListener *aListener,
                               PRBool aUseCapture,
                               PRBool aWantsUntrusted,
                               PRUint8 optional_argc);
 
   static void ReleaseGlobals();
 
+  // Determine if preferences allow WebSocket
+  static PRBool PrefEnabled();
+
 protected:
   nsresult ParseURL(const nsString& aURL);
   nsresult SetProtocol(const nsString& aProtocol);
   nsresult EstablishConnection();
 
   nsresult CreateAndDispatchSimpleEvent(const nsString& aName);
   nsresult CreateAndDispatchMessageEvent(nsCString *aData);
   nsresult CreateAndDispatchCloseEvent(PRBool aWasClean);
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -420,16 +420,17 @@ include $(topsrcdir)/config/rules.mk
 		test_x-frame-options.html \
 		file_x-frame-options_main.html \
 		file_x-frame-options_page.sjs \
 		test_createHTMLDocument.html \
 		test_bug564047.html \
 		test_bug567350.html \
 		test_bug574596.html \
 		test_bug578096.html \
+		test_bug592366.html \
 		test_bug597345.html \
 		script-1_bug597345.sjs \
 		script-2_bug597345.js \
 		test_bug598877.html \
 		test_bug599588.html \
 		test_bug600466.html \
 		test_bug600468.html \
 		test_bug600471.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug592366.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=592366
+-->
+<head>
+  <title>Test for Bug 592366</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=592366">Mozilla Bug 592366</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe onload='runTest();'></iframe>  
+<iframe onload='runTest();'></iframe>  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+/** Test for Bug 592366 **/
+var iframesToLoad = 2;
+function runTest() {
+  --iframesToLoad;
+  if (iframesToLoad) {
+    return;
+  }
+
+  ok(true, "Obligatory succeeding assertion.");
+
+  var s = document.createElement("script");
+  s.src = "data:text/javascript,parent.ok(false, 'This script should not be executed.');"
+  
+  var iframes = document.getElementsByTagName("iframe");
+
+  iframes[0].contentDocument.body.appendChild(s);
+  iframes[1].contentDocument.body.appendChild(s);
+
+  setTimeout(function() {
+    SimpleTest.finish();
+  }, 500);
+}
+</script>
+</pre>
+</body>
+</html>
+
--- a/content/base/test/test_websocket.html
+++ b/content/base/test/test_websocket.html
@@ -588,29 +588,41 @@ function test22()
   ws.onopen = shouldNotOpen;
   ws.onclose = function(e)
   {
     shouldCloseNotCleanly(e);
     doTest(23);
   };
 }
 
+var domBranch;
+var oldPrefVal;
+
 function finishWSTest()
 {
   for (i = 0; i < all_ws.length; ++i) {
     if (all_ws[i] != shouldNotReceiveCloseEvent &&
         !all_ws[i]._receivedCloseEvent) {
       ok(false, "didn't called close on test " + all_ws[i]._testNumber + "!");
     }
   }
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  domBranch.setBoolPref("override-security-block", oldPrefVal);
   SimpleTest.finish();
 }
 
 function testWebSocket ()
 {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  var prefService =
+      Components.classes["@mozilla.org/preferences-service;1"]
+      .getService(Components.interfaces.nsIPrefService);
+  domBranch = prefService.getBranch("network.websocket.");
+  oldPrefVal = domBranch.getBoolPref("override-security-block");
+  domBranch.setBoolPref("override-security-block", true);
   doTest(first_test);
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 
--- a/content/base/test/test_websocket_hello.html
+++ b/content/base/test/test_websocket_hello.html
@@ -12,32 +12,48 @@
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=472529">Mozilla Bug </a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var ws;
+var oldPrefVal;
+var domBranch;
+
+function finishWSTest() {
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+    domBranch.setBoolPref("override-security-block", oldPrefVal);
+    SimpleTest.finish();
+}
 
 function testWebSocket () {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  var prefService =
+      Components.classes["@mozilla.org/preferences-service;1"]
+      .getService(Components.interfaces.nsIPrefService);
+  domBranch = prefService.getBranch("network.websocket.");
+  oldPrefVal = domBranch.getBoolPref("override-security-block");
+  domBranch.setBoolPref("override-security-block", true);
+
   ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket_hello");
   ws.onopen = function(e) {
     ws.send("data");
   }
   ws.onclose = function(e) {
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
-    SimpleTest.finish();
+    finishWSTest();
   }
   ws.onmessage = function(e) {
     is(e.data, "Hello world!", "Wrong data");
     ws.close();
-    SimpleTest.finish();
+    finishWSTest();
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 <div>
--- a/content/base/test/test_ws_basic_tests.html
+++ b/content/base/test/test_ws_basic_tests.html
@@ -12,45 +12,61 @@
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=472529">Mozilla Bug </a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var ws;
+var oldPrefVal;
+var domBranch;
 
 var params = ["protocol", "resource", "origin", "end"];
 var results = ["test", "/tests/content/base/test/file_ws_basic_tests", "http://mochi.test:8888", "end"];
 
 function forcegc(){
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   Components.utils.forceGC();
   var wu =  window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                   .getInterface(Components.interfaces.nsIDOMWindowUtils);
   wu.garbageCollect();
 }
 
+function finishWSTest() {
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+    domBranch.setBoolPref("override-security-block", oldPrefVal);
+    SimpleTest.finish();
+}
+
 function testWebSocket () {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  var prefService =
+      Components.classes["@mozilla.org/preferences-service;1"]
+      .getService(Components.interfaces.nsIPrefService);
+  domBranch = prefService.getBranch("network.websocket.");
+  oldPrefVal = domBranch.getBoolPref("override-security-block");
+  domBranch.setBoolPref("override-security-block", true);
+
   var url = "ws://mochi.test:8888/tests/content/base/test/file_ws_basic_tests";
   ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_ws_basic_tests", "test");
   is(ws.url, url, "Wrong Websocket.url!");
   ws.onopen = function(e) {
     for (var i = 0; i < params.length; ++i) {
       document.getElementById('log').textContent += "sending " + params[i] + "\n";
       ws.send(params[i]);
     }
   }
   ws.onclose = function(e) {
     is(results.length, 0, "All the messages should have been processed!");
     testWebSocket2();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
-    SimpleTest.finish();
+    finishWSTest();
   }
   ws.onmessage = function(e) {
     document.getElementById('log').textContent += "\n" + e.data;
     is(e.data, results[0], "Unexpected message");
     results.shift();
   }
 }
 
@@ -66,17 +82,17 @@ function testWebSocket2() {
     ws.send("end");
   }
   ws.onclose = function(e) {
     is(messageCount, testCount, "Didn't receive all the messages!");
     testWebSocket3();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
-    SimpleTest.finish();
+    finishWSTest();
   }
   ws.onmessage = function(e) {
     ++messageCount;
     is(e.data, testMessage + messageCount, "Wrong message");
     document.getElementById('log').textContent = messageCount;
     if (messageCount == testCount) {
       this.onmessage = null;
     }
@@ -96,17 +112,17 @@ function testWebSocket3() {
     ws.send("end");
   }
   ws.onclose = function(e) {
     is(messageCount, testCount, "Didn't receive all the messages!");
     testWebSocket4();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
-    SimpleTest.finish();
+    finishWSTest();
   }
   ws.onmessage = function(e) {
     forcegc(); // Do something evil, call cycle collector a lot.
     ++messageCount;
     is(e.data, testMessage + messageCount, "Wrong message");
     document.getElementById('log').textContent = messageCount;
     if (messageCount == testCount) {
       this.onmessage = null;
@@ -123,17 +139,17 @@ function testWebSocket4() {
   }
   ws.onclose = function(e) {
     is(this, ws, "'this' should point to the WebSocket. (2)");
     //ok(e.wasClean, "Connection should have closed cleanly.");
     testWebSocket5();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
-    SimpleTest.finish();
+    finishWSTest();
   }
   ws.onmessage = function(e) {
     is(this, ws, "'this' should point to the WebSocket. (3)");
     is(e.data, longString, "Didn't get the huge message back!");
     document.getElementById('log').textContent += "\nReceived the huge message";
     this.close();
   }
 }
@@ -144,21 +160,21 @@ function testWebSocket5() {
     this.close();
   }
   ws.onclose = function(e) {
     //ok(e.wasClean, "Connection should have closed cleanly.");
     is(this.bufferedAmount, 0, "Shouldn't have anything buffered");
     var msg = "some data";
     this.send(msg);
     ok(this.bufferedAmount, msg.length, "Should have some data buffered");
-    SimpleTest.finish();
+    finishWSTest();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
-    SimpleTest.finish();
+    finishWSTest();
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 <pre id="log">
 </pre>
--- a/content/canvas/src/CustomQS_WebGL.h
+++ b/content/canvas/src/CustomQS_WebGL.h
@@ -902,33 +902,37 @@ nsIDOMWebGLRenderingContext_VertexAttrib
 static inline void FASTCALL
 helper_nsIDOMWebGLRenderingContext_Uniform_x_iv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj,
                                                       JSObject *arg, int nElements)
 {
     XPC_QS_ASSERT_CONTEXT_OK(cx);
 
     nsIDOMWebGLRenderingContext *self;
     xpc_qsSelfRef selfref;
-    xpc_qsArgValArray<3> vp(cx);
-    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, &vp.array[0], nsnull)) {
+    js::Anchor<jsval> self_anchor;
+    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr,
+                          &self_anchor.get(), nsnull)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     if (!arg) {
         xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformNiv");
         js_SetTraceableNativeFailed(cx);
     }
 
     js::AutoValueRooter obj_tvr(cx);
 
     nsIWebGLUniformLocation *location;
     xpc_qsSelfRef location_selfref;
+    js::Anchor<jsval> location_anchor;
     nsresult rv_convert_arg0
-        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location, &location_selfref.ptr, &vp.array[1], nsnull);
+        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location,
+                           &location_selfref.ptr, &location_anchor.get(),
+                           nsnull);
     if (NS_FAILED(rv_convert_arg0)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     js::TypedArray *wa = 0;
 
     if (helper_isInt32Array(arg)) {
@@ -970,33 +974,37 @@ helper_nsIDOMWebGLRenderingContext_Unifo
 static inline void FASTCALL
 helper_nsIDOMWebGLRenderingContext_Uniform_x_fv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj,
                                                       JSObject *arg, int nElements)
 {
     XPC_QS_ASSERT_CONTEXT_OK(cx);
 
     nsIDOMWebGLRenderingContext *self;
     xpc_qsSelfRef selfref;
-    xpc_qsArgValArray<3> vp(cx);
-    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, &vp.array[0], nsnull)) {
+    js::Anchor<jsval> self_anchor;
+    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr,
+                          &self_anchor.get(), nsnull)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     if (!arg) {
         xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformNfv");
         js_SetTraceableNativeFailed(cx);
     }
 
     js::AutoValueRooter obj_tvr(cx);
 
     nsIWebGLUniformLocation *location;
     xpc_qsSelfRef location_selfref;
+    js::Anchor<jsval> location_anchor;
     nsresult rv_convert_arg0
-        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location, &location_selfref.ptr, &vp.array[1], nsnull);
+        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location,
+                           &location_selfref.ptr, &location_anchor.get(),
+                           nsnull);
     if (NS_FAILED(rv_convert_arg0)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     js::TypedArray *wa = 0;
 
     if (helper_isFloat32Array(arg)) {
@@ -1040,33 +1048,37 @@ helper_nsIDOMWebGLRenderingContext_Unifo
 static inline void FASTCALL
 helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj,
                                                             JSBool transpose, JSObject *arg, int nElements)
 {
     XPC_QS_ASSERT_CONTEXT_OK(cx);
 
     nsIDOMWebGLRenderingContext *self;
     xpc_qsSelfRef selfref;
-    xpc_qsArgValArray<4> vp(cx);
-    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, &vp.array[0], nsnull)) {
+    js::Anchor<jsval> self_anchor;
+    if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr,
+                          &self_anchor.get(), nsnull)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     if (!arg) {
         xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformMatrixNfv");
         js_SetTraceableNativeFailed(cx);
     }
 
     js::AutoValueRooter obj_tvr(cx);
 
     nsIWebGLUniformLocation *location;
     xpc_qsSelfRef location_selfref;
+    js::Anchor<jsval> location_anchor;
     nsresult rv_convert_arg0
-        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location, &location_selfref.ptr, &vp.array[1], nsnull);
+        = xpc_qsUnwrapThis(cx, locationobj, nsnull, &location,
+                           &location_selfref.ptr, &location_anchor.get(),
+                           nsnull);
     if (NS_FAILED(rv_convert_arg0)) {
         js_SetTraceableNativeFailed(cx);
         return;
     }
 
     js::TypedArray *wa = 0;
 
     if (helper_isFloat32Array(arg)) {
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -1607,19 +1607,21 @@ protected:
             return;
 
         PRBool initializeColorBuffer = mColorAttachment.HasUninitializedRenderbuffer();
         PRBool initializeDepthBuffer = mDepthAttachment.HasUninitializedRenderbuffer() ||
                                        mDepthStencilAttachment.HasUninitializedRenderbuffer();
         PRBool initializeStencilBuffer = mStencilAttachment.HasUninitializedRenderbuffer() ||
                                          mDepthStencilAttachment.HasUninitializedRenderbuffer();
 
-        realGLboolean savedColorMask[] = {0}, savedDepthMask = 0;
+        realGLboolean savedColorMask[4] = {0};
+        realGLboolean savedDepthMask = 0;
         GLuint savedStencilMask = 0;
-        GLfloat savedColorClearValue[] = {0.f}, savedDepthClearValue = 0.f;
+        GLfloat savedColorClearValue[4] = {0.f};
+        GLfloat savedDepthClearValue = 0.f;
         GLint savedStencilClearValue = 0;
         GLuint clearBits = 0;
 
         realGLboolean wasScissorTestEnabled = mContext->gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST);
         mContext->gl->fDisable(LOCAL_GL_SCISSOR_TEST);
 
         realGLboolean wasDitherEnabled = mContext->gl->fIsEnabled(LOCAL_GL_DITHER);
         mContext->gl->fDisable(LOCAL_GL_DITHER);
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -1073,17 +1073,18 @@ nsCanvasRenderingContext2D::SetDimension
             nsCOMPtr<nsIContent> content =
                 do_QueryInterface(static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement));
             nsIDocument* ownerDoc = nsnull;
             if (content)
                 ownerDoc = content->GetOwnerDoc();
             nsRefPtr<LayerManager> layerManager = nsnull;
 
             if (ownerDoc)
-              layerManager = nsContentUtils::LayerManagerForDocument(ownerDoc);
+              layerManager =
+                nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
 
             if (layerManager) {
               surface = layerManager->CreateOptimalSurface(gfxIntSize(width, height), format);
             } else {
               surface = gfxPlatform::GetPlatform()->
                 CreateOffscreenSurface(gfxIntSize(width, height), gfxASurface::ContentFromFormat(format));
             }
         }
@@ -2122,17 +2123,17 @@ nsCanvasRenderingContext2D::ArcTo(float 
         mThebes->NegativeArc(gfxPoint(cx, cy), radius, angle0, angle1);
     else
         mThebes->Arc(gfxPoint(cx, cy), radius, angle0, angle1);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsCanvasRenderingContext2D::Arc(float x, float y, float r, float startAngle, float endAngle, int ccw)
+nsCanvasRenderingContext2D::Arc(float x, float y, float r, float startAngle, float endAngle, PRBool ccw)
 {
     if (!FloatValidate(x,y,r,startAngle,endAngle))
         return NS_ERROR_DOM_SYNTAX_ERR;
 
     gfxPoint p(x,y);
 
     if (ccw)
         mThebes->NegativeArc(p, r, startAngle, endAngle);
--- a/content/events/src/nsEventListenerService.cpp
+++ b/content/events/src/nsEventListenerService.cpp
@@ -122,67 +122,74 @@ nsEventListenerInfo::GetJSVal(jsval* aJS
   return PR_FALSE;
 }
 
 NS_IMETHODIMP
 nsEventListenerInfo::ToSource(nsAString& aResult)
 {
   aResult.SetIsVoid(PR_TRUE);
 
-  nsresult rv;
-  jsval v = JSVAL_NULL;
-  nsAutoGCRoot root(&v, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (GetJSVal(&v)) {
-    nsCOMPtr<nsIThreadJSContextStack> stack =
-      nsContentUtils::ThreadJSContextStack();
-    if (stack) {
-      JSContext* cx = nsnull;
-      stack->GetSafeJSContext(&cx);
-      if (cx && NS_SUCCEEDED(stack->Push(cx))) {
-        {
-          // Extra block to finish the auto request before calling pop
-          JSAutoRequest ar(cx);
+  nsCOMPtr<nsIThreadJSContextStack> stack =
+    nsContentUtils::ThreadJSContextStack();
+  if (stack) {
+    JSContext* cx = nsnull;
+    stack->GetSafeJSContext(&cx);
+    if (cx && NS_SUCCEEDED(stack->Push(cx))) {
+      {
+        // Extra block to finish the auto request before calling pop
+        JSAutoRequest ar(cx);
+        jsval v = JSVAL_NULL;
+        if (GetJSVal(&v)) {
           JSString* str = JS_ValueToSource(cx, v);
           if (str) {
             aResult.Assign(nsDependentJSString(str));
           }
         }
-        stack->Pop(&cx);
       }
+      stack->Pop(&cx);
     }
   }
-
+  
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEventListenerInfo::GetDebugObject(nsISupports** aRetVal)
 {
   *aRetVal = nsnull;
 
 #ifdef MOZ_JSDEBUGGER
   nsresult rv = NS_OK;
-  jsval v = JSVAL_NULL;
-  nsAutoGCRoot root(&v, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (GetJSVal(&v)) {
-    nsCOMPtr<jsdIDebuggerService> jsd =
-      do_GetService("@mozilla.org/js/jsd/debugger-service;1", &rv);
-    NS_ENSURE_SUCCESS(rv, NS_OK);
+  nsCOMPtr<jsdIDebuggerService> jsd =
+    do_GetService("@mozilla.org/js/jsd/debugger-service;1", &rv);
+  NS_ENSURE_SUCCESS(rv, NS_OK);
+  
+  PRBool isOn = PR_FALSE;
+  jsd->GetIsOn(&isOn);
+  NS_ENSURE_TRUE(isOn, NS_OK);
 
-    PRBool isOn = PR_FALSE;
-    jsd->GetIsOn(&isOn);
-    NS_ENSURE_TRUE(isOn, NS_OK);
+  nsCOMPtr<nsIThreadJSContextStack> stack =
+    nsContentUtils::ThreadJSContextStack();
+  if (stack) {
+    JSContext* cx = nsnull;
+    stack->GetSafeJSContext(&cx);
+    if (cx && NS_SUCCEEDED(stack->Push(cx))) {
+      {
+        // Extra block to finish the auto request before calling pop
+        JSAutoRequest ar(cx);
 
-    nsCOMPtr<jsdIValue> jsdValue;
-    jsd->WrapJSValue(v, getter_AddRefs(jsdValue));
-    *aRetVal = jsdValue.forget().get();
-    return NS_OK;
+        jsval v = JSVAL_NULL;
+        if (GetJSVal(&v)) {
+          nsCOMPtr<jsdIValue> jsdValue;
+          jsd->WrapJSValue(v, getter_AddRefs(jsdValue));
+          *aRetVal = jsdValue.forget().get();
+          return NS_OK;
+        }
+      }
+    }
   }
 #endif
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEventListenerService::GetListenerInfoFor(nsIDOMEventTarget* aEventTarget,
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -582,21 +582,29 @@ nsTextStateManager::ContentRemoved(nsIDo
     nsContentUtils::AddScriptRunner(
         new TextChangeEvent(mWidget, offset, offset + childOffset, offset));
 }
 
 static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
                                     nsIContent* aContent)
 {
   if (aContent) {
-    nsINode* root = nsnull;
-    nsINode* node = aContent;
-    while (node && node->IsEditable()) {
-      root = node;
-      node = node->GetParent();
+    nsIContent* root = nsnull;
+    nsIContent* content = aContent;
+    while (content && content->IntrinsicState().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
+      root = content;
+      content = content->GetParent();
+    }
+    if (!root) {
+      NS_ASSERTION(content, "We should have a content node here");
+      // See if the document is editable
+      nsIDocument* doc = content->GetCurrentDoc();
+      if (doc && doc->IsEditable()) {
+        return doc;
+      }
     }
     return root;
   }
   if (aPresContext) {
     nsIDocument* document = aPresContext->Document();
     if (document && document->IsEditable())
       return document;
   }
@@ -666,17 +674,18 @@ nsIMEStateManager::OnTextStateFocus(nsPr
   }
   return NS_OK;
 }
 
 nsresult
 nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel,
                                             nsIContent** aRoot)
 {
-  if (!sTextStateObserver || !sTextStateObserver->mEditableNode)
+  if (!sTextStateObserver || !sTextStateObserver->mEditableNode ||
+      !sTextStateObserver->mSel)
     return NS_ERROR_NOT_AVAILABLE;
 
   NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent,
                "uninitialized text state observer");
   NS_ADDREF(*aSel = sTextStateObserver->mSel);
   NS_ADDREF(*aRoot = sTextStateObserver->mRootContent);
   return NS_OK;
 }
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -2204,17 +2204,18 @@ ImageContainer* nsHTMLMediaElement::GetI
     return nsnull;
 
   // Only video frames need an image container.
   nsCOMPtr<nsIDOMHTMLVideoElement> video =
     do_QueryInterface(static_cast<nsIContent*>(this));
   if (!video)
     return nsnull;
 
-  nsRefPtr<LayerManager> manager = nsContentUtils::LayerManagerForDocument(GetOwnerDoc());
+  nsRefPtr<LayerManager> manager =
+    nsContentUtils::PersistentLayerManagerForDocument(GetOwnerDoc());
   if (!manager)
     return nsnull;
 
   mImageContainer = manager->CreateImageContainer();
   return mImageContainer;
 }
 
 nsresult nsHTMLMediaElement::DispatchAudioAvailableEvent(float* aFrameBuffer,
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/576612-1.html
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+function boom()
+{
+
+  var v = document.getElementById("v");
+  v.src = "data:text/plain,_";
+  document.documentElement.appendChild(v);
+
+}
+</script>
+</head>
+<body onload="boom();"><video id="v" src="data:video/ogg;codecs=&quot;theora,vorbis&quot;,1"></video></body>
+</html>
--- a/content/media/test/crashtests/crashtests.list
+++ b/content/media/test/crashtests/crashtests.list
@@ -2,8 +2,9 @@ load 459439-1.html
 load 466607-1.html
 load 466945-1.html
 load 468763-1.html
 load 474744-1.html
 HTTP load 481136-1.html # needs to be HTTP to recognize the ogg as an audio file?
 load 493915-1.html
 load 495794-1.html
 load 492286-1.xhtml
+load 576612-1.html
--- a/content/smil/nsSMILAnimationFunction.cpp
+++ b/content/smil/nsSMILAnimationFunction.cpp
@@ -79,26 +79,27 @@ nsAttrValue::EnumTable nsSMILAnimationFu
 // Any negative number should be fine as a sentinel here,
 // because valid distances are non-negative.
 #define COMPUTE_DISTANCE_ERROR (-1)
 
 //----------------------------------------------------------------------
 // Constructors etc.
 
 nsSMILAnimationFunction::nsSMILAnimationFunction()
-  : mIsActive(PR_FALSE),
+  : mSampleTime(-1),
+    mRepeatIteration(0),
+    mBeginTime(LL_MININT),
+    mAnimationElement(nsnull),
+    mErrorFlags(0),
+    mIsActive(PR_FALSE),
     mIsFrozen(PR_FALSE),
-    mSampleTime(-1),
-    mRepeatIteration(0),
     mLastValue(PR_FALSE),
     mHasChanged(PR_TRUE),
     mValueNeedsReparsingEverySample(PR_FALSE),
-    mBeginTime(LL_MININT),
-    mAnimationElement(nsnull),
-    mErrorFlags(0)
+    mPrevSampleWasSingleValueAnimation(PR_FALSE)
 {
 }
 
 void
 nsSMILAnimationFunction::SetAnimationElement(
     nsISMILAnimationElement* aAnimationElement)
 {
   mAnimationElement = aAnimationElement;
@@ -171,21 +172,27 @@ nsSMILAnimationFunction::UnsetAttr(nsIAt
   return foundMatch;
 }
 
 void
 nsSMILAnimationFunction::SampleAt(nsSMILTime aSampleTime,
                                   const nsSMILTimeValue& aSimpleDuration,
                                   PRUint32 aRepeatIteration)
 {
-  if (mHasChanged || mLastValue || mSampleTime != aSampleTime ||
-      mSimpleDuration != aSimpleDuration ||
-      mRepeatIteration != aRepeatIteration) {
-    mHasChanged = PR_TRUE;
-  }
+  // * Update mHasChanged ("Might this sample be different from prev one?")
+  // Were we previously sampling a fill="freeze" final val? (We're not anymore.)
+  mHasChanged |= mLastValue;
+
+  // Are we sampling at a new point in simple duration? And does that matter?
+  mHasChanged |=
+    (mSampleTime != aSampleTime || mSimpleDuration != aSimpleDuration) &&
+    !IsValueFixedForSimpleDuration();
+
+  // Are we on a new repeat and accumulating across repeats?
+  mHasChanged |= (mRepeatIteration != aRepeatIteration) && GetAccumulate();
 
   mSampleTime       = aSampleTime;
   mSimpleDuration   = aSimpleDuration;
   mRepeatIteration  = aRepeatIteration;
   mLastValue        = PR_FALSE;
 }
 
 void
@@ -218,16 +225,17 @@ nsSMILAnimationFunction::Inactivate(PRBo
   mHasChanged = PR_TRUE;
 }
 
 void
 nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr,
                                        nsSMILValue& aResult)
 {
   mHasChanged = PR_FALSE;
+  mPrevSampleWasSingleValueAnimation = PR_FALSE;
 
   // Skip animations that are inactive or in error
   if (!IsActiveOrFrozen() || mErrorFlags != 0)
     return;
 
   // Get the animation values
   nsSMILValueArray values;
   nsresult rv = GetValues(aSMILAttr, values);
@@ -255,16 +263,17 @@ nsSMILAnimationFunction::ComposeResult(c
     return;
 
   nsSMILValue result;
 
   if (values.Length() == 1 && !IsToAnimation()) {
 
     // Single-valued animation
     result = values[0];
+    mPrevSampleWasSingleValueAnimation = PR_TRUE;
 
   } else if (mLastValue) {
 
     // Sampling last value
     const nsSMILValue& last = values[values.Length() - 1];
     result = last;
 
     // See comment in AccumulateResult: to-animation does not accumulate
@@ -915,16 +924,23 @@ nsSMILAnimationFunction::CheckKeySplines
       (IsToAnimation() && splineSpecs != 1)) {
     SetKeySplinesErrorFlag(PR_TRUE);
     return;
   }
 
   SetKeySplinesErrorFlag(PR_FALSE);
 }
 
+PRBool
+nsSMILAnimationFunction::IsValueFixedForSimpleDuration() const
+{
+  return mSimpleDuration.IsIndefinite() ||
+    (!mHasChanged && mPrevSampleWasSingleValueAnimation);
+}
+
 //----------------------------------------------------------------------
 // Property getters
 
 PRBool
 nsSMILAnimationFunction::GetAccumulate() const
 {
   const nsAttrValue* value = GetAttr(nsGkAtoms::accumulate);
   if (!value)
--- a/content/smil/nsSMILAnimationFunction.h
+++ b/content/smil/nsSMILAnimationFunction.h
@@ -339,16 +339,20 @@ protected:
   void         CheckKeySplines(PRUint32 aNumValues);
 
   virtual PRBool IsToAnimation() const {
     return !HasAttr(nsGkAtoms::values) &&
             HasAttr(nsGkAtoms::to) &&
            !HasAttr(nsGkAtoms::from);
   }
 
+  // Returns PR_TRUE if we know our composited value won't change over the
+  // simple duration of this animation (for a fixed base value).
+  virtual PRBool IsValueFixedForSimpleDuration() const;
+
   inline PRBool IsAdditive() const {
     /*
      * Animation is additive if:
      *
      * (1) additive = "sum" (GetAdditive() == true), or
      * (2) it is 'by animation' (by is set, from and values are not)
      *
      * Although animation is not additive if it is 'to animation'
@@ -404,30 +408,24 @@ protected:
 
   static nsAttrValue::EnumTable sAdditiveTable[];
   static nsAttrValue::EnumTable sCalcModeTable[];
   static nsAttrValue::EnumTable sAccumulateTable[];
 
   nsTArray<double>              mKeyTimes;
   nsTArray<nsSMILKeySpline>     mKeySplines;
 
-  PRPackedBool                  mIsActive;
-  PRPackedBool                  mIsFrozen;
-
   // These are the parameters provided by the previous sample. Currently we
   // perform lazy calculation. That is, we only calculate the result if and when
   // instructed by the compositor. This allows us to apply the result directly
   // to the animation value and allows the compositor to filter out functions
   // that it determines will not contribute to the final result.
   nsSMILTime                    mSampleTime; // sample time within simple dur
   nsSMILTimeValue               mSimpleDuration;
   PRUint32                      mRepeatIteration;
-  PRPackedBool                  mLastValue;
-  PRPackedBool                  mHasChanged;
-  PRPackedBool                  mValueNeedsReparsingEverySample;
 
   nsSMILTime                    mBeginTime; // document time
 
   // The owning animation element. This is used for sorting based on document
   // position and for fetching attribute values stored in the element.
   // Raw pointer is OK here, because this nsSMILAnimationFunction can't outlive
   // its owning animation element.
   nsISMILAnimationElement*      mAnimationElement;
@@ -461,11 +459,19 @@ protected:
   // @see
   // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#FromToByAndAdditive
   nsSMILValue                   mFrozenValue;
 
   // Allows us to check whether an animation function has changed target from
   // sample to sample (because if neither target nor animated value have
   // changed, we don't have to do anything).
   nsSMILWeakTargetIdentifier    mLastTarget;
+
+  // Boolean flags
+  PRPackedBool                  mIsActive:1;
+  PRPackedBool                  mIsFrozen:1;
+  PRPackedBool                  mLastValue:1;
+  PRPackedBool                  mHasChanged:1;
+  PRPackedBool                  mValueNeedsReparsingEverySample:1;
+  PRPackedBool                  mPrevSampleWasSingleValueAnimation:1;
 };
 
 #endif // NS_SMILANIMATIONFUNCTION_H_
--- a/content/smil/nsSMILSetAnimationFunction.h
+++ b/content/smil/nsSMILSetAnimationFunction.h
@@ -76,16 +76,21 @@ public:
 protected:
   // Although <set> animation might look like to-animation, unlike to-animation,
   // it never interpolates values.
   // Returning PR_FALSE here will mean this animation function gets treated as
   // a single-valued function and no interpolation will be attempted.
   NS_OVERRIDE virtual PRBool IsToAnimation() const {
     return PR_FALSE;
   }
+
+  // <set> applies the exact same value across the simple duration.
+  NS_OVERRIDE virtual PRBool IsValueFixedForSimpleDuration() 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;
 };
--- a/content/svg/content/src/DOMSVGPathSeg.h
+++ b/content/svg/content/src/DOMSVGPathSeg.h
@@ -77,17 +77,19 @@ class DOMSVGPathSeg : public nsIDOMSVGPa
 {
 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.
+   * Unlike the other list classes, we hide our ctor (because no one should be
+   * creating instances of this class directly). This factory method in exposed
+   * instead to take care of creating 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()!
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGPoint.cpp
@@ -0,0 +1,208 @@
+/* -*- 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 "DOMSVGPoint.h"
+#include "DOMSVGPointList.h"
+#include "SVGPoint.h"
+#include "SVGAnimatedPointList.h"
+#include "nsSVGElement.h"
+#include "nsIDOMSVGPoint.h"
+#include "nsDOMError.h"
+#include "nsIDOMSVGMatrix.h"
+
+// See the architecture comment in DOMSVGPointList.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(DOMSVGPoint)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGPoint)
+  // We may not belong to a list, so we must null check tmp->mList.
+  if (tmp->mList) {
+    tmp->mList->mItems[tmp->mListIndex] = nsnull;
+  }
+NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGPoint)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPoint)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPoint)
+
+DOMCI_DATA(SVGPoint, DOMSVGPoint)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPoint)
+  NS_INTERFACE_MAP_ENTRY(DOMSVGPoint) // pseudo-interface
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPoint)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGPoint)
+NS_INTERFACE_MAP_END
+
+
+NS_IMETHODIMP
+DOMSVGPoint::GetX(float* aX)
+{
+#ifdef MOZ_SMIL
+  if (mIsAnimValItem && HasOwner()) {
+    Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
+  }
+#endif
+  *aX = HasOwner() ? InternalItem().mX : mPt.mX;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPoint::SetX(float aX)
+{
+  if (mIsAnimValItem || mIsReadonly) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  NS_ENSURE_FINITE(aX, NS_ERROR_ILLEGAL_VALUE);
+
+  if (HasOwner()) {
+    InternalItem().mX = aX;
+    Element()->DidChangePointList(PR_TRUE);
+#ifdef MOZ_SMIL
+    if (mList->AttrIsAnimating()) {
+      Element()->AnimationNeedsResample();
+    }
+#endif
+    return NS_OK;
+  }
+  mPt.mX = aX;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPoint::GetY(float* aY)
+{
+#ifdef MOZ_SMIL
+  if (mIsAnimValItem && HasOwner()) {
+    Element()->FlushAnimations(); // May make HasOwner() == PR_FALSE
+  }
+#endif
+  *aY = HasOwner() ? InternalItem().mY : mPt.mY;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPoint::SetY(float aY)
+{
+  if (mIsAnimValItem || mIsReadonly) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  NS_ENSURE_FINITE(aY, NS_ERROR_ILLEGAL_VALUE);
+
+  if (HasOwner()) {
+    InternalItem().mY = aY;
+    Element()->DidChangePointList(PR_TRUE);
+#ifdef MOZ_SMIL
+    if (mList->AttrIsAnimating()) {
+      Element()->AnimationNeedsResample();
+    }
+#endif
+    return NS_OK;
+  }
+  mPt.mY = aY;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPoint::MatrixTransform(nsIDOMSVGMatrix *matrix,
+                             nsIDOMSVGPoint **_retval)
+{
+  if (!matrix)
+    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
+
+  float a, b, c, d, e, f;
+  matrix->GetA(&a);
+  matrix->GetB(&b);
+  matrix->GetC(&c);
+  matrix->GetD(&d);
+  matrix->GetE(&e);
+  matrix->GetF(&f);
+
+  float x = HasOwner() ? InternalItem().mX : mPt.mX;
+  float y = HasOwner() ? InternalItem().mY : mPt.mY;
+
+  NS_ADDREF(*_retval = new DOMSVGPoint(a*x + c*y + e, b*x + d*y + f));
+  return NS_OK;
+}
+
+void
+DOMSVGPoint::InsertingIntoList(DOMSVGPointList *aList,
+                               PRUint32 aListIndex,
+                               PRBool aIsAnimValItem)
+{
+  NS_ABORT_IF_FALSE(!HasOwner(), "Inserting item that already has an owner");
+
+  mList = aList;
+  mListIndex = aListIndex;
+  mIsReadonly = PR_FALSE;
+  mIsAnimValItem = aIsAnimValItem;
+
+  NS_ABORT_IF_FALSE(IndexIsValid(), "Bad index for DOMSVGPoint!");
+}
+
+void
+DOMSVGPoint::RemovingFromList()
+{
+  mPt = InternalItem();
+  mList = nsnull;
+  NS_ABORT_IF_FALSE(!mIsReadonly, "mIsReadonly set for list");
+  mIsAnimValItem = PR_FALSE;
+}
+
+SVGPoint&
+DOMSVGPoint::InternalItem()
+{
+  return mList->InternalList().mItems[mListIndex];
+}
+
+#ifdef DEBUG
+PRBool
+DOMSVGPoint::IndexIsValid()
+{
+  return mListIndex < mList->InternalList().Length();
+}
+#endif
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGPoint.h
@@ -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 ***** */
+
+#ifndef MOZILLA_DOMSVGPOINT_H__
+#define MOZILLA_DOMSVGPOINT_H__
+
+#include "nsIDOMSVGPoint.h"
+#include "DOMSVGPointList.h"
+#include "SVGPoint.h"
+#include "gfxPoint.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsAutoPtr.h"
+
+class nsSVGElement;
+
+// We make DOMSVGPoint a pseudo-interface to allow us to QI to it in order to
+// check that the objects that scripts pass to DOMSVGPointList methods are
+// our *native* point objects.
+//
+// {d6b6c440-af8d-40ee-856b-02a317cab275}
+#define MOZILLA_DOMSVGPOINT_IID \
+  { 0xd6b6c440, 0xaf8d, 0x40ee, \
+    { 0x85, 0x6b, 0x02, 0xa3, 0x17, 0xca, 0xb2, 0x75 } }
+
+namespace mozilla {
+
+/**
+ * Class DOMSVGPoint
+ *
+ * This class creates the DOM objects that wrap internal SVGPoint objects that
+ * are in an SVGPointList. It is also used to create the objects returned by
+ * SVGSVGElement.createSVGPoint() and other functions that return DOM SVGPoint
+ * objects.
+ *
+ * See the architecture comment in DOMSVGPointList.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.
+ */
+class DOMSVGPoint : public nsIDOMSVGPoint
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGPOINT_IID)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGPoint)
+  NS_DECL_NSIDOMSVGPOINT
+
+  /**
+   * Generic ctor for DOMSVGPoint objects that are created for an attribute.
+   */
+  DOMSVGPoint(DOMSVGPointList *aList,
+              PRUint32 aListIndex,
+              PRBool aIsAnimValItem)
+    : mList(aList)
+    , mListIndex(aListIndex)
+    , mIsReadonly(PR_FALSE)
+    , mIsAnimValItem(aIsAnimValItem)
+  {
+    // These shifts are in sync with the members.
+    NS_ABORT_IF_FALSE(aList &&
+                      aListIndex < (1U << 30), "bad arg");
+
+    NS_ABORT_IF_FALSE(IndexIsValid(), "Bad index for DOMSVGPoint!");
+  }
+
+  DOMSVGPoint(const DOMSVGPoint *aPt = nsnull)
+    : mList(nsnull)
+    , mListIndex(0)
+    , mIsReadonly(PR_FALSE)
+    , mIsAnimValItem(PR_FALSE)
+  {
+    if (aPt) {
+      mPt = aPt->ToSVGPoint();
+    }
+  }
+
+  DOMSVGPoint(float aX, float aY)
+    : mList(nsnull)
+    , mListIndex(0)
+    , mIsReadonly(PR_FALSE)
+    , mIsAnimValItem(PR_FALSE)
+  {
+    mPt.mX = aX;
+    mPt.mY = aY;
+  }
+
+  DOMSVGPoint(const gfxPoint &aPt)
+    : mList(nsnull)
+    , mListIndex(0)
+    , mIsReadonly(PR_FALSE)
+    , mIsAnimValItem(PR_FALSE)
+  {
+    mPt.mX = float(aPt.x);
+    mPt.mY = float(aPt.y);
+    NS_ASSERTION(NS_FloatIsFinite(mPt.mX) && NS_FloatIsFinite(mPt.mX),
+                 "DOMSVGPoint coords are not finite");
+  }
+
+
+  ~DOMSVGPoint() {
+    // Our mList's weak ref to us must be nulled out when we die. If GC has
+    // unlinked us using the cycle collector code, then that has already
+    // happened, and mList is null.
+    if (mList) {
+      mList->mItems[mListIndex] = nsnull;
+    }
+  }
+
+  /**
+   * Create an unowned copy of this object. The caller is responsible for the
+   * first AddRef()!
+   */
+  DOMSVGPoint* Clone() {
+    return new DOMSVGPoint(this);
+  }
+
+  PRBool IsInList() const {
+    return !!mList;
+  }
+
+  /**
+   * In future, if this class is used for non-list points, this will be
+   * different to IsInList(). "Owner" here means that the instance has an
+   * internal counterpart from which it gets its values. (A better name may
+   * be HasWrappee().)
+   */
+  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 DOMSVGPointList).)
+   */
+  void InsertingIntoList(DOMSVGPointList *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();
+
+  SVGPoint ToSVGPoint() const {
+    return HasOwner() ? const_cast<DOMSVGPoint*>(this)->InternalItem() : mPt;
+  }
+
+  PRBool IsReadonly() const {
+    return mIsReadonly;
+  }
+  void SetReadonly(PRBool aReadonly) {
+    mIsReadonly = aReadonly;
+  }
+
+protected:
+
+  nsSVGElement* Element() {
+    return mList->Element();
+  }
+
+  /**
+   * Get a reference to the internal SVGPoint 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.
+   */
+  SVGPoint& InternalItem();
+
+#ifdef DEBUG
+  PRBool IndexIsValid();
+#endif
+
+  nsRefPtr<DOMSVGPointList> 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:30;
+  PRUint32 mIsReadonly:1;    // PRUint32 because MSVC won't pack otherwise
+  PRUint32 mIsAnimValItem:1; // PRUint32 because MSVC won't pack otherwise
+
+  // The following member is only used when we're not in a list:
+  SVGPoint mPt;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGPoint, MOZILLA_DOMSVGPOINT_IID)
+
+} // namespace mozilla
+
+#endif // MOZILLA_DOMSVGPOINT_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGPointList.cpp
@@ -0,0 +1,389 @@
+/* -*- 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 "DOMSVGPointList.h"
+#include "DOMSVGPoint.h"
+#include "nsDOMError.h"
+#include "SVGAnimatedPointList.h"
+#include "nsCOMPtr.h"
+#include "nsSVGAttrTearoffTable.h"
+
+// See the comment in this file's header.
+
+using namespace mozilla;
+
+static nsSVGAttrTearoffTable<void, DOMSVGPointList>
+  sSVGPointListTearoffTable;
+
+NS_SVG_VAL_IMPL_CYCLE_COLLECTION(DOMSVGPointList, mElement)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPointList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPointList)
+
+DOMCI_DATA(SVGPointList, DOMSVGPointList)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPointList)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPointList)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGPointList)
+NS_INTERFACE_MAP_END
+
+
+/* static */ already_AddRefed<DOMSVGPointList>
+DOMSVGPointList::GetDOMWrapper(void *aList,
+                               nsSVGElement *aElement,
+                               PRBool aIsAnimValList)
+{
+  DOMSVGPointList *wrapper =
+    sSVGPointListTearoffTable.GetTearoff(aList);
+  if (!wrapper) {
+    wrapper = new DOMSVGPointList(aElement, aIsAnimValList);
+    sSVGPointListTearoffTable.AddTearoff(aList, wrapper);
+  }
+  NS_ADDREF(wrapper);
+  return wrapper;
+}
+
+/* static */ DOMSVGPointList*
+DOMSVGPointList::GetDOMWrapperIfExists(void *aList)
+{
+  return sSVGPointListTearoffTable.GetTearoff(aList);
+}
+
+DOMSVGPointList::~DOMSVGPointList()
+{
+  // We no longer have any list items, and there are no script references to
+  // us.
+  //
+  // Do NOT use InternalList() as the key here! That's different!
+  void *key = mIsAnimValList ?
+    InternalAList().GetAnimValKey() :
+    InternalAList().GetBaseValKey();
+  sSVGPointListTearoffTable.RemoveTearoff(key);
+}
+
+void
+DOMSVGPointList::InternalListWillChangeTo(const SVGPointList& 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 too!
+
+  PRUint32 oldLength = mItems.Length();
+  PRUint32 newLength = aNewValue.Length();
+
+  // If our length will decrease, notify the items that will be removed:
+  for (PRUint32 i = newLength; i < oldLength; ++i) {
+    if (mItems[i]) {
+      mItems[i]->RemovingFromList();
+    }
+  }
+
+  if (!mItems.SetLength(newLength)) {
+    // We silently ignore SetLength OOM failure since being out of sync is safe
+    // so long as we have *fewer* items than our internal list.
+    mItems.Clear();
+    return;
+  }
+
+  // If our length has increased, null out the new pointers:
+  for (PRUint32 i = oldLength; i < newLength; ++i) {
+    mItems[i] = nsnull;
+  }
+}
+
+PRBool
+DOMSVGPointList::AttrIsAnimating() const
+{
+  return const_cast<DOMSVGPointList*>(this)->InternalAList().IsAnimating();
+}
+
+SVGPointList&
+DOMSVGPointList::InternalList()
+{
+  SVGAnimatedPointList *alist = mElement->GetAnimatedPointList();
+  return mIsAnimValList && alist->IsAnimating() ? *alist->mAnimVal : alist->mBaseVal;
+}
+
+SVGAnimatedPointList&
+DOMSVGPointList::InternalAList()
+{
+  NS_ABORT_IF_FALSE(mElement->GetAnimatedPointList(), "Internal error");
+  return *mElement->GetAnimatedPointList();
+}
+
+// ----------------------------------------------------------------------------
+// nsIDOMSVGPointList implementation:
+
+NS_IMETHODIMP
+DOMSVGPointList::GetNumberOfItems(PRUint32 *aNumberOfItems)
+{
+#ifdef MOZ_SMIL
+  if (IsAnimValList()) {
+    Element()->FlushAnimations();
+  }
+#endif
+  *aNumberOfItems = Length();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPointList::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(SVGPointList()); // clears mItems
+
+    if (!AttrIsAnimating()) {
+      // The anim val list is in sync with the base val list
+      DOMSVGPointList *animList =
+        GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
+      if (animList) {
+        animList->InternalListWillChangeTo(SVGPointList()); // clears its mItems
+      }
+    }
+
+    InternalList().Clear();
+    Element()->DidChangePointList(PR_TRUE);
+#ifdef MOZ_SMIL
+    if (AttrIsAnimating()) {
+      Element()->AnimationNeedsResample();
+    }
+#endif
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPointList::Initialize(nsIDOMSVGPoint *aNewItem,
+                            nsIDOMSVGPoint **_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<DOMSVGPoint> domItem = do_QueryInterface(aNewItem);
+  if (!domItem) {
+    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
+  }
+  if (domItem->HasOwner() || domItem->IsReadonly()) {
+    aNewItem = domItem->Clone();
+  }
+
+  Clear();
+  return InsertItemBefore(aNewItem, 0, _retval);
+}
+
+NS_IMETHODIMP
+DOMSVGPointList::GetItem(PRUint32 aIndex,
+                         nsIDOMSVGPoint **_retval)
+{
+#ifdef MOZ_SMIL
+  if (IsAnimValList()) {
+    Element()->FlushAnimations();
+  }
+#endif
+  if (aIndex < Length()) {
+    EnsureItemAt(aIndex);
+    NS_ADDREF(*_retval = mItems[aIndex]);
+    return NS_OK;
+  }
+  *_retval = nsnull;
+  return NS_ERROR_DOM_INDEX_SIZE_ERR;
+}
+
+NS_IMETHODIMP
+DOMSVGPointList::InsertItemBefore(nsIDOMSVGPoint *aNewItem,
+                                  PRUint32 aIndex,
+                                  nsIDOMSVGPoint **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  nsCOMPtr<DOMSVGPoint> domItem = do_QueryInterface(aNewItem);
+  if (!domItem) {
+    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
+  }
+  if (domItem->HasOwner() || domItem->IsReadonly()) {
+    domItem = domItem->Clone(); // must do this before changing anything!
+  }
+  aIndex = NS_MIN(aIndex, mItems.Length());
+
+  // Ensure we have enough memory so we can avoid complex error handling below:
+  if (!mItems.SetCapacity(mItems.Length() + 1) ||
+      !InternalList().SetCapacity(InternalList().Length() + 1)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  InternalList().InsertItem(aIndex, domItem->ToSVGPoint());
+  mItems.InsertElementAt(aIndex, domItem.get());
+
+  // This MUST come after the insertion into InternalList(), or else 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) {
+    if (mItems[i]) {
+      mItems[i]->UpdateListIndex(i);
+    }
+  }
+
+  Element()->DidChangePointList(PR_TRUE);
+#ifdef MOZ_SMIL
+  if (AttrIsAnimating()) {
+    Element()->AnimationNeedsResample();
+  }
+#endif
+  *_retval = domItem.forget().get();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPointList::ReplaceItem(nsIDOMSVGPoint *aNewItem,
+                             PRUint32 aIndex,
+                             nsIDOMSVGPoint **_retval)
+{
+  *_retval = nsnull;
+  if (IsAnimValList()) {
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  nsCOMPtr<DOMSVGPoint> 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->IsReadonly()) {
+    domItem = domItem->Clone(); // must do this before changing anything!
+  }
+
+  if (mItems[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:
+    mItems[aIndex]->RemovingFromList();
+  }
+
+  InternalList()[aIndex] = domItem->ToSVGPoint();
+  mItems[aIndex] = domItem;
+
+  // This MUST come after the assignment to InternalList, otherwise that call
+  // would end up reading bad data from InternalList()!
+  domItem->InsertingIntoList(this, aIndex, IsAnimValList());
+
+  Element()->DidChangePointList(PR_TRUE);
+#ifdef MOZ_SMIL
+  if (AttrIsAnimating()) {
+    Element()->AnimationNeedsResample();
+  }
+#endif
+  NS_ADDREF(*_retval = domItem.get());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPointList::RemoveItem(PRUint32 aIndex,
+                            nsIDOMSVGPoint **_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:
+  mItems[aIndex]->RemovingFromList();
+  NS_ADDREF(*_retval = mItems[aIndex]);
+
+  InternalList().RemoveItem(aIndex);
+  mItems.RemoveElementAt(aIndex);
+
+  for (PRUint32 i = aIndex; i < Length(); ++i) {
+    if (mItems[i]) {
+      mItems[i]->UpdateListIndex(i);
+    }
+  }
+
+  Element()->DidChangePointList(PR_TRUE);
+#ifdef MOZ_SMIL
+  if (AttrIsAnimating()) {
+    Element()->AnimationNeedsResample();
+  }
+#endif
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMSVGPointList::AppendItem(nsIDOMSVGPoint *aNewItem,
+                            nsIDOMSVGPoint **_retval)
+{
+  return InsertItemBefore(aNewItem, Length(), _retval);
+}
+
+void
+DOMSVGPointList::EnsureItemAt(PRUint32 aIndex)
+{
+  if (!mItems[aIndex]) {
+    mItems[aIndex] = new DOMSVGPoint(this, aIndex, IsAnimValList());
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/DOMSVGPointList.h
@@ -0,0 +1,210 @@
+/* -*- 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_DOMSVGPOINTLIST_H__
+#define MOZILLA_DOMSVGPOINTLIST_H__
+
+#include "nsIDOMSVGPointList.h"
+#include "SVGPointList.h"
+#include "SVGPoint.h"
+#include "nsCOMArray.h"
+#include "nsAutoPtr.h"
+
+class nsSVGElement;
+
+namespace mozilla {
+
+class DOMSVGPoint;
+class SVGAnimatedPointList;
+
+/**
+ * Class DOMSVGPointList
+ *
+ * This class is used to create the DOM tearoff objects that wrap internal
+ * SVGPointList 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 nsIDOMSVGAnimatedPointList interface
+ * in SVG, we have no parent DOMSVGAnimatedPointList (unlike DOMSVGLengthList
+ * which has a parent DOMSVGAnimatedLengthList class). (There is an
+ * SVGAnimatedPoints 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 DOMSVGAnimatedPointList (and is in
+ * DOMSVGAnimatedLengthList) is contained in this class.
+ *
+ * This class is strongly intertwined with DOMSVGPoint. Our DOMSVGPoint
+ * 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 DOMSVGPointList : public nsIDOMSVGPointList
+{
+  friend class DOMSVGPoint;
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGPointList)
+  NS_DECL_NSIDOMSVGPOINTLIST
+
+  /**
+   * Factory method to create and return a DOMSVGPointList wrapper
+   * for a given internal SVGPointList object. The factory takes care
+   * of caching the object that it returns so that the same object can be
+   * returned for the given SVGPointList 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 SVGPointList will naturally result in a new
+   * DOMSVGPointList being returned.
+   *
+   * It's unfortunate that aList is a void* instead of a typed argument. This
+   * is because the mBaseVal and mAnimVal members of SVGAnimatedPointList are
+   * of different types - a plain SVGPointList, and a SVGPointList*. We
+   * use the addresses of these members as the key for the hash table, and
+   * clearly SVGPointList* and a SVGPointList** are not the same type.
+   */
+  static already_AddRefed<DOMSVGPointList>
+  GetDOMWrapper(void *aList,
+                nsSVGElement *aElement,
+                PRBool aIsAnimValList);
+
+  /**
+   * This method returns the DOMSVGPointList wrapper for an internal
+   * SVGPointList object if it currently has a wrapper. If it does
+   * not, then nsnull is returned.
+   */
+  static DOMSVGPointList*
+  GetDOMWrapperIfExists(void *aList);
+
+  /**
+   * This will normally be the same as InternalList().Length(), except if
+   * we've hit OOM, in which case our length will be zero.
+   */
+  PRUint32 Length() const {
+    NS_ABORT_IF_FALSE(mItems.Length() == 0 ||
+                      mItems.Length() ==
+                        const_cast<DOMSVGPointList*>(this)->InternalList().Length(),
+                      "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 SVGPointList& 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.
+   */
+  DOMSVGPointList(nsSVGElement *aElement, PRBool aIsAnimValList)
+    : mElement(aElement)
+    , mIsAnimValList(aIsAnimValList)
+  {
+    // This call populates mItems with the same number of items as there are
+    // points 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());
+  }
+
+  ~DOMSVGPointList();
+
+  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 SVGPointList.
+   *
+   * 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.
+   */
+  SVGPointList& InternalList();
+
+  SVGAnimatedPointList& InternalAList();
+
+  /// Creates an instance of the appropriate DOMSVGPoint sub-class for
+  // aIndex, if it doesn't already exist.
+  void EnsureItemAt(PRUint32 aIndex);
+
+  // Weak refs to our DOMSVGPoint items. The items are friends and take care
+  // of clearing our pointer to them when they die.
+  nsTArray<DOMSVGPoint*> mItems;
+
+  // Strong ref to our element to keep it alive. We hold this not only for
+  // ourself, but also for our DOMSVGPoint items too.
+  nsRefPtr<nsSVGElement> mElement;
+
+  PRPackedBool mIsAnimValList;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_DOMSVGPOINTLIST_H__
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -52,16 +52,18 @@ CPPSRCS		= \
 		DOMSVGAnimatedLengthList.cpp \
 		DOMSVGAnimatedNumberList.cpp \
 		DOMSVGLength.cpp \
 		DOMSVGLengthList.cpp \
 		DOMSVGNumber.cpp \
 		DOMSVGNumberList.cpp \
 		DOMSVGPathSeg.cpp \
 		DOMSVGPathSegList.cpp \
+		DOMSVGPoint.cpp \
+		DOMSVGPointList.cpp \
 		nsDOMSVGZoomEvent.cpp \
 		nsDOMSVGEvent.cpp \
 		nsSVGAElement.cpp \
 		nsSVGAltGlyphElement.cpp \
 		nsSVGAngle.cpp \
 		nsSVGAnimatedTransformList.cpp \
 		nsSVGBoolean.cpp \
 		nsSVGCircleElement.cpp \
@@ -88,18 +90,16 @@ CPPSRCS		= \
 		nsSVGMaskElement.cpp \
 		nsSVGMatrix.cpp \
 		nsSVGMetadataElement.cpp \
 		nsSVGNumber2.cpp \
 		nsSVGPathDataParser.cpp \
 		nsSVGPathElement.cpp \
 		nsSVGPathGeometryElement.cpp \
 		nsSVGPatternElement.cpp \
-		nsSVGPoint.cpp \
-		nsSVGPointList.cpp \
 		nsSVGPolyElement.cpp \
 		nsSVGPolygonElement.cpp \
 		nsSVGPolylineElement.cpp \
 		nsSVGPreserveAspectRatio.cpp \
 		nsSVGScriptElement.cpp \
 		nsSVGString.cpp \
 		nsSVGStringProxyValue.cpp \
 		nsSVGStylableElement.cpp \
@@ -120,21 +120,23 @@ CPPSRCS		= \
 		nsSVGTransformList.cpp \
 		nsSVGTransformListParser.cpp \
 		nsSVGUseElement.cpp \
 		nsSVGValue.cpp \
 		nsSVGViewBox.cpp \
 		SVGAnimatedLengthList.cpp \
 		SVGAnimatedNumberList.cpp \
 		SVGAnimatedPathSegList.cpp \
+		SVGAnimatedPointList.cpp \
 		SVGLength.cpp \
 		SVGLengthList.cpp \
 		SVGNumberList.cpp \
 		SVGPathData.cpp \
 		SVGPathSegUtils.cpp \
+		SVGPointList.cpp \
 		$(NULL)
 
 ifdef MOZ_SMIL
 CPPSRCS += nsSVGAnimateElement.cpp \
            nsSVGAnimateTransformElement.cpp \
            nsSVGAnimateMotionElement.cpp \
            nsSVGAnimationElement.cpp \
            nsSVGMpathElement.cpp \
@@ -144,32 +146,32 @@ CPPSRCS += nsSVGAnimateElement.cpp \
            SVGLengthListSMILType.cpp \
            SVGMotionSMILType.cpp \
            SVGMotionSMILAttr.cpp \
            SVGMotionSMILAnimationFunction.cpp \
            SVGMotionSMILPathUtils.cpp \
            SVGNumberListSMILType.cpp \
            SVGOrientSMILType.cpp \
            SVGPathSegListSMILType.cpp \
+           SVGPointListSMILType.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
 
 EXPORTS =  			\
 	nsISVGValue.h              \
 	nsISVGValueObserver.h      \
 	nsISVGValueUtils.h         \
 	nsSVGFeatures.h            \
 	nsSVGRect.h                \
-	nsSVGPoint.h               \
 	nsSVGMatrix.h              \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES += 	\
 		-I$(srcdir)/../../../shared/public \
 		-I$(srcdir)/../../../html/base/src \
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGAnimatedPointList.cpp
@@ -0,0 +1,243 @@
+/* -*- 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 "SVGAnimatedPointList.h"
+#include "DOMSVGPointList.h"
+#include "nsSVGElement.h"
+#include "nsSVGAttrTearoffTable.h"
+#ifdef MOZ_SMIL
+#include "nsSMILValue.h"
+#include "SVGPointListSMILType.h"
+#endif // MOZ_SMIL
+
+// See the comments in this file's header!
+
+using namespace mozilla;
+
+nsresult
+SVGAnimatedPointList::SetBaseValueString(const nsAString& aValue)
+{
+  SVGPointList newBaseValue;
+
+  // The spec says that the point 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
+  // DOMSVGPointList::InternalListWillChangeTo().
+
+  DOMSVGPointList *baseValWrapper =
+    DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey());
+  if (baseValWrapper) {
+    baseValWrapper->InternalListWillChangeTo(newBaseValue);
+  }
+
+  DOMSVGPointList *animValWrapper;
+  if (!IsAnimating()) {  // DOM anim val wraps our base val too!
+    animValWrapper = DOMSVGPointList::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
+SVGAnimatedPointList::ClearBaseValue()
+{
+  // We must send these notifications *before* changing mBaseVal! (See above.)
+
+  DOMSVGPointList *baseValWrapper =
+    DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey());
+  if (baseValWrapper) {
+    baseValWrapper->InternalListWillChangeTo(SVGPointList());
+  }
+
+  if (!IsAnimating()) { // DOM anim val wraps our base val too!
+    DOMSVGPointList *animValWrapper =
+      DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
+    if (animValWrapper) {
+      animValWrapper->InternalListWillChangeTo(SVGPointList());
+    }
+  }
+
+  mBaseVal.Clear();
+  // Caller notifies
+}
+
+nsresult
+SVGAnimatedPointList::SetAnimValue(const SVGPointList& 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.
+  // It is not possible for us to reliably distinguish between calls to this
+  // method that are setting a new sample for an existing animation (in which
+  // case our list length isn't changing and we wouldn't need to notify our DOM
+  // wrapper to keep its length in sync), and calls to this method that are
+  // setting the first sample of a new animation that will override the base
+  // value/an existing animation (in which case our length may be changing and
+  // our DOM wrapper may need to be notified). Happily though, it's cheap to
+  // just blindly notify our animVal's DOM wrapper of our new value each time
+  // this method is called, so that's what we do.
+
+  // We must send this notification *before* changing mAnimVal! (See above.)
+
+  DOMSVGPointList *domWrapper =
+    DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
+  if (domWrapper) {
+    domWrapper->InternalListWillChangeTo(aNewAnimValue);
+  }
+  if (!mAnimVal) {
+    mAnimVal = new SVGPointList();
+  }
+  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);
+    return rv;
+  }
+  aElement->DidAnimatePointList();
+  return NS_OK;
+}
+
+void
+SVGAnimatedPointList::ClearAnimValue(nsSVGElement *aElement)
+{
+  // We must send these notifications *before* changing mAnimVal! (See above.)
+
+  DOMSVGPointList *domWrapper =
+    DOMSVGPointList::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->DidAnimatePointList();
+}
+
+#ifdef MOZ_SMIL
+nsISMILAttr*
+SVGAnimatedPointList::ToSMILAttr(nsSVGElement *aElement)
+{
+  return new SMILAnimatedPointList(this, aElement);
+}
+
+nsresult
+SVGAnimatedPointList::
+  SMILAnimatedPointList::ValueFromString(const nsAString& aStr,
+                               const nsISMILAnimationElement* /*aSrcElement*/,
+                               nsSMILValue& aValue,
+                               PRBool& aPreventCachingOfSandwich) const
+{
+  nsSMILValue val(&SVGPointListSMILType::sSingleton);
+  SVGPointListAndInfo *list = static_cast<SVGPointListAndInfo*>(val.mU.mPtr);
+  nsresult rv = list->SetValueFromString(aStr);
+  if (NS_SUCCEEDED(rv)) {
+    list->SetInfo(mElement);
+    aValue.Swap(val);
+  }
+  aPreventCachingOfSandwich = PR_FALSE;
+  return rv;
+}
+
+nsSMILValue
+SVGAnimatedPointList::SMILAnimatedPointList::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(&SVGPointListSMILType::sSingleton);
+  SVGPointListAndInfo *list = static_cast<SVGPointListAndInfo*>(tmp.mU.mPtr);
+  nsresult rv = list->CopyFrom(mVal->mBaseVal);
+  if (NS_SUCCEEDED(rv)) {
+    list->SetInfo(mElement);
+    val.Swap(tmp);
+  }
+  return val;
+}
+
+nsresult
+SVGAnimatedPointList::SMILAnimatedPointList::SetAnimValue(const nsSMILValue& aValue)
+{
+  NS_ASSERTION(aValue.mType == &SVGPointListSMILType::sSingleton,
+               "Unexpected type to assign animated value");
+  if (aValue.mType == &SVGPointListSMILType::sSingleton) {
+    mVal->SetAnimValue(*static_cast<SVGPointListAndInfo*>(aValue.mU.mPtr),
+                       mElement);
+  }
+  return NS_OK;
+}
+
+void
+SVGAnimatedPointList::SMILAnimatedPointList::ClearAnimValue()
+{
+  if (mVal->mAnimVal) {
+    mVal->ClearAnimValue(mElement);
+  }
+}
+#endif // MOZ_SMIL
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGAnimatedPointList.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_SVGANIMATEDPOINTLIST_H__
+#define MOZILLA_SVGANIMATEDPOINTLIST_H__
+
+#include "SVGPointList.h"
+
+class nsSVGElement;
+
+#ifdef MOZ_SMIL
+#include "nsISMILAttr.h"
+#endif // MOZ_SMIL
+
+namespace mozilla {
+
+/**
+ * Class SVGAnimatedPointList
+ *
+ * Despite the fact that no SVGAnimatedPointList 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
+ * SVGAnimatedPoints 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 SVGPointList objects,
+ * which helps ensure that the DOM wrappers for SVGPointList objects' are
+ * always kept in sync. This is vitally important (see the comment in
+ * DOMSVGPointList::InternalListWillChangeTo) and frees consumers from having
+ * to know or worry about wrappers (or forget about them!) for the most part.
+ */
+class SVGAnimatedPointList
+{
+  // friends so that they can get write access to mBaseVal and mAnimVal
+  friend class DOMSVGPoint;
+  friend class DOMSVGPointList;
+
+public:
+  SVGAnimatedPointList() {}
+
+  /**
+   * Because it's so important that mBaseVal and its DOMSVGPointList wrapper
+   * (if any) be kept in sync (see the comment in
+   * DOMSVGPointList::InternalListWillChangeTo), this method returns a const
+   * reference. Only our friend classes may get mutable references to mBaseVal.
+   */
+  const SVGPointList& GetBaseValue() const {
+    return mBaseVal;
+  }
+
+  nsresult SetBaseValueString(const nsAString& aValue);
+
+  void ClearBaseValue();
+
+  /**
+   * const! See comment for GetBaseValue!
+   */
+  const SVGPointList& GetAnimValue() const {
+    return mAnimVal ? *mAnimVal : mBaseVal;
+  }
+
+  nsresult SetAnimValue(const SVGPointList& 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="">).
+
+  SVGPointList mBaseVal;
+  nsAutoPtr<SVGPointList> mAnimVal;
+
+#ifdef MOZ_SMIL
+  struct SMILAnimatedPointList : public nsISMILAttr
+  {
+  public:
+    SMILAnimatedPointList(SVGAnimatedPointList* 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.
+    SVGAnimatedPointList *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_SVGANIMATEDPOINTLIST_H__
--- a/content/svg/content/src/SVGNumberListSMILType.cpp
+++ b/content/svg/content/src/SVGNumberListSMILType.cpp
@@ -167,17 +167,16 @@ SVGNumberListSMILType::ComputeDistance(c
   const SVGNumberListAndInfo& from =
     *static_cast<const SVGNumberListAndInfo*>(aFrom.mU.mPtr);
   const SVGNumberListAndInfo& to =
     *static_cast<const SVGNumberListAndInfo*>(aTo.mU.mPtr);
 
   if (from.Length() != to.Length()) {
     // Lists in the 'values' attribute must have the same length.
     // nsSVGUtils::ReportToConsole
-    aDistance = 0.0;
     return NS_ERROR_FAILURE;
   }
 
   // We return the root of the sum of the squares of the delta between the
   // numbers at each correspanding index.
 
   double total = 0.0;
 
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPoint.h
@@ -0,0 +1,130 @@
+/* -*- 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_SVGPOINT_H__
+#define MOZILLA_SVGPOINT_H__
+
+#include "nsDebug.h"
+#include "nsContentUtils.h"
+#include "gfxPoint.h"
+
+namespace mozilla {
+
+/**
+ * This class is currently used for point list attributes.
+ *
+ * The DOM wrapper class for this class is DOMSVGPoint.
+ */
+class SVGPoint
+{
+public:
+
+  SVGPoint()
+#ifdef DEBUG
+    : mX(0.0f)
+    , mY(0.0f)
+#endif
+  {}
+
+  SVGPoint(float aX, float aY)
+    : mX(aX)
+    , mY(aY)
+  {
+    NS_ASSERTION(IsValid(), "Constructed an invalid SVGPoint");
+  }
+
+  SVGPoint(const SVGPoint &aOther)
+    : mX(aOther.mX)
+    , mY(aOther.mY)
+  {}
+
+  SVGPoint& operator=(const SVGPoint &rhs) {
+    mX = rhs.mX;
+    mY = rhs.mY;
+    return *this;
+  }
+
+  PRBool operator==(const SVGPoint &rhs) const {
+    return mX == rhs.mX && mY == rhs.mY;
+  }
+
+  SVGPoint& operator+=(const SVGPoint &rhs) {
+    mX += rhs.mX;
+    mY += rhs.mY;
+    return *this;
+  }
+
+  operator gfxPoint() const {
+    return gfxPoint(mX, mY);
+  }
+
+#ifdef DEBUG
+  PRBool IsValid() const {
+    return NS_FloatIsFinite(mX) && NS_FloatIsFinite(mY);
+  }
+#endif
+
+  float mX;
+  float mY;
+};
+
+inline SVGPoint operator+(const SVGPoint& aP1,
+                          const SVGPoint& aP2)
+{
+  return SVGPoint(aP1.mX + aP2.mX, aP1.mY + aP2.mY);
+}
+
+inline SVGPoint operator-(const SVGPoint& aP1,
+                          const SVGPoint& aP2)
+{
+  return SVGPoint(aP1.mX - aP2.mX, aP1.mY - aP2.mY);
+}
+
+inline SVGPoint operator*(float aFactor,
+                          const SVGPoint& aPoint)
+{
+  return SVGPoint(aFactor * aPoint.mX, aFactor * aPoint.mY);
+}
+
+inline SVGPoint operator*(const SVGPoint& aPoint,
+                          float aFactor)
+{
+  return SVGPoint(aFactor * aPoint.mX, aFactor * aPoint.mY);
+}
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGPOINT_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPointList.cpp
@@ -0,0 +1,143 @@
+/* -*- 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 "SVGPointList.h"
+#include "SVGAnimatedPointList.h"
+#include "nsSVGElement.h"
+#include "nsISVGValueUtils.h"
+#include "nsDOMError.h"
+#include "nsContentUtils.h"
+#include "nsString.h"
+#include "nsSVGUtils.h"
+#include "string.h"
+#include "prdtoa.h"
+#include "nsTextFormatter.h"
+#include "nsCharSeparatedTokenizer.h"
+
+using namespace mozilla;
+
+nsresult
+SVGPointList::CopyFrom(const SVGPointList& rhs)
+{
+  if (!SetCapacity(rhs.Length())) {
+    // Yes, we do want fallible alloc here
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  mItems = rhs.mItems;
+  return NS_OK;
+}
+
+void
+SVGPointList::GetValueAsString(nsAString& aValue) const
+{
+  aValue.Truncate();
+  PRUnichar buf[50];
+  PRUint32 last = mItems.Length() - 1;
+  for (PRUint32 i = 0; i < mItems.Length(); ++i) {
+    // Would like to use aValue.AppendPrintf("%f,%f", item.mX, item.mY),
+    // but it's not possible to always avoid trailing zeros.
+    nsTextFormatter::snprintf(buf, NS_ARRAY_LENGTH(buf),
+                              NS_LITERAL_STRING("%g,%g").get(),
+                              double(mItems[i].mX), double(mItems[i].mY));
+    // We ignore OOM, since it's not useful for us to return an error.
+    aValue.Append(buf);
+    if (i != last) {
+      aValue.Append(' ');
+    }
+  }
+}
+
+static inline char* SkipWhitespace(char* str)
+{
+  while (IsSVGWhitespace(*str))
+    ++str;
+  return str;
+}
+
+nsresult
+SVGPointList::SetValueFromString(const nsAString& aValue)
+{
+  // The spec says that the list is parsed and accepted up to the first error
+  // encountered, so we must call CopyFrom even if an error occurs. We still
+  // want to throw any error code from setAttribute if there's a problem
+  // though, so we must take care to return any error code.
+
+  nsresult rv = NS_OK;
+
+  SVGPointList temp;
+
+  nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
+    tokenizer(aValue, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
+
+  nsCAutoString str1, str2;  // outside loop to minimize memory churn
+
+  while (tokenizer.hasMoreTokens()) {
+    CopyUTF16toUTF8(tokenizer.nextToken(), str1);
+    const char *token1 = str1.get();
+    if (*token1 == '\0' || !tokenizer.hasMoreTokens()) {
+      rv = NS_ERROR_DOM_SYNTAX_ERR;
+      break;
+    }
+    CopyUTF16toUTF8(tokenizer.nextToken(), str2);
+    const char *token2 = str2.get();
+    if (*token2 == '\0') {
+      rv = NS_ERROR_DOM_SYNTAX_ERR;
+      break;
+    }
+
+    char *end;
+    float x = float(PR_strtod(token1, &end));
+    if (*end != '\0' || !NS_FloatIsFinite(x)) {
+      rv = NS_ERROR_DOM_SYNTAX_ERR;
+      break;
+    }
+    float y = float(PR_strtod(token2, &end));
+    if (*end != '\0' || !NS_FloatIsFinite(y)) {
+      rv = NS_ERROR_DOM_SYNTAX_ERR;
+      break;
+    }
+
+    temp.AppendItem(SVGPoint(x, y));
+  }
+  if (tokenizer.lastTokenEndedWithSeparator()) {
+    rv = NS_ERROR_DOM_SYNTAX_ERR; // trailing comma
+  }
+  nsresult rv2 = CopyFrom(temp);
+  if (NS_FAILED(rv2)) {
+    return rv2; // prioritize OOM error code over syntax errors
+  }
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPointList.h
@@ -0,0 +1,230 @@
+/* -*- 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_SVGPOINTLIST_H__
+#define MOZILLA_SVGPOINTLIST_H__
+
+#include "SVGPoint.h"
+#include "nsTArray.h"
+#include "nsSVGElement.h"
+
+namespace mozilla {
+
+/**
+ * ATTENTION! WARNING! WATCH OUT!!
+ *
+ * Consumers that modify objects of this type absolutely MUST keep the DOM
+ * wrappers for those lists (if any) in sync!! That's why this class is so
+ * locked down.
+ *
+ * The DOM wrapper class for this class is DOMSVGPointList.
+ */
+class SVGPointList
+{
+  friend class SVGAnimatedPointList;
+  friend class DOMSVGPointList;
+  friend class DOMSVGPoint;
+
+public:
+
+  SVGPointList(){}
+  ~SVGPointList(){}
+
+  // 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 mItems.IsEmpty();
+  }
+
+  PRUint32 Length() const {
+    return mItems.Length();
+  }
+
+  const SVGPoint& operator[](PRUint32 aIndex) const {
+    return mItems[aIndex];
+  }
+
+  PRBool operator==(const SVGPointList& rhs) const {
+    // memcmp can be faster than |mItems == rhs.mItems|
+    return mItems.Length() == rhs.mItems.Length() &&
+           memcmp(mItems.Elements(), rhs.mItems.Elements(),
+                  mItems.Length() * sizeof(SVGPoint)) == 0;
+  }
+
+  PRBool SetCapacity(PRUint32 aSize) {
+    return mItems.SetCapacity(aSize);
+  }
+
+  void Compact() {
+    mItems.Compact();
+  }
+
+  // Access to methods that can modify objects of this type is deliberately
+  // limited. This is to reduce the chances of someone modifying objects of
+  // this type without taking the necessary steps to keep DOM wrappers in sync.
+  // If you need wider access to these methods, consider adding a method to
+  // SVGAnimatedPointList 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 SVGPointList& rhs);
+
+  SVGPoint& operator[](PRUint32 aIndex) {
+    return mItems[aIndex];
+  }
+
+  /**
+   * This may fail (return PR_FALSE) on OOM if the internal capacity is being
+   * increased, in which case the list will be left unmodified.
+   */
+  PRBool SetLength(PRUint32 aNumberOfItems) {
+    return mItems.SetLength(aNumberOfItems);
+  }
+
+private:
+
+  // Marking the following private only serves to show which methods are only
+  // used by our friend classes (as opposed to our subclasses) - it doesn't
+  // really provide additional safety.
+
+  nsresult SetValueFromString(const nsAString& aValue);
+
+  void Clear() {
+    mItems.Clear();
+  }
+
+  PRBool InsertItem(PRUint32 aIndex, const SVGPoint &aPoint) {
+    if (aIndex >= mItems.Length()) {
+      aIndex = mItems.Length();
+    }
+    return !!mItems.InsertElementAt(aIndex, aPoint);
+  }
+
+  void ReplaceItem(PRUint32 aIndex, const SVGPoint &aPoint) {
+    NS_ASSERTION(aIndex < mItems.Length(),
+                 "DOM wrapper caller should have raised INDEX_SIZE_ERR");
+    mItems[aIndex] = aPoint;
+  }
+
+  void RemoveItem(PRUint32 aIndex) {
+    NS_ASSERTION(aIndex < mItems.Length(),
+                 "DOM wrapper caller should have raised INDEX_SIZE_ERR");
+    mItems.RemoveElementAt(aIndex);
+  }
+
+  PRBool AppendItem(SVGPoint aPoint) {
+    return !!mItems.AppendElement(aPoint);
+  }
+
+protected:
+
+  /* See SVGLengthList for the rationale for using nsTArray<SVGPoint> instead
+   * of nsTArray<SVGPoint, 1>.
+   */
+  nsTArray<SVGPoint> mItems;
+};
+
+
+/**
+ * This SVGPointList subclass is for SVGPointListSMILType which needs a
+ * mutable version of SVGPointList. 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.
+ *
+ * This class contains a strong reference to the element that instances of
+ * this class are being used to animate. This is because the SMIL code stores
+ * instances of this class in nsSMILValue objects, some of which are cached.
+ * Holding a strong reference to the element here prevents the element from
+ * disappearing out from under the SMIL code unexpectedly.
+ */
+class SVGPointListAndInfo : public SVGPointList
+{
+public:
+
+  SVGPointListAndInfo(nsSVGElement *aElement = nsnull)
+    : mElement(aElement)
+  {}
+
+  void SetInfo(nsSVGElement *aElement) {
+    mElement = aElement;
+  }
+
+  nsSVGElement* Element() const {
+    return mElement;
+  }
+
+  nsresult CopyFrom(const SVGPointListAndInfo& rhs) {
+    mElement = rhs.mElement;
+    return SVGPointList::CopyFrom(rhs);
+  }
+
+  /**
+   * Exposed so that SVGPointList baseVals can be copied to
+   * SVGPointListAndInfo objects. Note that callers should also call
+   * SetElement() when using this method!
+   */
+  nsresult CopyFrom(const SVGPointList& rhs) {
+    return SVGPointList::CopyFrom(rhs);
+  }
+  const SVGPoint& operator[](PRUint32 aIndex) const {
+    return SVGPointList::operator[](aIndex);
+  }
+  SVGPoint& operator[](PRUint32 aIndex) {
+    return SVGPointList::operator[](aIndex);
+  }
+  PRBool SetLength(PRUint32 aNumberOfItems) {
+    return SVGPointList::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_SVGPOINTLIST_H__
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPointListSMILType.cpp
@@ -0,0 +1,227 @@
+/* -*- 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 "SVGPointListSMILType.h"
+#include "nsSMILValue.h"
+#include "SVGPointList.h"
+#include "nsMathUtils.h"
+#include <math.h>
+
+using namespace mozilla;
+
+/*static*/ SVGPointListSMILType SVGPointListSMILType::sSingleton;
+
+//----------------------------------------------------------------------
+// nsISMILType implementation
+
+void
+SVGPointListSMILType::Init(nsSMILValue &aValue) const
+{
+  NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type");
+
+  SVGPointListAndInfo* pointList = new SVGPointListAndInfo();
+
+  aValue.mU.mPtr = pointList;
+  aValue.mType = this;
+}
+
+void
+SVGPointListSMILType::Destroy(nsSMILValue& aValue) const
+{
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
+  delete static_cast<SVGPointListAndInfo*>(aValue.mU.mPtr);
+  aValue.mU.mPtr = nsnull;
+  aValue.mType = &nsSMILNullType::sSingleton;
+}
+
+nsresult
+SVGPointListSMILType::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 SVGPointListAndInfo* src =
+    static_cast<const SVGPointListAndInfo*>(aSrc.mU.mPtr);
+  SVGPointListAndInfo* dest =
+    static_cast<SVGPointListAndInfo*>(aDest.mU.mPtr);
+
+  return dest->CopyFrom(*src);
+}
+
+PRBool
+SVGPointListSMILType::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 SVGPointListAndInfo*>(aLeft.mU.mPtr) ==
+         *static_cast<const SVGPointListAndInfo*>(aRight.mU.mPtr);
+}
+
+nsresult
+SVGPointListSMILType::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");
+
+  SVGPointListAndInfo& dest =
+    *static_cast<SVGPointListAndInfo*>(aDest.mU.mPtr);
+  const SVGPointListAndInfo& valueToAdd =
+    *static_cast<const SVGPointListAndInfo*>(aValueToAdd.mU.mPtr);
+
+  NS_ABORT_IF_FALSE(dest.Element() || valueToAdd.Element(),
+                    "Target element propagation failure");
+
+  if (!valueToAdd.Element()) {
+    NS_ABORT_IF_FALSE(valueToAdd.Length() == 0,
+                      "Not identity value - target element propagation failure");
+    return NS_OK;
+  }
+  if (!dest.Element()) {
+    NS_ABORT_IF_FALSE(dest.Length() == 0,
+                      "Not identity value - target element propagation failure");
+    if (!dest.SetLength(valueToAdd.Length())) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    for (PRUint32 i = 0; i < dest.Length(); ++i) {
+      dest[i] = aCount * valueToAdd[i];
+    }
+    dest.SetInfo(valueToAdd.Element()); // propagate target element info!
+    return NS_OK;
+  }
+  NS_ABORT_IF_FALSE(dest.Element() == valueToAdd.Element(),
+                    "adding values from different elements...?");
+  if (dest.Length() != valueToAdd.Length()) {
+    // For now we only support animation between lists with the same number of
+    // items. nsSVGUtils::ReportToConsole
+    return NS_ERROR_FAILURE;
+  }
+  for (PRUint32 i = 0; i < dest.Length(); ++i) {
+    dest[i] += aCount * valueToAdd[i];
+  }
+  dest.SetInfo(valueToAdd.Element()); // propagate target element info!
+  return NS_OK;
+}
+
+nsresult
+SVGPointListSMILType::ComputeDistance(const nsSMILValue& aFrom,
+                                      const nsSMILValue& aTo,
+                                      double& aDistance) const
+{
+  NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
+  NS_PRECONDITION(aTo.mType == this, "Incompatible SMIL type");
+
+  const SVGPointListAndInfo& from =
+    *static_cast<const SVGPointListAndInfo*>(aFrom.mU.mPtr);
+  const SVGPointListAndInfo& to =
+    *static_cast<const SVGPointListAndInfo*>(aTo.mU.mPtr);
+
+  if (from.Length() != to.Length()) {
+    // Lists in the 'values' attribute must have the same length.
+    // nsSVGUtils::ReportToConsole
+    return NS_ERROR_FAILURE;
+  }
+
+  // We return the root of the sum of the squares of the distances between the
+  // points at each corresponding index.
+
+  double total = 0.0;
+
+  for (PRUint32 i = 0; i < to.Length(); ++i) {
+    double dx = to[i].mX - from[i].mX;
+    double dy = to[i].mY - from[i].mY;
+    total += dx * dx + dy * dy;
+  }
+  double distance = sqrt(total);
+  if (!NS_FloatIsFinite(distance)) {
+    return NS_ERROR_FAILURE;
+  }
+  aDistance = distance;
+
+  return NS_OK;
+}
+
+nsresult
+SVGPointListSMILType::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 SVGPointListAndInfo& start =
+    *static_cast<const SVGPointListAndInfo*>(aStartVal.mU.mPtr);
+  const SVGPointListAndInfo& end =
+    *static_cast<const SVGPointListAndInfo*>(aEndVal.mU.mPtr);
+  SVGPointListAndInfo& result =
+    *static_cast<SVGPointListAndInfo*>(aResult.mU.mPtr);
+
+  NS_ABORT_IF_FALSE(end.Element(), "Can't propagate target element");
+  NS_ABORT_IF_FALSE(start.Element() == end.Element() || !start.Element(),
+                    "Different target elements");
+
+  if (start.Element() && // 'start' is not an "identity" value
+      start.Length() != end.Length()) {
+    // For now we only support animation between lists of the same length.
+    // nsSVGUtils::ReportToConsole
+    return NS_ERROR_FAILURE;
+  }
+  if (!result.SetLength(end.Length())) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  result.SetInfo(end.Element()); // propagate target element info!
+
+  if (start.Length() != end.Length()) {
+    NS_ABORT_IF_FALSE(start.Length() == 0, "Not an identity value");
+    for (PRUint32 i = 0; i < end.Length(); ++i) {
+      result[i] = aUnitDistance * end[i];
+    }
+    return NS_OK;
+  }
+  for (PRUint32 i = 0; i < end.Length(); ++i) {
+    result[i] = start[i] + (end[i] - start[i]) * aUnitDistance;
+  }
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGPointListSMILType.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) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef MOZILLA_SVGPOINTLISTSMILTYPE_H_
+#define MOZILLA_SVGPOINTLISTSMILTYPE_H_
+
+#include "nsISMILType.h"
+
+class nsSMILValue;
+
+namespace mozilla {
+
+////////////////////////////////////////////////////////////////////////
+// SVGPointListSMILType
+//
+// Operations for animating an SVGPointList.
+//
+class SVGPointListSMILType : public nsISMILType
+{
+public:
+  // Singleton for nsSMILValue objects to hold onto.
+  static SVGPointListSMILType 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.
+  SVGPointListSMILType() {}
+  ~SVGPointListSMILType() {}
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGPOINTLISTSMILTYPE_H_
--- a/content/svg/content/src/nsDOMSVGZoomEvent.cpp
+++ b/content/svg/content/src/nsDOMSVGZoomEvent.cpp
@@ -33,24 +33,25 @@
  * 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 "nsDOMSVGZoomEvent.h"
 #include "nsContentUtils.h"
 #include "nsSVGRect.h"
-#include "nsSVGPoint.h"
+#include "DOMSVGPoint.h"
 #include "nsSVGSVGElement.h"
 #include "nsIDOMSVGSVGElement.h"
 #include "nsIContent.h"
 #include "nsIPresShell.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/Element.h"
 
+using namespace mozilla;
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsDOMSVGZoomEvent::nsDOMSVGZoomEvent(nsPresContext* aPresContext,
                                      nsGUIEvent* aEvent)
   : nsDOMUIEvent(aPresContext,
@@ -86,23 +87,25 @@ nsDOMSVGZoomEvent::nsDOMSVGZoomEvent(nsP
           nsSVGSVGElement *SVGSVGElement =
             static_cast<nsSVGSVGElement*>(rootElement);
   
           mNewScale = SVGSVGElement->GetCurrentScale();
           mPreviousScale = SVGSVGElement->GetPreviousScale();
 
           const nsSVGTranslatePoint& translate =
             SVGSVGElement->GetCurrentTranslate();
-          NS_NewSVGReadonlyPoint(getter_AddRefs(mNewTranslate),
-                                 translate.GetX(), translate.GetY());
+          mNewTranslate =
+            new DOMSVGPoint(translate.GetX(), translate.GetY());
+          mNewTranslate->SetReadonly(PR_TRUE);
 
           const nsSVGTranslatePoint& prevTranslate =
             SVGSVGElement->GetPreviousTranslate();
-          NS_NewSVGReadonlyPoint(getter_AddRefs(mPreviousTranslate),
-                                 prevTranslate.GetX(), prevTranslate.GetY());
+          mPreviousTranslate =
+            new DOMSVGPoint(prevTranslate.GetX(), prevTranslate.GetY());
+          mPreviousTranslate->SetReadonly(PR_TRUE);
         }
       }
     }
   }
 }
 
 
 //----------------------------------------------------------------------
--- a/content/svg/content/src/nsDOMSVGZoomEvent.h
+++ b/content/svg/content/src/nsDOMSVGZoomEvent.h
@@ -37,31 +37,37 @@
 
 #ifndef __NS_SVGZOOMEVENT_H__
 #define __NS_SVGZOOMEVENT_H__
 
 #include "nsIDOMSVGZoomEvent.h"
 #include "nsDOMUIEvent.h"
 #include "nsIDOMSVGSVGElement.h"
 
+namespace mozilla {
+class DOMSVGPoint;
+}
+
 class nsDOMSVGZoomEvent : public nsDOMUIEvent,
                           public nsIDOMSVGZoomEvent
 {
 public:
+  typedef mozilla::DOMSVGPoint DOMSVGPoint;
+
   nsDOMSVGZoomEvent(nsPresContext* aPresContext, nsGUIEvent* aEvent);
                      
   // nsISupports interface:
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMSVGZoomEvent interface:
   NS_DECL_NSIDOMSVGZOOMEVENT
 
   // Forward to base class
   NS_FORWARD_TO_NSDOMUIEVENT
 
 private:
-  float                    mPreviousScale;
-  nsCOMPtr<nsIDOMSVGPoint> mPreviousTranslate;
-  float                    mNewScale;
-  nsCOMPtr<nsIDOMSVGPoint> mNewTranslate;
+  float mPreviousScale;
+  float mNewScale;
+  nsRefPtr<DOMSVGPoint> mPreviousTranslate;
+  nsRefPtr<DOMSVGPoint> mNewTranslate;
 };
 
 #endif // __NS_SVGZOOMEVENT_H__
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -71,16 +71,17 @@
 #include "nsSVGInteger.h"
 #include "nsSVGAngle.h"
 #include "nsSVGBoolean.h"
 #include "nsSVGEnum.h"
 #include "nsSVGViewBox.h"
 #include "nsSVGString.h"
 #include "SVGAnimatedNumberList.h"
 #include "SVGAnimatedLengthList.h"
+#include "SVGAnimatedPointList.h"
 #include "SVGAnimatedPathSegList.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsIDOMSVGPointList.h"
 #include "nsIDOMSVGAnimatedPoints.h"
 #include "nsIDOMSVGTransformList.h"
 #include "nsIDOMSVGAnimTransformList.h"
 #include "nsIDOMSVGAnimatedRect.h"
 #include "nsIDOMSVGGradientElement.h"
@@ -180,17 +181,20 @@ nsSVGElement::Init()
   }
 
   NumberListAttributesInfo numberListInfo = GetNumberListInfo();
 
   for (i = 0; i < numberListInfo.mNumberListCount; i++) {
     numberListInfo.Reset(i);
   }
 
-  // No need to reset SVGPathData since the default value in always the same
+  // No need to reset SVGPointList since the default value is always the same
+  // (an empty list).
+
+  // No need to reset SVGPathData since the default value is always the same
   // (an empty list).
 
   StringAttributesInfo stringInfo = GetStringInfo();
 
   for (i = 0; i < stringInfo.mStringCount; i++) {
     stringInfo.Reset(i);
   }
 
@@ -394,16 +398,32 @@ nsSVGElement::ParseAttribute(PRInt32 aNa
           }
           foundMatch = PR_TRUE;
           break;
         }
       }
     }
 
     if (!foundMatch) {
+      // Check for SVGAnimatedPointList attribute
+      if (GetPointListAttrName() == aAttribute) {
+        SVGAnimatedPointList* pointList = GetAnimatedPointList();
+        if (pointList) {
+          rv = pointList->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 pointList->ClearBaseValue()
+          }
+          foundMatch = PR_TRUE;
+        }
+      }
+    }
+
+    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
@@ -619,16 +639,28 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespa
           DidChangeNumberList(i, PR_FALSE);
           foundMatch = PR_TRUE;
           break;
         }
       }
     }
 
     if (!foundMatch) {
+      // Check if this is a point list attribute going away
+      if (GetPointListAttrName() == aName) {
+        SVGAnimatedPointList *pointList = GetAnimatedPointList();
+        if (pointList) {
+          pointList->ClearBaseValue();
+          DidChangePointList(PR_FALSE);
+          foundMatch = PR_TRUE;
+        }
+      }
+    }
+
+    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;
         }
@@ -1680,34 +1712,63 @@ nsSVGElement::GetAnimatedNumberList(nsIA
       return &info.mNumberLists[i];
     }
   }
   NS_ABORT_IF_FALSE(PR_FALSE, "Bad caller");
   return nsnull;
 }
 
 void
+nsSVGElement::DidChangePointList(PRBool aDoSetAttr)
+{
+  NS_ABORT_IF_FALSE(GetPointListAttrName(), "Changing non-existent point list?");
+
+  if (!aDoSetAttr)
+    return;
+
+  nsAutoString newStr;
+  GetAnimatedPointList()->GetBaseValue().GetValueAsString(newStr);
+
+  SetAttr(kNameSpaceID_None, GetPointListAttrName(), newStr, PR_TRUE);
+}
+
+void
+nsSVGElement::DidAnimatePointList()
+{
+  NS_ABORT_IF_FALSE(GetPointListAttrName(),
+                    "Animating non-existent path data?");
+
+  nsIFrame* frame = GetPrimaryFrame();
+
+  if (frame) {
+    frame->AttributeChanged(kNameSpaceID_None,
+                            GetPointListAttrName(),
+                            nsIDOMMutationEvent::MODIFICATION);
+  }
+}
+
+void
 nsSVGElement::DidChangePathSegList(PRBool aDoSetAttr)
 {
-  NS_ABORT_IF_FALSE(GetPathDataAttrName(), "Changing non-existant path data?");
+  NS_ABORT_IF_FALSE(GetPathDataAttrName(), "Changing non-existent 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?");
+                    "Animating non-existent path data?");
 
   nsIFrame* frame = GetPrimaryFrame();
 
   if (frame) {
     frame->AttributeChanged(kNameSpaceID_None,
                             GetPathDataAttrName(),
                             nsIDOMMutationEvent::MODIFICATION);
   }
@@ -2388,16 +2449,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);
       }
     }
   }
 
+  // PointLists:
+  {
+    if (GetPointListAttrName() == aName) {
+      SVGAnimatedPointList *pointList = GetAnimatedPointList();
+      if (pointList) {
+        return pointList->ToSMILAttr(this);
+      }
+    }
+  }
+
   // PathSegLists:
   {
     if (GetPathDataAttrName() == aName) {
       SVGAnimatedPathSegList *segList = GetAnimPathSegList();
       if (segList) {
         return segList->ToSMILAttr(this);
       }
     }
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -71,16 +71,17 @@ class nsSVGViewBox;
 class nsSVGPreserveAspectRatio;
 class nsSVGString;
 struct gfxMatrix;
 namespace mozilla {
 class SVGAnimatedNumberList;
 class SVGNumberList;
 class SVGAnimatedLengthList;
 class SVGUserUnitList;
+class SVGAnimatedPointList;
 class SVGAnimatedPathSegList;
 }
 
 typedef nsStyledElement nsSVGElementBase;
 
 class nsSVGElement : public nsSVGElementBase,    // nsIContent
                      public nsISVGValueObserver  // :nsISupportsWeakReference
 {
@@ -89,16 +90,17 @@ protected:
   nsresult Init();
   virtual ~nsSVGElement();
 
 public:
   typedef mozilla::SVGNumberList SVGNumberList;
   typedef mozilla::SVGAnimatedNumberList SVGAnimatedNumberList;
   typedef mozilla::SVGUserUnitList SVGUserUnitList;
   typedef mozilla::SVGAnimatedLengthList SVGAnimatedLengthList;
+  typedef mozilla::SVGAnimatedPointList SVGAnimatedPointList;
   typedef mozilla::SVGAnimatedPathSegList SVGAnimatedPathSegList;
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIContent interface methods
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
@@ -175,40 +177,45 @@ public:
   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 DidChangeNumberList(PRUint8 aAttrEnum, PRBool aDoSetAttr);
   virtual void DidChangeLengthList(PRUint8 aAttrEnum, PRBool aDoSetAttr);
+  virtual void DidChangePointList(PRBool aDoSetAttr);
   virtual void DidChangePathSegList(PRBool aDoSetAttr);
   virtual void DidChangeString(PRUint8 aAttrEnum) {}
 
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateNumber(PRUint8 aAttrEnum);
   virtual void 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 DidAnimateNumberList(PRUint8 aAttrEnum);
   virtual void DidAnimateLengthList(PRUint8 aAttrEnum);
+  virtual void DidAnimatePointList();
   virtual void DidAnimatePathSegList();
   virtual void DidAnimateTransform();
   virtual void DidAnimateString(PRUint8 aAttrEnum);
 
   void GetAnimatedLengthValues(float *aFirst, ...);
   void GetAnimatedNumberValues(float *aFirst, ...);
   void GetAnimatedIntegerValues(PRInt32 *aFirst, ...);
   SVGAnimatedNumberList* GetAnimatedNumberList(PRUint8 aAttrEnum);
   SVGAnimatedNumberList* GetAnimatedNumberList(nsIAtom *aAttrName);
   void GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...);
   SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum);
+  virtual SVGAnimatedPointList* GetAnimatedPointList() {
+    return nsnull;
+  }
   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;
   }
 
@@ -221,16 +228,19 @@ public:
   void FlushAnimations() { /* do nothing */ }
 #endif
 
   virtual void RecompileScriptEventListeners();
 
   void GetStringBaseValue(PRUint8 aAttrEnum, nsAString& aResult) const;
   void SetStringBaseValue(PRUint8 aAttrEnum, const nsAString& aValue);
 
+  virtual nsIAtom* GetPointListAttrName() const {
+    return nsnull;
+  }
   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,
--- a/content/svg/content/src/nsSVGElementFactory.cpp
+++ b/content/svg/content/src/nsSVGElementFactory.cpp
@@ -240,19 +240,16 @@ nsresult
 NS_NewSVGSetElement(nsIContent **aResult,
                     already_AddRefed<nsINodeInfo> aNodeInfo);
 #endif // MOZ_SMIL
 
 nsresult
 NS_NewSVGElement(nsIContent** aResult, already_AddRefed<nsINodeInfo> aNodeInfo,
                  FromParser aFromParser)
 {
-  NS_PRECONDITION(NS_SVGEnabled(),
-                  "creating an SVG element while SVG disabled");
-
   static const char kSVGStyleSheetURI[] = "resource://gre/res/svg.css";
 
   // this bit of code is to load svg.css on demand
   nsIDocument *doc = aNodeInfo.get()->GetDocument();
   if (doc)
     doc->EnsureCatalogStyleSheet(kSVGStyleSheetURI);
 
   nsIAtom *name = aNodeInfo.get()->NameAtom();
--- a/content/svg/content/src/nsSVGFeatures.cpp
+++ b/content/svg/content/src/nsSVGFeatures.cpp
@@ -53,20 +53,16 @@
 #include "nsWhitespaceTokenizer.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsStyleUtil.h"
 #include "nsSVGUtils.h"
 
 /*static*/ PRBool
 nsSVGFeatures::HaveFeature(const nsAString& aFeature)
 {
-  if (!NS_SVGEnabled()) {
-    return PR_FALSE;
-  }
-
 #define SVG_SUPPORTED_FEATURE(str) if (aFeature.Equals(NS_LITERAL_STRING(str).get())) return PR_TRUE;
 #define SVG_UNSUPPORTED_FEATURE(str)
 #include "nsSVGFeaturesList.h"
 #undef SVG_SUPPORTED_FEATURE
 #undef SVG_UNSUPPORTED_FEATURE
   return PR_FALSE;
 }
 
--- a/content/svg/content/src/nsSVGPathElement.cpp
+++ b/content/svg/content/src/nsSVGPathElement.cpp
@@ -41,17 +41,17 @@
 #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 "DOMSVGPoint.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 
 using namespace mozilla;
 
 nsSVGElement::NumberInfo nsSVGPathElement::sNumberInfo = 
 { &nsGkAtoms::pathLength, 0, PR_FALSE };
 
@@ -124,17 +124,18 @@ nsSVGPathElement::GetPointAtLength(float
   float totalLength = flat->GetLength();
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::pathLength)) {
     float pathLength = mPathLength.GetAnimValue();
     distance *= totalLength / pathLength;
   }
   distance = NS_MAX(0.f,         distance);
   distance = NS_MIN(totalLength, distance);
 
-  return NS_NewSVGPoint(_retval, flat->FindPoint(gfxPoint(distance, 0)));
+  NS_ADDREF(*_retval = new DOMSVGPoint(flat->FindPoint(gfxPoint(distance, 0))));
+  return NS_OK;
 }
 
 /* unsigned long getPathSegAtLength (in float distance); */
 NS_IMETHODIMP
 nsSVGPathElement::GetPathSegAtLength(float distance, PRUint32 *_retval)
 {
   NS_ENSURE_FINITE(distance, NS_ERROR_ILLEGAL_VALUE);
   *_retval = mD.GetAnimValue().GetPathSegAtLength(distance);
deleted file mode 100644
--- a/content/svg/content/src/nsSVGPoint.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Mozilla SVG project.
- *
- * The Initial Developer of the Original Code is
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *   Jonathan Watt <jonathan.watt@strath.ac.uk>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsSVGPoint.h"
-#include "nsIDOMSVGMatrix.h"
-#include "nsSVGValue.h"
-#include "nsContentUtils.h"
-#include "nsDOMError.h"
-
-class nsSVGPoint : public nsIDOMSVGPoint,
-                   public nsSVGValue
-{
-public:
-  nsSVGPoint(float x, float y);
-
-  // nsISupports interface:
-  NS_DECL_ISUPPORTS
-
-  // nsIDOMSVGPoint interface:
-  NS_DECL_NSIDOMSVGPOINT
-
-  // nsISVGValue interface:
-  NS_IMETHOD SetValueString(const nsAString& aValue);
-  NS_IMETHOD GetValueString(nsAString& aValue);
-  
-protected:
-  float mX;
-  float mY;
-};
-
-
-//----------------------------------------------------------------------
-// Implementation
-
-nsresult
-NS_NewSVGPoint(nsIDOMSVGPoint** result, float x, float y)
-{
-  *result = new nsSVGPoint(x, y);
-  if (!*result)
-    return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(*result);
-  return NS_OK;
-}
-
-nsresult
-NS_NewSVGPoint(nsIDOMSVGPoint** result, const gfxPoint& point)
-{
-  return NS_NewSVGPoint(result, float(point.x), float(point.y));
-}
-
-nsSVGPoint::nsSVGPoint(float x, float y)
-    : mX(x), mY(y)
-{
-}
-
-//----------------------------------------------------------------------
-// nsISupports methods:
-
-NS_IMPL_ADDREF(nsSVGPoint)
-NS_IMPL_RELEASE(nsSVGPoint)
-
-DOMCI_DATA(SVGPoint, nsSVGPoint)
-
-NS_INTERFACE_MAP_BEGIN(nsSVGPoint)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValue)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPoint)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGPoint)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
-NS_INTERFACE_MAP_END
-
-//----------------------------------------------------------------------
-// nsIDOMSVGPoint methods:
-
-/* attribute float x; */
-NS_IMETHODIMP nsSVGPoint::GetX(float *aX)
-{
-  *aX = mX;
-  return NS_OK;
-}
-NS_IMETHODIMP nsSVGPoint::SetX(float aX)
-{
-  NS_ENSURE_FINITE(aX, NS_ERROR_ILLEGAL_VALUE);
-
-  WillModify();
-  mX = aX;
-  DidModify();
-  
-  return NS_OK;
-}
-
-/* attribute float y; */
-NS_IMETHODIMP nsSVGPoint::GetY(float *aY)
-{
-  *aY = mY;
-  return NS_OK;
-}
-NS_IMETHODIMP nsSVGPoint::SetY(float aY)
-{
-  NS_ENSURE_FINITE(aY, NS_ERROR_ILLEGAL_VALUE);
-
-  WillModify();
-  mY = aY;
-  DidModify();
-  
-  return NS_OK;
-}
-
-/* nsIDOMSVGPoint matrixTransform (in nsIDOMSVGMatrix matrix); */
-NS_IMETHODIMP nsSVGPoint::MatrixTransform(nsIDOMSVGMatrix *matrix,
-                                          nsIDOMSVGPoint **_retval)
-{
-  if (!matrix)
-    return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
-
-  float a, b, c, d, e, f;
-  matrix->GetA(&a);
-  matrix->GetB(&b);
-  matrix->GetC(&c);
-  matrix->GetD(&d);
-  matrix->GetE(&e);
-  matrix->GetF(&f);
-  
-  return NS_NewSVGPoint(_retval, a*mX + c*mY + e, b*mX + d*mY + f);
-}
-
-//----------------------------------------------------------------------
-// nsISVGValue methods:
-NS_IMETHODIMP
-nsSVGPoint::SetValueString(const nsAString& aValue)
-{
-  NS_NOTYETIMPLEMENTED("nsSVGPoint::SetValueString");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-nsSVGPoint::GetValueString(nsAString& aValue)
-{
-  NS_NOTYETIMPLEMENTED("nsSVGPoint::GetValueString");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-
-
-////////////////////////////////////////////////////////////////////////
-// Implement a readonly version of SVGPoint
-//
-// We need this because attributes of some SVG interfaces *and* the objects the
-// attributes refer to (including SVGPoints) are supposed to be readonly
-
-class nsSVGReadonlyPoint : public nsSVGPoint
-{
-public:
-  nsSVGReadonlyPoint(float x, float y)
-    : nsSVGPoint(x, y)
-  {
-  }
-
-  // override setters to make the whole object readonly
-  NS_IMETHODIMP SetX(float) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
-  NS_IMETHODIMP SetY(float) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
-  NS_IMETHODIMP SetValueString(const nsAString&) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
-};
-
-nsresult
-NS_NewSVGReadonlyPoint(nsIDOMSVGPoint** result, float x, float y)
-{
-  *result = new nsSVGReadonlyPoint(x, y);
-  if (!*result)
-    return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(*result);
-  return NS_OK;
-}
-
deleted file mode 100644
--- a/content/svg/content/src/nsSVGPoint.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Mozilla SVG project.
- *
- * The Initial Developer of the Original Code is
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *   Jonathan Watt <jonathan.watt@strath.ac.uk>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef __NS_SVGPOINT_H__
-#define __NS_SVGPOINT_H__
-
-#include "nsIDOMSVGPoint.h"
-#include "gfxPoint.h"
-
-nsresult
-NS_NewSVGPoint(nsIDOMSVGPoint** result, float x = 0.0f, float y = 0.0f);
-
-nsresult
-NS_NewSVGPoint(nsIDOMSVGPoint** result, const gfxPoint& point);
-
-nsresult
-NS_NewSVGReadonlyPoint(nsIDOMSVGPoint** result, float x = 0.0f, float y = 0.0f);
-
-#endif //__NS_SVGPOINT_H__
deleted file mode 100644
--- a/content/svg/content/src/nsSVGPointList.cpp
+++ /dev/null
@@ -1,377 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Mozilla SVG project.
- *
- * The Initial Developer of the Original Code is
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsSVGPointList.h"
-#include "nsSVGPoint.h"
-#include "nsSVGUtils.h"
-#include "nsCharSeparatedTokenizer.h"
-#include "nsDOMError.h"
-#include "prdtoa.h"
-#include "nsReadableUtils.h"
-#include "nsTextFormatter.h"
-#include "nsCRT.h"
-#include "nsCOMArray.h"
-#include "nsContentUtils.h"
-
-#define NS_ENSURE_NATIVE_POINT(obj, retval)             \
-  {                                                     \
-    nsCOMPtr<nsISVGValue> val = do_QueryInterface(obj); \
-    if (!val) {                                         \
-      *retval = nsnull;                                 \
-      return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;           \
-    }                                                   \
-  }
-
-nsresult
-nsSVGPointList::Create(const nsAString& aValue,
-                       nsISVGValue** aResult)
-{
-  *aResult = (nsISVGValue*) new nsSVGPointList();
-  if(!*aResult) return NS_ERROR_OUT_OF_MEMORY;
-  
-  NS_ADDREF(*aResult);
-
-  (*aResult)->SetValueString(aValue);  
-  return NS_OK;
-}
-
-nsresult
-nsSVGPointList::Create(nsIDOMSVGPointList** aResult)
-{
-  *aResult = (nsIDOMSVGPointList*) new nsSVGPointList();
-  if(!*aResult) return NS_ERROR_OUT_OF_MEMORY;
-  
-  NS_ADDREF(*aResult);
-  return NS_OK;
-}
-
-nsSVGPointList::nsSVGPointList()
-{
-}
-
-nsSVGPointList::~nsSVGPointList()
-{
-  ReleasePoints();
-}
-
-void
-nsSVGPointList::ReleasePoints()
-{
-  WillModify();
-  PRUint32 count = mPoints.Length();
-  for (PRUint32 i = 0; i < count; ++i) {
-    nsIDOMSVGPoint* point = ElementAt(i);
-    nsCOMPtr<nsISVGValue> val = do_QueryInterface(point);
-    if (val)
-      val->RemoveObserver(this);
-    NS_RELEASE(point);
-  }
-  mPoints.Clear();
-  DidModify();
-}
-
-nsIDOMSVGPoint*
-nsSVGPointList::ElementAt(PRInt32 index)
-{
-  return mPoints.ElementAt(index);
-}
-
-void
-nsSVGPointList::AppendElement(nsIDOMSVGPoint* aElement)
-{
-  WillModify();
-  NS_ADDREF(aElement);
-  mPoints.AppendElement(aElement);
-  nsCOMPtr<nsISVGValue> val = do_QueryInterface(aElement);
-  if (val)
-    val->AddObserver(this);
-  DidModify();
-}
-
-void
-nsSVGPointList::RemoveElementAt(PRInt32 index)
-{
-  WillModify();
-  nsIDOMSVGPoint* point = ElementAt(index);
-  NS_ASSERTION(point, "null point");
-  nsCOMPtr<nsISVGValue> val = do_QueryInterface(point);
-  if (val)
-    val->RemoveObserver(this);
-  mPoints.RemoveElementAt(index);
-  NS_RELEASE(point);
-  DidModify();
-}
-
-void
-nsSVGPointList::InsertElementAt(nsIDOMSVGPoint* aElement, PRInt32 index)
-{
-  WillModify();
-  NS_ADDREF(aElement);
-  mPoints.InsertElementAt(index, aElement);
-  nsCOMPtr<nsISVGValue> val = do_QueryInterface(aElement);
-  if (val)
-    val->AddObserver(this);
-  DidModify();
-}
-
-//----------------------------------------------------------------------
-// nsISupports methods:
-
-NS_IMPL_ADDREF(nsSVGPointList)
-NS_IMPL_RELEASE(nsSVGPointList)
-
-DOMCI_DATA(SVGPointList, nsSVGPointList)
-
-NS_INTERFACE_MAP_BEGIN(nsSVGPointList)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValue)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPointList)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGPointList)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
-NS_INTERFACE_MAP_END
-
-
-//----------------------------------------------------------------------
-// nsISVGValue methods:
-
-NS_IMETHODIMP
-nsSVGPointList::SetValueString(const nsAString& aValue)
-{
-  nsCharSeparatedTokenizer
-    tokenizer(aValue, ',',
-              nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
-  nsCOMArray<nsIDOMSVGPoint> points;
-
-  PRBool parseError = PR_FALSE;
-
-  while (tokenizer.hasMoreTokens()) {
-    // Parse 2 tokens
-    NS_ConvertUTF16toUTF8 utf8String1(tokenizer.nextToken());
-    const char *token1 = utf8String1.get();
-    if (!tokenizer.hasMoreTokens() ||  // No 2nd token.
-        *token1 == '\0') {             // 1st token is empty string.
-      parseError = PR_TRUE;
-      break;
-    }
-    NS_ConvertUTF16toUTF8 utf8String2(tokenizer.nextToken());
-    const char *token2 = utf8String2.get();
-    if (*token2 == '\0') {             // 2nd token is empty string.
-      parseError = PR_TRUE;
-      break;
-    }
-
-    // Convert parsed tokens to float values.
-    char *end;
-    float x = float(PR_strtod(token1, &end));
-    if (*end != '\0' || !NS_FloatIsFinite(x)) {
-      parseError = PR_TRUE;
-      break;
-    }
-    float y = float(PR_strtod(token2, &end));
-    if (*end != '\0' || !NS_FloatIsFinite(y)) {
-      parseError = PR_TRUE;
-      break;
-    }
-
-    // Build a point from our parsed float values.
-    nsCOMPtr<nsIDOMSVGPoint> point;
-    NS_NewSVGPoint(getter_AddRefs(point), x, y); // uses infallible 'new'.
-    points.AppendObject(point);
-  }
-
-  if (tokenizer.lastTokenEndedWithSeparator()) { // Reject trailing comma
-    parseError = PR_TRUE;
-  }
-
-  if (parseError) {
-    // XXX nsSVGUtils::ReportToConsole()
-  }
-
-  WillModify();
-  ReleasePoints();
-  PRInt32 count = points.Count();
-  for (PRInt32 i = 0; i < count; ++i) {
-    AppendElement(points.ObjectAt(i));
-  }
-  DidModify();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSVGPointList::GetValueString(nsAString& aValue)
-{
-  aValue.Truncate();
-
-  PRUint32 count = mPoints.Length();
-
-  if (count == 0) return NS_OK;
-
-  PRUint32 i = 0;
-  PRUnichar buf[48];
-  
-  while (1) {
-    nsIDOMSVGPoint* point = ElementAt(i);
-    float x, y;
-    point->GetX(&x);
-    point->GetY(&y);
-    
-    nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar), NS_LITERAL_STRING("%g,%g").get(), (double)x, (double)y);
-    aValue.Append(buf);
-
-    if (++i >= count) break;
-
-    aValue.AppendLiteral(" ");
-  }
-  
-  return NS_OK;
-}
-
-//----------------------------------------------------------------------
-// nsIDOMSVGPointList methods:
-
-/* readonly attribute unsigned long numberOfItems; */
-NS_IMETHODIMP nsSVGPointList::GetNumberOfItems(PRUint32 *aNumberOfItems)
-{
-  *aNumberOfItems = mPoints.Length();
-  return NS_OK;
-}
-
-/* void clear (); */
-NS_IMETHODIMP nsSVGPointList::Clear()
-{
-  WillModify();
-  ReleasePoints();
-  DidModify();
-  return NS_OK;
-}
-
-/* nsIDOMSVGPoint initialize (in nsIDOMSVGPoint newItem); */
-NS_IMETHODIMP nsSVGPointList::Initialize(nsIDOMSVGPoint *newItem,
-                                         nsIDOMSVGPoint **_retval)
-{
-  NS_ENSURE_NATIVE_POINT(newItem, _retval);
-  Clear();
-  return AppendItem(newItem, _retval);
-}
-
-/* nsIDOMSVGPoint getItem (in unsigned long index); */
-NS_IMETHODIMP nsSVGPointList::GetItem(PRUint32 index, nsIDOMSVGPoint **_retval)
-{
-  if (index >= mPoints.Length()) {
-    *_retval = nsnull;
-    return NS_ERROR_DOM_INDEX_SIZE_ERR;
-  }
-
-  *_retval  = ElementAt(index);
-  NS_ADDREF(*_retval);
-  return NS_OK;
-}
-
-/* nsIDOMSVGPoint insertItemBefore (in nsIDOMSVGPoint newItem, in unsigned long index); */
-NS_IMETHODIMP nsSVGPointList::InsertItemBefore(nsIDOMSVGPoint *newItem,
-                                               PRUint32 index,
-                                               nsIDOMSVGPoint **_retval)
-{
-  NS_ENSURE_NATIVE_POINT(newItem, _retval);
-
-  NS_NOTYETIMPLEMENTED("write me");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-/* nsIDOMSVGPoint replaceItem (in nsIDOMSVGPoint newItem, in unsigned long index); */
-NS_IMETHODIMP nsSVGPointList::ReplaceItem(nsIDOMSVGPoint *newItem,
-                                          PRUint32 index,
-                                          nsIDOMSVGPoint **_retval)
-{
-  NS_ENSURE_NATIVE_POINT(newItem, _retval);
-
-  NS_NOTYETIMPLEMENTED("write me");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-/* nsIDOMSVGPoint removeItem (in unsigned long index); */
-NS_IMETHODIMP nsSVGPointList::RemoveItem(PRUint32 index, nsIDOMSVGPoint **_retval)
-{
-  if (index >= mPoints.Length()) {
-    *_retval = nsnull;
-    return NS_ERROR_DOM_INDEX_SIZE_ERR;
-  }
-
-  *_retval = ElementAt(index);
-  NS_ADDREF(*_retval);
-  WillModify();
-  RemoveElementAt(index);
-  DidModify();
-  return NS_OK;
-}
-
-/* nsIDOMSVGPoint appendItem (in nsIDOMSVGPoint newItem); */
-NS_IMETHODIMP nsSVGPointList::AppendItem(nsIDOMSVGPoint *newItem,
-                                         nsIDOMSVGPoint **_retval)
-{
-  // XXX The SVG specs state that 'if newItem is already in a list, it
-  // is removed from its previous list before it is inserted into this
-  // list'. We don't do that. Should we?
-  
-  NS_ENSURE_NATIVE_POINT(newItem, _retval);
-  *_retval = newItem;
-  AppendElement(newItem);
-  NS_ADDREF(*_retval);
-  return NS_OK;
-}
-
-//----------------------------------------------------------------------
-// nsISVGValueObserver methods
-
-NS_IMETHODIMP
-nsSVGPointList::WillModifySVGObservable(nsISVGValue* observable,
-                                        modificationType aModType)
-{
-  WillModify(aModType);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSVGPointList::DidModifySVGObservable (nsISVGValue* observable,
-                                        modificationType aModType)
-{
-  DidModify(aModType);
-  return NS_OK;
-}
deleted file mode 100644
--- a/content/svg/content/src/nsSVGPointList.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Mozilla SVG project.
- *
- * The Initial Developer of the Original Code is
- * Crocodile Clips Ltd..
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef __NS_SVGPOINTLIST_H__
-#define __NS_SVGPOINTLIST_H__
-
-#include "nsSVGValue.h"
-#include "nsISVGValueObserver.h"
-#include "nsWeakReference.h"
-#include "nsIDOMSVGPointList.h"
-#include "nsTArray.h"
-
-
-class nsSVGPointList : public nsSVGValue,
-                       public nsIDOMSVGPointList,
-                       public nsISVGValueObserver
-{
-public:
-  static nsresult Create(const nsAString& aValue, nsISVGValue** aResult);
-  static nsresult Create(nsIDOMSVGPointList** aResult);
-  
-protected:
-  nsSVGPointList();
-  virtual ~nsSVGPointList();
-  
-public:
-  // nsISupports interface:
-  NS_DECL_ISUPPORTS
-
-  // nsIDOMSVGPointList interface:
-  NS_DECL_NSIDOMSVGPOINTLIST
-  
-  // remainder of nsISVGValue interface:
-  NS_IMETHOD SetValueString(const nsAString& aValue);
-  NS_IMETHOD GetValueString(nsAString& aValue);
-
-  // nsISVGValueObserver
-  NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
-                                     modificationType aModType);
-  NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
-                                     modificationType aModType);
-  
-  // nsISupportsWeakReference
-  // implementation inherited from nsSupportsWeakReference
-
-  
-  // other methods:
-  nsIDOMSVGPoint* ElementAt(PRInt32 index);
-  void AppendElement(nsIDOMSVGPoint* aElement);
-  void RemoveElementAt(PRInt32 index);
-  void InsertElementAt(nsIDOMSVGPoint* aElement, PRInt32 index);
-  
-protected:
-  void ReleasePoints();
-  
-  nsAutoTArray<nsIDOMSVGPoint*, 8> mPoints;
-};
-
-
-#endif //__NS_SVGPOINTLIST_H__
--- a/content/svg/content/src/nsSVGPolyElement.cpp
+++ b/content/svg/content/src/nsSVGPolyElement.cpp
@@ -30,17 +30,21 @@
  * 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 "nsSVGPolyElement.h"
+#include "DOMSVGPointList.h"
 #include "gfxContext.h"
+#include "nsSVGUtils.h"
+
+using namespace mozilla;
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGPolyElement,nsSVGPolyElementBase)
 NS_IMPL_RELEASE_INHERITED(nsSVGPolyElement,nsSVGPolyElementBase)
 
 NS_INTERFACE_MAP_BEGIN(nsSVGPolyElement)
@@ -51,51 +55,34 @@ NS_INTERFACE_MAP_END_INHERITING(nsSVGPol
 // Implementation
 
 nsSVGPolyElement::nsSVGPolyElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsSVGPolyElementBase(aNodeInfo)
 {
 
 }
 
-nsresult
-nsSVGPolyElement::Init()
-{
-  nsresult rv = nsSVGPolyElementBase::Init();
-  NS_ENSURE_SUCCESS(rv,rv);
-
-  // Create mapped properties:
-  
-  // points #IMPLIED
-  rv = nsSVGPointList::Create(getter_AddRefs(mPoints));
-  NS_ENSURE_SUCCESS(rv,rv);
-  rv = AddMappedSVGValue(nsGkAtoms::points, mPoints);
-  NS_ENSURE_SUCCESS(rv,rv);
-
-  return rv;
-}
-
 //----------------------------------------------------------------------
 // nsIDOMSGAnimatedPoints methods:
 
 /* readonly attribute nsIDOMSVGPointList points; */
 NS_IMETHODIMP 
 nsSVGPolyElement::GetPoints(nsIDOMSVGPointList * *aPoints)
 {
-  *aPoints = mPoints;
-  NS_ADDREF(*aPoints);
+  void *key = mPoints.GetBaseValKey();
+  *aPoints = DOMSVGPointList::GetDOMWrapper(key, this, PR_FALSE).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGPointList animatedPoints; */
 NS_IMETHODIMP 
 nsSVGPolyElement::GetAnimatedPoints(nsIDOMSVGPointList * *aAnimatedPoints)
 {
-  *aAnimatedPoints = mPoints;
-  NS_ADDREF(*aAnimatedPoints);
+  void *key = mPoints.GetAnimValKey();
+  *aAnimatedPoints = DOMSVGPointList::GetDOMWrapper(key, this, PR_TRUE).get();
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 // nsIContent methods
 
 NS_IMETHODIMP_(PRBool)
 nsSVGPolyElement::IsAttributeMapped(const nsIAtom* name) const
@@ -118,34 +105,26 @@ nsSVGPolyElement::AttributeDefinesGeomet
     return PR_TRUE;
 
   return PR_FALSE;
 }
 
 void
 nsSVGPolyElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
 {
-  if (!mPoints)
-    return;
+  const SVGPointList &points = mPoints.GetAnimValue();
 
-  PRUint32 count;
-  mPoints->GetNumberOfItems(&count);
-  if (count == 0)
+  if (!points.Length())
     return;
 
   float px = 0.0, py = 0.0, prevAngle;
 
-  for (PRUint32 i = 0; i < count; ++i) {
-    nsCOMPtr<nsIDOMSVGPoint> point;
-    mPoints->GetItem(i, getter_AddRefs(point));
-
-    float x, y;
-    point->GetX(&x);
-    point->GetY(&y);
-
+  for (PRUint32 i = 0; i < points.Length(); ++i) {
+    float x = points[i].mX;
+    float y = points[i].mY;
     float angle = atan2(y-py, x-px);
     if (i == 1)
       aMarks->ElementAt(aMarks->Length() - 1).angle = angle;
     else if (i > 1)
       aMarks->ElementAt(aMarks->Length() - 1).angle =
         nsSVGUtils::AngleBisect(prevAngle, angle);
 
     aMarks->AppendElement(nsSVGMark(x, y, 0));
@@ -156,31 +135,19 @@ nsSVGPolyElement::GetMarkPoints(nsTArray
   }
 
   aMarks->ElementAt(aMarks->Length() - 1).angle = prevAngle;
 }
 
 void
 nsSVGPolyElement::ConstructPath(gfxContext *aCtx)
 {
-  if (!mPoints)
-    return;
+  const SVGPointList &points = mPoints.GetAnimValue();
 
-  PRUint32 count;
-  mPoints->GetNumberOfItems(&count);
-  if (count == 0)
+  if (!points.Length())
     return;
 
-  PRUint32 i;
-  for (i = 0; i < count; ++i) {
-    nsCOMPtr<nsIDOMSVGPoint> point;
-    mPoints->GetItem(i, getter_AddRefs(point));
-
-    float x, y;
-    point->GetX(&x);
-    point->GetY(&y);
-    if (i == 0)
-      aCtx->MoveTo(gfxPoint(x, y));
-    else
-      aCtx->LineTo(gfxPoint(x, y));
+  aCtx->MoveTo(points[0]);
+  for (PRUint32 i = 1; i < points.Length(); ++i) {
+    aCtx->LineTo(points[i]);
   }
 }
 
--- a/content/svg/content/src/nsSVGPolyElement.h
+++ b/content/svg/content/src/nsSVGPolyElement.h
@@ -33,46 +33,48 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef NS_SVGPOLYELEMENT_H_
 #define NS_SVGPOLYELEMENT_H_
 
 #include "nsSVGPathGeometryElement.h"
-#include "nsCOMPtr.h"
-#include "nsIDOMSVGPoint.h"
-#include "nsSVGPointList.h"
 #include "nsIDOMSVGAnimatedPoints.h"
-#include "nsSVGUtils.h"
+#include "SVGAnimatedPointList.h"
 
 typedef nsSVGPathGeometryElement nsSVGPolyElementBase;
 
 class gfxContext;
 
 class nsSVGPolyElement : public nsSVGPolyElementBase,
                          public nsIDOMSVGAnimatedPoints
 {
 protected:
   nsSVGPolyElement(already_AddRefed<nsINodeInfo> aNodeInfo);
-  nsresult Init();
 
 public:
   //interfaces
   
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGANIMATEDPOINTS
 
   // nsIContent interface
   NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* name) const;
-  
+
+  virtual SVGAnimatedPointList* GetAnimatedPointList() {
+    return &mPoints;
+  }
+  virtual nsIAtom* GetPointListAttrName() const {
+    return nsGkAtoms::points;
+  }
+
   // nsSVGPathGeometryElement methods:
   virtual PRBool AttributeDefinesGeometry(const nsIAtom *aName);
   virtual PRBool IsMarkable() { return PR_TRUE; }
   virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks);
   virtual void ConstructPath(gfxContext *aCtx);
 
 protected:
-  nsCOMPtr<nsIDOMSVGPointList> mPoints;
-
+  SVGAnimatedPointList mPoints;
 };
 
 #endif //NS_SVGPOLYELEMENT_H_
--- a/content/svg/content/src/nsSVGPolygonElement.cpp
+++ b/content/svg/content/src/nsSVGPolygonElement.cpp
@@ -34,16 +34,17 @@
  * 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 "nsSVGPolyElement.h"
 #include "nsIDOMSVGPolygonElement.h"
 #include "gfxContext.h"
+#include "nsSVGUtils.h"
 
 typedef nsSVGPolyElement nsSVGPolygonElementBase;
 
 class nsSVGPolygonElement : public nsSVGPolygonElementBase,
                             public nsIDOMSVGPolygonElement
 {
 protected:
   friend nsresult NS_NewSVGPolygonElement(nsIContent **aResult,
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -43,17 +43,17 @@
 #include "DOMSVGLength.h"
 #include "nsSVGAngle.h"
 #include "nsCOMPtr.h"
 #include "nsIPresShell.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
 #include "nsSVGMatrix.h"
-#include "nsSVGPoint.h"
+#include "DOMSVGPoint.h"
 #include "nsSVGTransform.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIFrame.h"
 #include "nsISVGSVGFrame.h" //XXX
 #include "nsSVGRect.h"
 #include "nsISVGValueUtils.h"
 #include "nsDOMError.h"
 #include "nsISVGChildFrame.h"
@@ -125,18 +125,19 @@ nsSVGTranslatePoint::DOMVal::MatrixTrans
   matrix->GetB(&b);
   matrix->GetC(&c);
   matrix->GetD(&d);
   matrix->GetE(&e);
   matrix->GetF(&f);
 
   float x = mVal->GetX();
   float y = mVal->GetY();
-  
-  return NS_NewSVGPoint(_retval, a*x + c*y + e, b*x + d*y + f);
+
+  NS_ADDREF(*_retval = new DOMSVGPoint(a*x + c*y + e, b*x + d*y + f));
+  return NS_OK;
 }
 
 nsSVGElement::LengthInfo nsSVGSVGElement::sLengthInfo[4] =
 {
   { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::X },
   { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::Y },
   { &nsGkAtoms::width, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, nsSVGUtils::X },
   { &nsGkAtoms::height, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, nsSVGUtils::Y },
@@ -650,17 +651,18 @@ nsSVGSVGElement::CreateSVGAngle(nsIDOMSV
 {
   return NS_NewDOMSVGAngle(_retval);
 }
 
 /* nsIDOMSVGPoint createSVGPoint (); */
 NS_IMETHODIMP
 nsSVGSVGElement::CreateSVGPoint(nsIDOMSVGPoint **_retval)
 {
-  return NS_NewSVGPoint(_retval);
+  NS_ADDREF(*_retval = new DOMSVGPoint(0, 0));
+  return NS_OK;
 }
 
 /* nsIDOMSVGMatrix createSVGMatrix (); */
 NS_IMETHODIMP
 nsSVGSVGElement::CreateSVGMatrix(nsIDOMSVGMatrix **_retval)
 {
   return NS_NewSVGMatrix(_retval);
 }
--- a/content/svg/content/test/test_SVGxxxList.xhtml
+++ b/content/svg/content/test/test_SVGxxxList.xhtml
@@ -17,16 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     <filter>
       <feComponentTransfer>
         <feFuncR id="feFuncR" type="table"/>
       </feComponentTransfer>
     </filter>
   </desc>
   <text id="text">text</text>
   <path id="path"/>
+  <polyline id="polyline"/>
 </svg>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 <![CDATA[
 
 
 SimpleTest.waitForExplicitFinish();
@@ -142,36 +143,37 @@ var tests = [
     attr_val_5a: '3 4 5 6 7',
     attr_val_5b: '7 6 5 4 3',
     item_constructor: function() {
       // We need this function literal to avoid "Illegal operation on
       // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO.
       return document.getElementById('svg').createSVGNumber();
     }
   },
-/*
   {
     // SVGPointList test:
     target_element_id: 'polyline',
     attr_name: 'points',
     prop_name: null, // SVGAnimatedPoints is an inherited interface!
     bv_name: 'points',
     av_name: 'animatedPoints',
     el_type: 'SVGPolylineElement',
     prop_type: null,
     list_type: 'SVGPointList',
     item_type: 'SVGPoint',