Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Thu, 16 Feb 2012 15:06:30 -0800
changeset 105857 7d6904f186bcf0149c2391267b4d7529c7f326a8
parent 105856 8f0f05d821e514704e49e3ee5cfa482d8cc3768e (current diff)
parent 86981 78fde7e54d9275bacdae0d20536adf573d26e6a6 (diff)
child 105858 8aaf5f626c54aa48ac363ce4c32ad109f2125803
push id1075
push uservporof@mozilla.com
push dateThu, 13 Sep 2012 10:46:49 +0000
treeherderfx-team@f39786e8364d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
Merge from mozilla-central.
accessible/src/html/nsHyperTextAccessible.cpp
b2g/installer/package-manifest.in
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/base/content/tabbrowser.xml
browser/locales/en-US/searchplugins/google-params.inc
build/mobile/robocop/Actions.java.in
config/autoconf.mk.in
configure.in
content/base/public/nsIMessageWakeupService.idl
content/base/src/nsAttrAndChildArray.h
content/base/src/nsAttrValue.cpp
content/base/src/nsAttrValue.h
content/base/src/nsEventSource.cpp
content/base/src/nsEventSource.h
content/base/src/nsWebSocket.cpp
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/canvas/src/WebGLContext.h
content/html/content/src/nsDOMValidityState.h
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLInputElement.h
content/html/content/src/nsHTMLMediaElement.cpp
content/media/nsBuiltinDecoder.cpp
content/svg/content/src/DOMSVGLength.cpp
content/svg/content/src/DOMSVGNumber.cpp
content/svg/content/src/DOMSVGPathSeg.cpp
content/svg/content/src/DOMSVGPathSegList.cpp
content/svg/content/src/DOMSVGPoint.cpp
content/svg/content/src/DOMSVGPointList.cpp
content/svg/content/src/DOMSVGTransform.cpp
content/svg/content/src/DOMSVGTransform.h
content/svg/content/src/Makefile.in
content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
content/svg/content/src/SVGTransform.h
content/svg/content/src/nsSVGAngle.cpp
content/svg/content/src/nsSVGAngle.h
content/svg/content/src/nsSVGBoolean.cpp
content/svg/content/src/nsSVGBoolean.h
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGElement.h
content/svg/content/src/nsSVGEnum.cpp
content/svg/content/src/nsSVGEnum.h
content/svg/content/src/nsSVGInteger.cpp
content/svg/content/src/nsSVGIntegerPair.cpp
content/svg/content/src/nsSVGIntegerPair.h
content/svg/content/src/nsSVGLength2.cpp
content/svg/content/src/nsSVGLength2.h
content/svg/content/src/nsSVGMarkerElement.cpp
content/svg/content/src/nsSVGNumber2.cpp
content/svg/content/src/nsSVGNumberPair.cpp
content/svg/content/src/nsSVGNumberPair.h
content/svg/content/src/nsSVGViewBox.cpp
docshell/base/nsDocShell.cpp
dom/Makefile.in
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/dom-config.mk
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
gfx/layers/basic/BasicImages.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsfriendapi.cpp
js/src/jsgc.cpp
js/src/jsgcmark.cpp
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
layout/base/FrameLayerBuilder.cpp
layout/build/Makefile.in
layout/reftests/bugs/reftest.list
layout/style/nsCSSParser.cpp
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/test/Makefile.in
layout/svg/base/src/nsSVGForeignObjectFrame.cpp
layout/svg/base/src/nsSVGImageFrame.cpp
mobile/android/base/GeckoApp.java
mobile/android/base/Makefile.in
mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png
mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png
mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_placeholder.png
mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png
mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png
mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png
mobile/xul/installer/package-manifest.in
netwerk/protocol/http/SpdySession.cpp
netwerk/protocol/http/nsAHttpTransaction.h
netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnection.h
netwerk/protocol/http/nsHttpPipeline.cpp
netwerk/protocol/http/nsHttpTransaction.cpp
testing/testsuite-targets.mk
toolkit/components/places/tests/head_common.js
toolkit/content/widgets/videocontrols.css
toolkit/mozapps/installer/packager.mk
widget/android/nsAppShell.cpp
widget/cocoa/nsMenuItemX.h
widget/cocoa/nsMenuItemX.mm
widget/windows/nsNativeThemeWin.cpp
widget/windows/nsUXThemeData.cpp
widget/windows/nsUXThemeData.h
widget/windows/nsWindow.cpp
xpcom/glue/nsCycleCollectionParticipant.h
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -1626,16 +1626,19 @@ nsHyperTextAccessible::SetCaretOffset(PR
  * Gets the offset position of the caret (cursor).
  */
 NS_IMETHODIMP
 nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
 {
   NS_ENSURE_ARG_POINTER(aCaretOffset);
   *aCaretOffset = -1;
 
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
   // Not focused focusable accessible except document accessible doesn't have
   // a caret.
   if (!IsDoc() && !FocusMgr()->IsFocused(this) &&
       (State() & states::FOCUSABLE)) {
     return NS_OK;
   }
 
   // No caret if the focused node is not inside this DOM node and this DOM node
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -146,16 +146,19 @@
 @BINPATH@/components/dom.xpt
 @BINPATH@/components/dom_base.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_b2g.xpt
 #endif
 @BINPATH@/components/dom_battery.xpt
+#ifdef MOZ_B2G_BT
+@BINPATH@/components/dom_bluetooth.xpt
+#endif
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -352,16 +352,19 @@ pref("browser.search.log", false);
 // Ordering of Search Engines in the Engine list. 
 pref("browser.search.order.1",                "chrome://browser-region/locale/region.properties");
 pref("browser.search.order.2",                "chrome://browser-region/locale/region.properties");
 pref("browser.search.order.3",                "chrome://browser-region/locale/region.properties");
 
 // search bar results always open in a new tab
 pref("browser.search.openintab", false);
 
+// context menu searches open in the foreground
+pref("browser.search.context.loadInBackground", false);
+
 // send ping to the server to update
 pref("browser.search.update", true);
 
 // disable logging for the search service update system by default
 pref("browser.search.update.log", false);
 
 // Check whether we need to perform engine updates every 6 hours
 pref("browser.search.update.interval", 21600);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3550,20 +3550,21 @@ const BrowserSearch = {
 
     // getSubmission can return null if the engine doesn't have a URL
     // with a text/html response type.  This is unlikely (since
     // SearchService._addEngineToStore() should fail for such an engine),
     // but let's be on the safe side.
     if (!submission)
       return;
 
+    let inBackground = Services.prefs.getBoolPref("browser.search.context.loadInBackground");
     openLinkIn(submission.uri.spec,
                useNewTab ? "tab" : "current",
                { postData: submission.postData,
-                 inBackground: false,
+                 inBackground: inBackground,
                  relatedToCurrent: true });
   },
 
   /**
    * Returns the search bar element if it is present in the toolbar, null otherwise.
    */
   get searchBar() {
     return document.getElementById("searchbar");
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -531,17 +531,18 @@
                 // cancelled a pending load which would have cleared
                 // its anchor scroll detection temporary increment.
                 if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
                   this.mBrowser.userTypedClear += 2;
 
                 if (!this.mBlank) {
                   if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
                     this.mTab.setAttribute("busy", "true");
-                    this.mTabBrowser.setTabTitleLoading(this.mTab);
+                    if (!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
+                      this.mTabBrowser.setTabTitleLoading(this.mTab);
                   }
 
                   if (this.mTab.selected)
                     this.mTabBrowser.mIsBusy = true;
                 }
               }
               else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
                        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
@@ -3560,18 +3561,20 @@
             } catch(ex) {
               // Just ignore invalid urls
             }
           }
         }
 
         // these offsets are only used in dragend, but we need to free them here
         // as well
-        delete draggedTab._dragOffsetX;
-        delete draggedTab._dragOffsetY;
+        if (draggedTab) {
+          delete draggedTab._dragOffsetX;
+          delete draggedTab._dragOffsetY;
+        }
       ]]></handler>
 
       <handler event="dragend"><![CDATA[
         // Note: while this case is correctly handled here, this event
         // isn't dispatched when the tab is moved within the tabstrip,
         // see bug 460801.
 
         // * mozUserCancelled = the user pressed ESC to cancel the drag
@@ -3976,34 +3979,28 @@
                    xbl:inherits="value=label,crop,mirror"
                    flex="1"
                    crop="end"/>
       </xul:hbox>
     </content>
 
     <implementation implements="nsIDOMEventListener">
       <constructor><![CDATA[
-        window.addEventListener("findbaropen", this, false);
         window.addEventListener("resize", this, false);
       ]]></constructor>
 
       <destructor><![CDATA[
-        window.removeEventListener("findbaropen", this, false);
         window.removeEventListener("resize", this, false);
         MousePosTracker.removeListener(this);
       ]]></destructor>
 
       <property name="label">
         <setter><![CDATA[
-          if (!this.label) {
-            if (window.gFindBarInitialized && !window.gFindBar.hidden)
-              this.setAttribute("mirror", "true");
-            else
-              this.removeAttribute("mirror");
-          }
+          if (!this.label)
+            this.removeAttribute("mirror");
 
           this.style.minWidth = this.getAttribute("type") == "status" &&
                                 this.getAttribute("previoustype") == "status"
                                   ? getComputedStyle(this).width : "";
 
           if (val) {
             this.setAttribute("label", val);
             this.removeAttribute("inactive");
@@ -4041,30 +4038,26 @@
 
       <method name="handleEvent">
         <parameter name="event"/>
         <body><![CDATA[
           if (!this.label)
             return;
 
           switch (event.type) {
-            case "findbaropen":
-              this.setAttribute("mirror", "true");
-              this._calcMouseTargetRect();
-              break;
             case "resize":
               this._calcMouseTargetRect();
               break;
           }
         ]]></body>
       </method>
 
       <method name="_calcMouseTargetRect">
         <body><![CDATA[
-          let alignRight = (window.gFindBarInitialized && !window.gFindBar.hidden);
+          let alignRight = false;
 
           if (getComputedStyle(document.documentElement).direction == "rtl")
             alighRight = !alignRight;
 
           let rect = this.getBoundingClientRect();
           this._mouseTargetRect = {
             top:    rect.top,
             bottom: rect.bottom,
--- a/browser/base/content/test/authenticate.sjs
+++ b/browser/base/content/test/authenticate.sjs
@@ -10,34 +10,36 @@ function handleRequest(request, response
 
 
 function reallyHandleRequest(request, response) {
   var match;
   var requestAuth = true, requestProxyAuth = true;
 
   // Allow the caller to drive how authentication is processed via the query.
   // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar
-  var query = request.queryString;
+  // The extra ? allows the user/pass/realm checks to succeed if the name is
+  // at the beginning of the query string.
+  var query = "?" + request.queryString;
 
   var expected_user = "", expected_pass = "", realm = "mochitest";
   var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy";
-  var huge = false, plugin = false;
+  var huge = false, plugin = false, anonymous = false;
   var authHeaderCount = 1;
   // user=xxx
-  match = /user=([^&]*)/.exec(query);
+  match = /[^_]user=([^&]*)/.exec(query);
   if (match)
     expected_user = match[1];
 
   // pass=xxx
-  match = /pass=([^&]*)/.exec(query);
+  match = /[^_]pass=([^&]*)/.exec(query);
   if (match)
     expected_pass = match[1];
 
   // realm=xxx
-  match = /realm=([^&]*)/.exec(query);
+  match = /[^_]realm=([^&]*)/.exec(query);
   if (match)
     realm = match[1];
 
   // proxy_user=xxx
   match = /proxy_user=([^&]*)/.exec(query);
   if (match)
     proxy_expected_user = match[1];
 
@@ -61,26 +63,31 @@ function reallyHandleRequest(request, re
   if (match)
     plugin = true;
 
   // multiple=1
   match = /multiple=([^&]*)/.exec(query);
   if (match)
     authHeaderCount = match[1]+0;
 
+  // anonymous=1
+  match = /anonymous=1/.exec(query);
+  if (match)
+    anonymous = true;
 
   // Look for an authentication header, if any, in the request.
   //
   // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
   // 
   // This test only supports Basic auth. The value sent by the client is
   // "username:password", obscured with base64 encoding.
 
-  var actual_user = "", actual_pass = "", authHeader;
+  var actual_user = "", actual_pass = "", authHeader, authPresent = false;
   if (request.hasHeader("Authorization")) {
+    authPresent = true;
     authHeader = request.getHeader("Authorization");
     match = /Basic (.+)/.exec(authHeader);
     if (match.length != 2)
         throw "Couldn't parse auth header: " + authHeader;
 
     var userpass = base64ToString(match[1]); // no atob() :-(
     match = /(.*):(.*)/.exec(userpass);
     if (match.length != 3)
@@ -110,26 +117,34 @@ function reallyHandleRequest(request, re
     expected_pass == actual_pass) {
     requestAuth = false;
   }
   if (proxy_expected_user == proxy_actual_user &&
     proxy_expected_pass == proxy_actual_pass) {
     requestProxyAuth = false;
   }
 
-  if (requestProxyAuth) {
-    response.setStatusLine("1.0", 407, "Proxy authentication required");
-    for (i = 0; i < authHeaderCount; ++i)
-      response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true);
-  } else if (requestAuth) {
-    response.setStatusLine("1.0", 401, "Authentication required");
-    for (i = 0; i < authHeaderCount; ++i)
-      response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
+  if (anonymous) {
+    if (authPresent) {
+      response.setStatusLine("1.0", 400, "Unexpected authorization header found");
+    } else {
+      response.setStatusLine("1.0", 200, "Authorization header not found");
+    }
   } else {
-    response.setStatusLine("1.0", 200, "OK");
+    if (requestProxyAuth) {
+      response.setStatusLine("1.0", 407, "Proxy authentication required");
+      for (i = 0; i < authHeaderCount; ++i)
+        response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true);
+    } else if (requestAuth) {
+      response.setStatusLine("1.0", 401, "Authentication required");
+      for (i = 0; i < authHeaderCount; ++i)
+        response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
+    } else {
+      response.setStatusLine("1.0", 200, "OK");
+    }
   }
 
   response.setHeader("Content-Type", "application/xhtml+xml", false);
   response.write("<html xmlns='http://www.w3.org/1999/xhtml'>");
   response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n");
   response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n");
   response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n");
   response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n");
--- a/browser/base/content/test/browser_homeDrop.js
+++ b/browser/base/content/test/browser_homeDrop.js
@@ -23,24 +23,28 @@ function test() {
     ok(true, "dialog appeared in response to home button drop");
     domwindow.document.documentElement.cancelDialog();
     Services.wm.removeListener(dialogListener);
 
     // Now trigger the invalid URI test
     executeSoon(function () {
       let consoleListener = {
         observe: function (m) {
+          info("m: " + m + "\n");
+          info("m.message: " + m.message + "\n");
           if (m.message.indexOf("NS_ERROR_DOM_BAD_URI") > -1) {
-            Services.console.unregisterListener(consoleListener);
             ok(true, "drop was blocked");
             executeSoon(finish);
           }
         }
       }
       Services.console.registerListener(consoleListener);
+      registerCleanupFunction(function () {
+        Services.console.unregisterListener(consoleListener);
+      });
 
       // The drop handler throws an exception when dragging URIs that inherit
       // principal, e.g. javascript:
       expectUncaughtException();
       chromeUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "javascript:8888"}]], "copy", window, EventUtils);
     })
   });
 
--- a/browser/devtools/debugger/test/browser_dbg_select-line.js
+++ b/browser/devtools/debugger/test/browser_dbg_select-line.js
@@ -38,44 +38,48 @@ function testSelectLine() {
       is(gDebugger.StackFrames.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(gScripts.itemCount, 2, "Found the expected number of scripts.");
 
       ok(gDebugger.editor.getText().search(/debugger/) != -1,
         "The correct script was loaded initially.");
 
-      // getCaretPosition is 0-based.
-      is(gDebugger.editor.getCaretPosition().line, 5,
-         "The correct line is selected.");
+      // Yield control back to the event loop so that the debugger has a
+      // chance to highlight the proper line.
+      executeSoon(function(){
+        // getCaretPosition is 0-based.
+        is(gDebugger.editor.getCaretPosition().line, 5,
+           "The correct line is selected.");
 
-      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                        function onChange() {
-        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                             onChange);
-        ok(gDebugger.editor.getText().search(/debugger/) == -1,
-          "The second script is no longer displayed.");
+        gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                          function onChange() {
+          gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                               onChange);
+          ok(gDebugger.editor.getText().search(/debugger/) == -1,
+            "The second script is no longer displayed.");
 
-        ok(gDebugger.editor.getText().search(/firstCall/) != -1,
-          "The first script is displayed.");
+          ok(gDebugger.editor.getText().search(/firstCall/) != -1,
+            "The first script is displayed.");
 
-        // Yield control back to the event loop so that the debugger has a
-        // chance to highlight the proper line.
-        executeSoon(function(){
-          // getCaretPosition is 0-based.
-          is(gDebugger.editor.getCaretPosition().line, 4,
-             "The correct line is selected.");
+          // Yield control back to the event loop so that the debugger has a
+          // chance to highlight the proper line.
+          executeSoon(function(){
+            // getCaretPosition is 0-based.
+            is(gDebugger.editor.getCaretPosition().line, 4,
+               "The correct line is selected.");
 
-          gDebugger.StackFrames.activeThread.resume(function() {
-            removeTab(gTab);
-            finish();
+            gDebugger.StackFrames.activeThread.resume(function() {
+              removeTab(gTab);
+              finish();
+            });
           });
         });
+
+        // Click the oldest stack frame.
+        let element = gDebugger.document.getElementById("stackframe-3");
+        EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger);
       });
-
-      // Click the oldest stack frame.
-      let element = gDebugger.document.getElementById("stackframe-3");
-      EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger);
     }}, 0);
   });
 
   gDebuggee.firstCall();
 }
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -145,16 +145,19 @@
 @BINPATH@/components/dom.xpt
 @BINPATH@/components/dom_base.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_b2g.xpt
 #endif
 @BINPATH@/components/dom_battery.xpt
+#ifdef MOZ_B2G_BT
+@BINPATH@/components/dom_bluetooth.xpt
+#endif
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
@@ -393,16 +396,18 @@
 @BINPATH@/components/nsPrompter.manifest
 @BINPATH@/components/nsPrompter.js
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/components/SyncComponents.manifest
 @BINPATH@/components/Weave.js
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
+@BINPATH@/components/messageWakeupService.js
+@BINPATH@/components/messageWakeupService.manifest
 
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsSafebrowsingApplication.manifest
 @BINPATH@/components/nsSafebrowsingApplication.js
 @BINPATH@/components/nsURLClassifier.manifest
deleted file mode 100644
--- a/browser/locales/en-US/searchplugins/google-params.inc
+++ /dev/null
@@ -1,15 +0,0 @@
-  <Param name="q" value="{searchTerms}"/>
-  <Param name="ie" value="utf-8"/>
-  <Param name="oe" value="utf-8"/>
-  <Param name="aq" value="t"/>
-  <!-- Dynamic parameters -->
-  <Param name="rls" value="{moz:distributionID}:{moz:locale}:{moz:official}"/>
-#if MOZ_UPDATE_CHANNEL == beta
-  <MozParam name="client" condition="defaultEngine" trueValue="firefox-beta" falseValue="firefox"/>
-#elif MOZ_UPDATE_CHANNEL == aurora
-  <MozParam name="client" condition="defaultEngine" trueValue="firefox-aurora" falseValue="firefox"/>
-#elif MOZ_UPDATE_CHANNEL == nightly
-  <MozParam name="client" condition="defaultEngine" trueValue="firefox-nightly" falseValue="firefox"/>
-#else
-  <MozParam name="client" condition="defaultEngine" trueValue="firefox-a" falseValue="firefox"/>
-#endif
--- a/browser/locales/en-US/searchplugins/google.xml
+++ b/browser/locales/en-US/searchplugins/google.xml
@@ -1,21 +1,34 @@
+#define GOOGLE_PARAMS <Param name="q" value="{searchTerms}"/><Param name="ie" value="utf-8"/><Param name="oe" value="utf-8"/><Param name="aq" value="t"/><Param name="rls" value="{moz:distributionID}:{moz:locale}:{moz:official}"/>
+#if MOZ_UPDATE_CHANNEL == beta
+#define GOOGLE_CLIENT_PARAM <MozParam name="client" condition="defaultEngine" trueValue="firefox-beta" falseValue="firefox"/>
+#elif MOZ_UPDATE_CHANNEL == aurora
+#define GOOGLE_CLIENT_PARAM <MozParam name="client" condition="defaultEngine" trueValue="firefox-aurora" falseValue="firefox"/>
+#elif MOZ_UPDATE_CHANNEL == nightly
+#define GOOGLE_CLIENT_PARAM <MozParam name="client" condition="defaultEngine" trueValue="firefox-nightly" falseValue="firefox"/>
+#else
+#define GOOGLE_CLIENT_PARAM <MozParam name="client" condition="defaultEngine" trueValue="firefox-a" falseValue="firefox"/>
+#endif
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Google</ShortName>
 <Description>Google Search</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">data:image/png;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
 <Url type="application/x-suggestions+json" method="GET" template="http://suggestqueries.google.com/complete/search?output=firefox&amp;client=firefox&amp;hl={moz:locale}&amp;q={searchTerms}"/>
 <Url type="text/html" method="GET" template="http://www.google.com/search">
-#include google-params.inc
+#expand   __GOOGLE_PARAMS__
+#expand   __GOOGLE_CLIENT_PARAM__
 </Url>
 <!-- Keyword search URL is the same as the default, but with an additional parameter -->
 <Url type="application/x-moz-keywordsearch" method="GET" template="http://www.google.com/search">
-#include google-params.inc
+#expand   __GOOGLE_PARAMS__
+#expand   __GOOGLE_CLIENT_PARAM__
   <Param name="channel" value="fflb"/>
 </Url>
 <!-- Context/Right-click search URL is the same as the default, but with an additional parameter -->
 <Url type="application/x-moz-contextsearch" method="GET" template="http://www.google.com/search">
-#include google-params.inc
+#expand   __GOOGLE_PARAMS__
+#expand   __GOOGLE_CLIENT_PARAM__
   <Param name="channel" value="rcs"/>
 </Url>
 <SearchForm>http://www.google.com/</SearchForm>
 </SearchPlugin>
--- a/build/mobile/robocop/Actions.java.in
+++ b/build/mobile/robocop/Actions.java.in
@@ -36,48 +36,60 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 package @ANDROID_PACKAGE_NAME@;
 import java.util.List;
 
 public interface Actions {
-  public enum SpecialKey {
-    DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK
-  }
+
+    /** Special keys supported by sendSpecialKey() */
+    public enum SpecialKey {
+        DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK
+    }
 
-  public interface EventExpecter {
-    /** Blocks until the event has been received. Subsequent calls will return immediately. */
-    public void blockForEvent();
-    /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */
-    public boolean eventReceived();
-  }
+    public interface EventExpecter {
+        /** Blocks until the event has been received. Subsequent calls will return immediately. */
+        public void blockForEvent();
 
-  public interface RepeatedEventExpecter extends EventExpecter {
-    /** Blocks until at least one event has been received, and no events have been received in the last <code>millis</code> milliseconds. */
-    public void blockUntilClear(long millis);
-  }
+        /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */
+        public boolean eventReceived();
+    }
+
+    public interface RepeatedEventExpecter extends EventExpecter {
+        /** Blocks until at least one event has been received, and no events have been received in the last <code>millis</code> milliseconds. */
+        public void blockUntilClear(long millis);
+    }
 
-  /**
-   * Listens for a gecko event to be sent from the Gecko instance.
-   * The returned object can be used to test if the event has been
-   * received. Note that only one event is listened for.
-   * 
-   * @param geckoEvent The geckoEvent JSONObject's type
-   */
-  EventExpecter expectGeckoEvent(String geckoEvent);
+    /**
+     * Listens for a gecko event to be sent from the Gecko instance.
+     * The returned object can be used to test if the event has been
+     * received. Note that only one event is listened for.
+     * 
+     * @param geckoEvent The geckoEvent JSONObject's type
+     */
+    EventExpecter expectGeckoEvent(String geckoEvent);
+
+    /**
+     * Listens for a paint event. Note that calling expectPaint() will
+     * invalidate the event expecters returned from any previous calls
+     * to expectPaint(); calling any methods on those invalidated objects
+     * will result in undefined behaviour.
+     */
+    RepeatedEventExpecter expectPaint();
 
-  /**
-   * Listens for a paint event. Note that calling expectPaint() will
-   * invalidate the event expecters returned from any previous calls
-   * to expectPaint(); calling any methods on those invalidated objects
-   * will result in undefined behaviour.
-   */
-  RepeatedEventExpecter expectPaint();
+    /** 
+     * Send a string to the application 
+     * 
+     * @param keysToSend The string to send
+     */
+    void sendKeys(String keysToSend);
 
-  // Send the string kewsToSend to the application 
-  void sendKeys(String keysToSend);
-  //Send any of the above keys to the element
-  void sendSpecialKey(SpecialKey button);
+    /**
+     * Send a special keycode to the element
+     *
+     * @param key The special key to send
+     */
+    void sendSpecialKey(SpecialKey key);
 
-  void drag(int startingX, int endingX, int startingY, int endingY);
+    void drag(int startingX, int endingX, int startingY, int endingY);
 }
--- a/build/mobile/robocop/Assert.java.in
+++ b/build/mobile/robocop/Assert.java.in
@@ -35,24 +35,24 @@
  * 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 ***** */
 
 package @ANDROID_PACKAGE_NAME@;
 
 public interface Assert {
-  void dumpLog(String message);
-  void setLogFile(String filename);
-  void setTestName(String testName);
+    void dumpLog(String message);
+    void setLogFile(String filename);
+    void setTestName(String testName);
 
-  void finalize();
-  void ok(boolean condition, String name, String diag);
-  void is(Object a, Object b, String name);
-  void isnot(Object a, Object b, String name);
-  void todo(boolean condition, String name, String diag);
-  void todo_is(Object a, Object b, String name);
-  void todo_isnot(Object a, Object b, String name);
-  void info(String name, String message);
+    void finalize();
+    void ok(boolean condition, String name, String diag);
+    void is(Object a, Object b, String name);
+    void isnot(Object a, Object b, String name);
+    void todo(boolean condition, String name, String diag);
+    void todo_is(Object a, Object b, String name);
+    void todo_isnot(Object a, Object b, String name);
+    void info(String name, String message);
 
-  // robocop-specific asserts
-  void ispixel(int actual, int r, int g, int b, String name);
+    // robocop-specific asserts
+    void ispixel(int actual, int r, int g, int b, String name);
 }
--- a/build/mobile/robocop/Driver.java.in
+++ b/build/mobile/robocop/Driver.java.in
@@ -38,44 +38,44 @@
  * ***** END LICENSE BLOCK ***** */
 
 package @ANDROID_PACKAGE_NAME@;
 
 import java.util.List;
 import android.app.Activity;
 
 public interface Driver {
-  /**
-   * Find the first Element using the given method.
-   * 
-   * @param activity The activity the element belongs to
-   * @param name The name of the element
-   * @return The first matching element on the current context
-   * @throws RoboCopException If no matching elements are found
-   */
-  Element findElement(Activity activity, String name);
+    /**
+     * Find the first Element using the given method.
+     * 
+     * @param activity The activity the element belongs to
+     * @param name The name of the element
+     * @return The first matching element on the current context
+     * @throws RoboCopException If no matching elements are found
+     */
+    Element findElement(Activity activity, String name);
 
-  /**
-   * Sets up scroll handling so that data is received from the extension.
-   */
-  void setupScrollHandling();
+    /**
+     * Sets up scroll handling so that data is received from the extension.
+     */
+    void setupScrollHandling();
 
-  int getPageHeight();
-  int getScrollHeight();
-  int getHeight();
-  int getGeckoTop();
-  int getGeckoLeft();
-  int getGeckoWidth();
-  int getGeckoHeight();
+    int getPageHeight();
+    int getScrollHeight();
+    int getHeight();
+    int getGeckoTop();
+    int getGeckoLeft();
+    int getGeckoWidth();
+    int getGeckoHeight();
 
-  void startFrameRecording();
-  int stopFrameRecording();
+    void startFrameRecording();
+    int stopFrameRecording();
 
-  void startCheckerboardRecording();
-  float stopCheckerboardRecording();
+    void startCheckerboardRecording();
+    float stopCheckerboardRecording();
 
-  /**
-   * Get a copy of the painted content region.
-   * @return A 2-D array of pixels (indexed by y, then x). The pixels
-   * are in ARGB-8888 format.
-   */
-  int[][] getPaintedSurface();
+    /**
+     * Get a copy of the painted content region.
+     * @return A 2-D array of pixels (indexed by y, then x). The pixels
+     * are in ARGB-8888 format.
+     */
+    int[][] getPaintedSurface();
 }
--- a/build/mobile/robocop/Element.java.in
+++ b/build/mobile/robocop/Element.java.in
@@ -34,18 +34,26 @@
  * 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 ***** */
 
 package @ANDROID_PACKAGE_NAME@;
 
+/** 
+ *  Element provides access to a specific UI view (android.view.View). 
+ *  See also Driver.findElement().
+ */
 public interface Element {
-  //Click on the element
-  void click();
-  //Returns true if the element is currently displayed
-  boolean isDisplayed();
-  //Returns the text currently displayed on the element.
-  String getText();
-  //Returns view ID.
-  Integer getId();
+
+    /** Click on the element */
+    void click();
+
+    /** Returns true if the element is currently displayed */
+    boolean isDisplayed();
+
+    /** Returns the text currently displayed on the element */
+    String getText();
+
+    /** Returns the view ID */
+    Integer getId();
 }
--- a/build/mobile/robocop/FennecMochitestAssert.java.in
+++ b/build/mobile/robocop/FennecMochitestAssert.java.in
@@ -33,207 +33,199 @@
  * 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 ***** */
 
 package @ANDROID_PACKAGE_NAME@;
 
 import java.util.LinkedList;
-import java.util.List;
-import java.util.Date;
+import android.os.SystemClock;
 
 public class FennecMochitestAssert implements Assert {
-  // Objects for reflexive access of fennec classes.
-
-  private LinkedList<testInfo> testList = new LinkedList<testInfo>();
+    private LinkedList<testInfo> mTestList = new LinkedList<testInfo>();
 
-  // Internal state variables to make logging match up with existing mochitests
-  private int lineNumber = 0;
-  private int passed = 0;
-  private int failed = 0;
-  private int todo = 0;
-  
-  // Used to write the first line of the test file
-  private boolean logStarted = false;
-
-  // Used to write the test-start/test-end log lines
-  private String logTestName = "";
-
-  // Measure the time it takes to run test case
-  private long startTime = 0;
+    // Internal state variables to make logging match up with existing mochitests
+    private int mLineNumber = 0;
+    private int mPassed = 0;
+    private int mFailed = 0;
+    private int mTodo = 0;
+    
+    // Used to write the first line of the test file
+    private boolean mLogStarted = false;
 
-  public FennecMochitestAssert() {
-  }
+    // Used to write the test-start/test-end log lines
+    private String mLogTestName = "";
 
-  // Write information to a logfile and logcat
-  public void dumpLog(String message)
-  {
-    FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
-  }
+    // Measure the time it takes to run test case
+    private long mStartTime = 0;
 
-  // Set the filename used for dumpLog.
-  public void setLogFile(String filename)
-  {
-    FennecNativeDriver.setLogFile(filename);
+    public FennecMochitestAssert() {
+    }
 
-    String message;
-    if (!logStarted) {
-      dumpLog(Integer.toString(lineNumber++) + " INFO SimpleTest START");
-      logStarted = true;
+    /** Write information to a logfile and logcat */
+    public void dumpLog(String message) {
+        FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
     }
 
-    if (logTestName != "") {
-      long diff = (new Date().getTime()) - startTime;
-      message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName;
-      message += " | finished in " + diff + "ms";
-      dumpLog(message);
-      logTestName = "";
+    /** Set the filename used for dumpLog. */
+    public void setLogFile(String filename) {
+        FennecNativeDriver.setLogFile(filename);
+
+        String message;
+        if (!mLogStarted) {
+            dumpLog(Integer.toString(mLineNumber++) + " INFO SimpleTest START");
+            mLogStarted = true;
+        }
+
+        if (mLogTestName != "") {
+            long diff = SystemClock.uptimeMillis() - mStartTime;
+            message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
+            message += " | finished in " + diff + "ms";
+            dumpLog(message);
+            mLogTestName = "";
+        }
     }
-  }
 
-  public void setTestName(String testName)
-  {
-    String[] nameParts = testName.split("\\.");
-    logTestName = nameParts[nameParts.length - 1];
-    startTime = new Date().getTime();
+    public void setTestName(String testName) {
+        String[] nameParts = testName.split("\\.");
+        mLogTestName = nameParts[nameParts.length - 1];
+        mStartTime = SystemClock.uptimeMillis();
 
-    dumpLog(Integer.toString(lineNumber++) + " INFO TEST-START | " + logTestName);
-  }
+        dumpLog(Integer.toString(mLineNumber++) + " INFO TEST-START | " + mLogTestName);
+    }
 
-  class testInfo {
-    public boolean result;
-    public String name;
-    public String diag;
-    public boolean todo;
-    public testInfo(boolean r, String n, String d, boolean t) {
-      result = r;
-      name = n;
-      diag = d;
-      todo = t;
+    class testInfo {
+        public boolean mResult;
+        public String mName;
+        public String mDiag;
+        public boolean mTodo;
+        public testInfo(boolean r, String n, String d, boolean t) {
+            mResult = r;
+            mName = n;
+            mDiag = d;
+            mTodo = t;
+        }
+
     }
 
-  }
+    private void _logMochitestResult(testInfo test, String passString, String failString) {
+        boolean isError = true;
+        String resultString = failString;
+        if (test.mResult || test.mTodo) {
+            isError = false;
+        }
+        if (test.mResult)
+        {
+            resultString = passString;
+        }
+        String diag = test.mName;
+        if (test.mDiag != null) diag += " - " + test.mDiag;
 
-  private void _logMochitestResult(testInfo test, String passString, String failString)
-  {
-    boolean isError = true;
-    String resultString = failString;
-    if (test.result || test.todo) {
-      isError = false;
+        String message = Integer.toString(mLineNumber++) + " INFO " + resultString + " | " + mLogTestName + " | " + diag;
+        dumpLog(message);
+
+        if (test.mTodo) {
+            mTodo++;
+        } else if (isError) {
+            mFailed++;
+        } else {
+            mPassed++;
+        }
+        if (isError) {
+            junit.framework.Assert.fail(message);
+        }
     }
-    if (test.result)
-    {
-      resultString = passString;
-    }
-    String diag = test.name;
-    if (test.diag != null) diag += " - " + test.diag;
-
-    String message = Integer.toString(lineNumber++) + " INFO " + resultString + " | " + logTestName + " | " + diag;
-    dumpLog(message);
 
-    if (test.todo) {
-      todo++;
-    } else if (isError) {
-      failed++;
-    } else {
-      passed++;
-    }
-    if (isError) {
-      junit.framework.Assert.fail(message);
-    }
-  }
+    public void finalize() {
+        // It appears that we call finalize during cleanup, this might be an invalid assertion.
+        String message;
+
+        if (mLogTestName != "") {
+            long diff = SystemClock.uptimeMillis() - mStartTime;
+            message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
+            message += " | finished in " + diff + "ms";
+            dumpLog(message);
+            mLogTestName = "";
+        }
 
-  public void finalize()
-  {
-    // It appears that we call finalize during cleanup, this might be an invalid assertion.
-    String message;
+        message = Integer.toString(mLineNumber++) + " INFO TEST-START | Shutdown";
+        dumpLog(message);
+        message = Integer.toString(mLineNumber++) + " INFO Passed: " + Integer.toString(mPassed);
+        dumpLog(message);
+        message = Integer.toString(mLineNumber++) + " INFO Failed: " + Integer.toString(mFailed);
+        dumpLog(message);
+        message = Integer.toString(mLineNumber++) + " INFO Todo: " + Integer.toString(mTodo);
+        dumpLog(message);
+        message = Integer.toString(mLineNumber++) + " INFO SimpleTest FINISHED";
+        dumpLog(message);
+    }
 
-    if (logTestName != "") {
-      long diff = (new Date().getTime()) - startTime;
-      message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName;
-      message += " | finished in " + diff + "ms";
-      dumpLog(message);
-      logTestName = "";
+    public void ok(boolean condition, String name, String diag) {
+        testInfo test = new testInfo(condition, name, diag, false);
+        _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
+        mTestList.add(test);
     }
 
-    message = Integer.toString(lineNumber++) + " INFO TEST-START | Shutdown";
-    dumpLog(message);
-    message = Integer.toString(lineNumber++) + " INFO Passed: " + Integer.toString(passed);
-    dumpLog(message);
-    message = Integer.toString(lineNumber++) + " INFO Failed: " + Integer.toString(failed);
-    dumpLog(message);
-    message = Integer.toString(lineNumber++) + " INFO Todo: " + Integer.toString(todo);
-    dumpLog(message);
-    message = Integer.toString(lineNumber++) + " INFO SimpleTest FINISHED";
-    dumpLog(message);
-  }
-
-  public void ok(boolean condition, String name, String diag) {
-    testInfo test = new testInfo(condition, name, diag, false);
-    _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
-    testList.add(test);
-  }
-
-  public void is(Object a, Object b, String name) {
-    boolean pass = a.equals(b);
-    String diag = "got " + a.toString() + ", expected " + b.toString();
-    if(pass) {
-      diag = a.toString() + " should equal " + b.toString();
+    public void is(Object a, Object b, String name) {
+        boolean pass = a.equals(b);
+        String diag = "got " + a.toString() + ", expected " + b.toString();
+        if (pass) {
+            diag = a.toString() + " should equal " + b.toString();
+        }
+        ok(pass, name, diag);
     }
-    ok(pass, name, diag);
-  }
-  
-  public void isnot(Object a, Object b, String name) {
-    boolean pass = !a.equals(b);
-    String diag = "didn't expect " + a.toString() + ", but got it";
-    if(pass) {
-      diag = a.toString() + " should not equal " + b.toString();
+    
+    public void isnot(Object a, Object b, String name) {
+        boolean pass = !a.equals(b);
+        String diag = "didn't expect " + a.toString() + ", but got it";
+        if (pass) {
+            diag = a.toString() + " should not equal " + b.toString();
+        }
+        ok(pass, name, diag);
     }
-    ok(pass, name, diag);
-  }
 
-  public void ispixel(int actual, int r, int g, int b, String name) {
-    // When we read GL pixels the GPU has already processed them and they
-    // are usually off by a little bit. For example a CSS-color pixel of color #64FFF5
-    // was turned into #63FFF7 when it came out of glReadPixels. So in order to compare
-    // against the expected value, we use a little fuzz factor. For the alpha we just
-    // make sure it is always 0xFF.
-    int aAlpha = ((actual >> 24) & 0xFF);
-    int aR = ((actual >> 16) & 0xFF);
-    int aG = ((actual >> 8) & 0xFF);
-    int aB = (actual & 0xFF);
-    boolean pass = (aAlpha == 0xFF) /* alpha */
-                && (Math.abs(aR - r) < 8) /* red */
-                && (Math.abs(aG - g) < 8) /* green */
-                && (Math.abs(aB - b) < 8); /* blue */
-    ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")");
-  }
+    public void ispixel(int actual, int r, int g, int b, String name) {
+        // When we read GL pixels the GPU has already processed them and they
+        // are usually off by a little bit. For example a CSS-color pixel of color #64FFF5
+        // was turned into #63FFF7 when it came out of glReadPixels. So in order to compare
+        // against the expected value, we use a little fuzz factor. For the alpha we just
+        // make sure it is always 0xFF.
+        int aAlpha = ((actual >> 24) & 0xFF);
+        int aR = ((actual >> 16) & 0xFF);
+        int aG = ((actual >> 8) & 0xFF);
+        int aB = (actual & 0xFF);
+        boolean pass = (aAlpha == 0xFF) /* alpha */
+                                && (Math.abs(aR - r) < 8) /* red */
+                                && (Math.abs(aG - g) < 8) /* green */
+                                && (Math.abs(aB - b) < 8); /* blue */
+        ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")");
+    }
 
-  public void todo(boolean condition, String name, String diag) {
-    testInfo test = new testInfo(condition, name, diag, true);
-    _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
-    testList.add(test);
-  }
+    public void todo(boolean condition, String name, String diag) {
+        testInfo test = new testInfo(condition, name, diag, true);
+        _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
+        mTestList.add(test);
+    }
 
-  public void todo_is(Object a, Object b, String name) {
-    boolean pass = a.equals(b);
-    String diag = "got " + a.toString() + ", expected " + b.toString();
-    if(pass) {
-      diag = a.toString() + " should equal " + b.toString();
+    public void todo_is(Object a, Object b, String name) {
+        boolean pass = a.equals(b);
+        String diag = "got " + a.toString() + ", expected " + b.toString();
+        if (pass) {
+            diag = a.toString() + " should equal " + b.toString();
+        }
+        todo(pass, name, diag);
     }
-    todo(pass, name, diag);
-  }
-  
-  public void todo_isnot(Object a, Object b, String name) {
-    boolean pass = !a.equals(b);
-    String diag = "didn't expect " + a.toString() + ", but got it";
-    if(pass) {
-      diag = a.toString() + " should not equal " + b.toString();
+    
+    public void todo_isnot(Object a, Object b, String name) {
+        boolean pass = !a.equals(b);
+        String diag = "didn't expect " + a.toString() + ", but got it";
+        if (pass) {
+            diag = a.toString() + " should not equal " + b.toString();
+        }
+        todo(pass, name, diag);
     }
-    todo(pass, name, diag);
-  }
 
-  public void info(String name, String message) {
-    testInfo test = new testInfo(true, name, message, false);
-    _logMochitestResult(test, "TEST-INFO", "INFO FAILED?");
-  }
+    public void info(String name, String message) {
+        testInfo test = new testInfo(true, name, message, false);
+        _logMochitestResult(test, "TEST-INFO", "INFO FAILED?");
+    }
 }
--- a/build/mobile/robocop/FennecNativeActions.java.in
+++ b/build/mobile/robocop/FennecNativeActions.java.in
@@ -54,300 +54,299 @@ import android.view.KeyEvent;
 
 import java.util.concurrent.SynchronousQueue;
 
 import org.json.*;
 
 import com.jayway.android.robotium.solo.Solo;
 
 public class FennecNativeActions implements Actions {
-  private Solo solo;
-  private Instrumentation instr;
-  private Activity geckoApp;
+    private Solo mSolo;
+    private Instrumentation mInstr;
+    private Activity mGeckoApp;
 
-  // Objects for reflexive access of fennec classes.
-  private ClassLoader classLoader;
-  private Class gel;
-  private Class ge;
-  private Class gas;
-  private Class drawListener;
-  private Method registerGEL;
-  private Method unregisterGEL;
-  private Method sendGE;
-  private Method getLayerClient;
-  private Method setDrawListener;
+    // Objects for reflexive access of fennec classes.
+    private ClassLoader mClassLoader;
+    private Class mGel;
+    private Class mGe;
+    private Class mGas;
+    private Class mDrawListener;
+    private Method mRegisterGEL;
+    private Method mUnregisterGEL;
+    private Method mSendGE;
+    private Method mGetLayerClient;
+    private Method mSetDrawListener;
 
-  public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){
-    this.solo = robocop;
-    this.instr = instrumentation;
-    this.geckoApp = activity;
-    // Set up reflexive access of java classes and methods.
-    try {
-      classLoader = activity.getClassLoader();
-      gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
-      ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
-      gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
-      Class [] parameters = new Class[2];
-      parameters[0] = String.class;
-      parameters[1] = gel;
-      registerGEL = gas.getMethod("registerGeckoEventListener", parameters);
-      unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
-      parameters = new Class[1];
-      parameters[0] = ge;
-      sendGE = gas.getMethod("sendEventToGecko", parameters);
+    public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation) {
+        mSolo = robocop;
+        mInstr = instrumentation;
+        mGeckoApp = activity;
+        // Set up reflexive access of java classes and methods.
+        try {
+            mClassLoader = activity.getClassLoader();
+            mGel = mClassLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
+            mGe = mClassLoader.loadClass("org.mozilla.gecko.GeckoEvent");
+            mGas = mClassLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
+            Class [] parameters = new Class[2];
+            parameters[0] = String.class;
+            parameters[1] = mGel;
+            mRegisterGEL = mGas.getMethod("registerGeckoEventListener", parameters);
+            mUnregisterGEL = mGas.getMethod("unregisterGeckoEventListener", parameters);
+            parameters = new Class[1];
+            parameters[0] = mGe;
+            mSendGE = mGas.getMethod("sendEventToGecko", parameters);
 
-      getLayerClient = activity.getClass().getMethod("getSoftwareLayerClient");
-      Class gslc = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient");
-      drawListener = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener");
-      setDrawListener = gslc.getDeclaredMethod("setDrawListener", drawListener);
-     } catch (ClassNotFoundException e) {
-       e.printStackTrace();
-     } catch (SecurityException e) {
-       e.printStackTrace();
-     } catch (NoSuchMethodException e) {
-       e.printStackTrace();
-     } catch (IllegalArgumentException e) {
-       e.printStackTrace();
-     }
-  }
-
-  class wakeInvocationHandler implements InvocationHandler {
-    private final GeckoEventExpecter mEventExpecter;
-
-    public wakeInvocationHandler(GeckoEventExpecter expecter) {
-      mEventExpecter = expecter;
+            mGetLayerClient = activity.getClass().getMethod("getSoftwareLayerClient");
+            Class gslc = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient");
+            mDrawListener = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener");
+            mSetDrawListener = gslc.getDeclaredMethod("setDrawListener", mDrawListener);
+         } catch (ClassNotFoundException e) {
+             e.printStackTrace();
+         } catch (SecurityException e) {
+             e.printStackTrace();
+         } catch (NoSuchMethodException e) {
+             e.printStackTrace();
+         } catch (IllegalArgumentException e) {
+             e.printStackTrace();
+         }
     }
 
-    public Object invoke(Object proxy, Method method, Object[] args) {
-      String methodName = method.getName();
-      //Depending on the method, return a completely different type.
-      if(methodName.equals("toString")) {
-        return "wakeInvocationHandler";
-      }
-      if(methodName.equals("equals")) {
-        return this == args[0];
-      }
-      if(methodName.equals("clone")) {
-        return this;
-      }
-      if(methodName.equals("hashCode")) {
-        return 314;
-      }
-      FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, 
-        "Waking up on "+methodName);
-      mEventExpecter.notifyOfEvent();
-      return null;
-    }
-  }
+    class wakeInvocationHandler implements InvocationHandler {
+        private final GeckoEventExpecter mEventExpecter;
+
+        public wakeInvocationHandler(GeckoEventExpecter expecter) {
+            mEventExpecter = expecter;
+        }
 
-  class GeckoEventExpecter implements EventExpecter {
-    private final String mGeckoEvent;
-    private final Object[] mRegistrationParams;
-    private boolean mEventReceived;
-
-    GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
-      mGeckoEvent = geckoEvent;
-      mRegistrationParams = registrationParams;
-    }
-
-    public synchronized void blockForEvent() {
-      while (! mEventReceived) {
-        try {
-          this.wait();
-        } catch (InterruptedException ie) {
-          ie.printStackTrace();
-          break;
+        public Object invoke(Object proxy, Method method, Object[] args) {
+            String methodName = method.getName();
+            //Depending on the method, return a completely different type.
+            if(methodName.equals("toString")) {
+                return "wakeInvocationHandler";
+            }
+            if(methodName.equals("equals")) {
+                return this == args[0];
+            }
+            if(methodName.equals("clone")) {
+                return this;
+            }
+            if(methodName.equals("hashCode")) {
+                return 314;
+            }
+            FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG, 
+                "Waking up on "+methodName);
+            mEventExpecter.notifyOfEvent();
+            return null;
         }
-      }
-      FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
-        "unblocked on expecter for " + mGeckoEvent);
-    }
-
-    public synchronized boolean eventReceived() {
-      return mEventReceived;
     }
 
-    void notifyOfEvent() {
-      try {
-        unregisterGEL.invoke(null, mRegistrationParams);
-      } catch (IllegalAccessException e) {
-        e.printStackTrace();
-      } catch (InvocationTargetException e) {
-        e.printStackTrace();
-      }
-      FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
-        "received event " + mGeckoEvent);
-      synchronized (this) {
-        mEventReceived = true;
-        this.notifyAll();
-      }
+    class GeckoEventExpecter implements EventExpecter {
+        private final String mGeckoEvent;
+        private final Object[] mRegistrationParams;
+        private boolean mEventReceived;
+
+        GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
+            mGeckoEvent = geckoEvent;
+            mRegistrationParams = registrationParams;
+        }
+
+        public synchronized void blockForEvent() {
+            while (! mEventReceived) {
+                try {
+                    this.wait();
+                } catch (InterruptedException ie) {
+                    ie.printStackTrace();
+                    break;
+                }
+            }
+            FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
+                "unblocked on expecter for " + mGeckoEvent);
+        }
+
+        public synchronized boolean eventReceived() {
+            return mEventReceived;
+        }
+
+        void notifyOfEvent() {
+            try {
+                mUnregisterGEL.invoke(null, mRegistrationParams);
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (InvocationTargetException e) {
+                e.printStackTrace();
+            }
+            FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
+                "received event " + mGeckoEvent);
+            synchronized (this) {
+                mEventReceived = true;
+                this.notifyAll();
+            }
+        }
     }
-  }
-  
-  public EventExpecter expectGeckoEvent(String geckoEvent) {
-    FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
-      "waiting for "+geckoEvent);
-    try {
-      Class [] interfaces = new Class[1];
-      interfaces[0] = gel;
-      Object[] finalParams = new Object[2];
-      finalParams[0] = geckoEvent;
-     
-      GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
-      wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
-      Object proxy = Proxy.newProxyInstance(classLoader, interfaces, wIH);
-      finalParams[1] = proxy;
-      registerGEL.invoke(null, finalParams);
-      
-      return expecter;
-    } catch (IllegalAccessException e) {
-      e.printStackTrace();
-    } catch (InvocationTargetException e) {
-      e.printStackTrace();
-    }
-    return null;
-  }
-
-  class DrawListenerProxy implements InvocationHandler {
-    private final PaintExpecter mPaintExpecter;
-
-    DrawListenerProxy(PaintExpecter paintExpecter) {
-      mPaintExpecter = paintExpecter;
+    
+    public EventExpecter expectGeckoEvent(String geckoEvent) {
+        FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
+            "waiting for "+geckoEvent);
+        try {
+            Class [] interfaces = new Class[1];
+            interfaces[0] = mGel;
+            Object[] finalParams = new Object[2];
+            finalParams[0] = geckoEvent;
+         
+            GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
+            wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
+            Object proxy = Proxy.newProxyInstance(mClassLoader, interfaces, wIH);
+            finalParams[1] = proxy;
+            mRegisterGEL.invoke(null, finalParams);
+            
+            return expecter;
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+        }
+        return null;
     }
 
-    public Object invoke(Object proxy, Method method, Object[] args) {
-      String methodName = method.getName();
-      if ("drawFinished".equals(methodName)) {
-        FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
-          "Received drawFinished notification");
-        mPaintExpecter.notifyOfEvent();
-      } else if ("toString".equals(methodName)) {
-        return "DrawListenerProxy";
-      } else if ("equals".equals(methodName)) {
-        return false;
-      } else if ("hashCode".equals(methodName)) {
-        return 0;
-      }
-      return null;
-    }
-  }
+    class DrawListenerProxy implements InvocationHandler {
+        private final PaintExpecter mPaintExpecter;
+
+        DrawListenerProxy(PaintExpecter paintExpecter) {
+            mPaintExpecter = paintExpecter;
+        }
 
-  class PaintExpecter implements RepeatedEventExpecter {
-    private Object mLayerClient;
-    private boolean mPaintDone;
-
-    PaintExpecter() throws IllegalAccessException, InvocationTargetException {
-      mLayerClient = getLayerClient.invoke(geckoApp);
-      setDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(classLoader, new Class[] { drawListener }, new DrawListenerProxy(this)));
-    }
-
-    void notifyOfEvent() {
-      synchronized (this) {
-        mPaintDone = true;
-        this.notifyAll();
-      }
+        public Object invoke(Object proxy, Method method, Object[] args) {
+            String methodName = method.getName();
+            if ("drawFinished".equals(methodName)) {
+                FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
+                    "Received drawFinished notification");
+                mPaintExpecter.notifyOfEvent();
+            } else if ("toString".equals(methodName)) {
+                return "DrawListenerProxy";
+            } else if ("equals".equals(methodName)) {
+                return false;
+            } else if ("hashCode".equals(methodName)) {
+                return 0;
+            }
+            return null;
+        }
     }
 
-    public synchronized void blockForEvent() {
-      while (! mPaintDone) {
-        try {
-          this.wait();
-        } catch (InterruptedException ie) {
-          ie.printStackTrace();
-          break;
+    class PaintExpecter implements RepeatedEventExpecter {
+        private Object mLayerClient;
+        private boolean mPaintDone;
+
+        PaintExpecter() throws IllegalAccessException, InvocationTargetException {
+            mLayerClient = mGetLayerClient.invoke(mGeckoApp);
+            mSetDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(mClassLoader, new Class[] { mDrawListener }, new DrawListenerProxy(this)));
+        }
+
+        void notifyOfEvent() {
+            synchronized (this) {
+                mPaintDone = true;
+                this.notifyAll();
+            }
+        }
+
+        public synchronized void blockForEvent() {
+            while (!mPaintDone) {
+                try {
+                    this.wait();
+                } catch (InterruptedException ie) {
+                    ie.printStackTrace();
+                    break;
+                }
+            }
+            try {
+                mSetDrawListener.invoke(mLayerClient, (Object)null);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        public synchronized boolean eventReceived() {
+            return mPaintDone;
         }
-      }
-      try {
-        setDrawListener.invoke(mLayerClient, (Object)null);
-      } catch (Exception e) {
-        e.printStackTrace();
-      }
-    }
 
-    public synchronized boolean eventReceived() {
-      return mPaintDone;
+        public synchronized void blockUntilClear(long millis) {
+            if (millis <= 0) {
+                throw new IllegalArgumentException("millis must be > 0");
+            }
+            // wait for at least one event
+            while (!mPaintDone) {
+                try {
+                    this.wait();
+                } catch (InterruptedException ie) {
+                    ie.printStackTrace();
+                    break;
+                }
+            }
+            // now wait for a period of millis where we don't get an event
+            long startTime = SystemClock.uptimeMillis();
+            while (true) {
+                try {
+                    this.wait(millis);
+                } catch (InterruptedException ie) {
+                    ie.printStackTrace();
+                    break;
+                }
+                long endTime = SystemClock.uptimeMillis();
+                if (endTime - startTime >= millis) {
+                    // success
+                    break;
+                }
+                // we got a notify() before we could wait long enough, so we need to start over
+                startTime = endTime;
+            }
+            try {
+                mSetDrawListener.invoke(mLayerClient, (Object)null);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
     }
 
-    public synchronized void blockUntilClear(long millis) {
-      if (millis <= 0) {
-        throw new IllegalArgumentException("millis must be > 0");
-      }
-      // wait for at least one event
-      while (! mPaintDone) {
-        try {
-          this.wait();
-        } catch (InterruptedException ie) {
-          ie.printStackTrace();
-          break;
-        }
-      }
-      // now wait for a period of millis where we don't get an event
-      long startTime = SystemClock.uptimeMillis();
-      while (true) {
+    public RepeatedEventExpecter expectPaint() {
         try {
-          this.wait(millis);
-        } catch (InterruptedException ie) {
-          ie.printStackTrace();
-          break;
-        }
-        long endTime = SystemClock.uptimeMillis();
-        if (endTime - startTime >= millis) {
-          // success
-          break;
+            return new PaintExpecter();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
         }
-        // we got a notify() before we could wait long enough, so we need to start over
-        startTime = endTime;
-      }
-      try {
-        setDrawListener.invoke(mLayerClient, (Object)null);
-      } catch (Exception e) {
-        e.printStackTrace();
-      }
     }
-  }
 
-  public RepeatedEventExpecter expectPaint() {
-    try {
-      return new PaintExpecter();
-    } catch (Exception e) {
-      e.printStackTrace();
-      return null;
+    public void sendSpecialKey(SpecialKey button) {
+        switch(button) {
+            case DOWN:
+                mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
+                break;
+            case UP:
+                mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP);
+                break;
+            case LEFT:
+                mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT);
+                break;
+            case RIGHT:
+                mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT);
+                break;
+            case ENTER:
+                mInstr.sendCharacterSync(KeyEvent.KEYCODE_ENTER);
+                break;
+            case MENU:
+                mInstr.sendCharacterSync(KeyEvent.KEYCODE_MENU);
+                break;
+            case BACK:
+                mInstr.sendCharacterSync(KeyEvent.KEYCODE_BACK);
+                break;
+            default:
+                break;
+        }
     }
-  }
 
-  public void sendSpecialKey(SpecialKey button) {
-    switch( button) {
-      case DOWN:
-        instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
-        break;
-      case UP:
-        instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP);
-        break;
-      case LEFT:
-        instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT);
-        break;
-      case RIGHT:
-        instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT);
-        break;
-      case ENTER:
-        instr.sendCharacterSync(KeyEvent.KEYCODE_ENTER);
-        break;
-      case MENU:
-        instr.sendCharacterSync(KeyEvent.KEYCODE_MENU);
-        break;
-      case BACK:
-        instr.sendCharacterSync(KeyEvent.KEYCODE_BACK);
-        break;
-      default:
-        break;
+    @Override
+    public void sendKeys(String input) {
+        mInstr.sendStringSync(input);
     }
-  }
 
-  @Override
-  public void sendKeys(String input) {
-    instr.sendStringSync(input);
-  }
-
-
-  public void drag(int startingX, int endingX, int startingY, int endingY) {
-    solo.drag(startingX, endingX, startingY, endingY, 10);
-  }
+    public void drag(int startingX, int endingX, int startingY, int endingY) {
+        mSolo.drag(startingX, endingX, startingY, endingY, 10);
+    }
 }
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -41,18 +41,16 @@ package @ANDROID_PACKAGE_NAME@;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.IntBuffer;
-import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.HashMap;
 import java.util.List;
 
 import java.lang.Class;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.InvocationHandler;
@@ -63,401 +61,410 @@ import android.opengl.GLSurfaceView;
 import android.view.View;
 import android.util.Log;
 
 import org.json.*;
 
 import com.jayway.android.robotium.solo.Solo;
 
 public class FennecNativeDriver implements Driver {
-  // Map of IDs to element names.
-  private HashMap locators = null;
-  private Activity activity;
-  private Solo solo;
+    // Map of IDs to element names.
+    private HashMap mLocators = null;
+    private Activity mActivity;
+    private Solo mSolo;
+
+    private static String mLogFile = null;
+    private static LogLevel mLogLevel = LogLevel.LOG_LEVEL_INFO;
 
-  private static String mLogFile = null;
-  private static LogLevel mLogLevel = LogLevel.LOG_LEVEL_INFO;
+    // Objects for reflexive access of fennec classes.
+    private ClassLoader mClassLoader;
+    private Class mGel;
+    private Class mGe;
+    private Class mGas;
+    private Method mRegisterGEL;
+    private Method mUnregisterGEL;
+    private Method mSendGE;
+    private Method _startFrameRecording;
+    private Method _stopFrameRecording;
+    private Method _startCheckerboardRecording;
+    private Method _stopCheckerboardRecording;
+    private Method _getPixels;
 
-  // Objects for reflexive access of fennec classes.
-  private ClassLoader classLoader;
-  private Class gel;
-  private Class ge;
-  private Class gas;
-  private Method registerGEL;
-  private Method unregisterGEL;
-  private Method sendGE;
-  private Method _startFrameRecording;
-  private Method _stopFrameRecording;
-  private Method _startCheckerboardRecording;
-  private Method _stopCheckerboardRecording;
-  private Method _getPixels;
+    public enum LogLevel {
+        LOG_LEVEL_DEBUG(1), 
+        LOG_LEVEL_INFO(2),    
+        LOG_LEVEL_WARN(3), 
+        LOG_LEVEL_ERROR(4);
 
-  public enum LogLevel {
-    LOG_LEVEL_DEBUG(1), 
-    LOG_LEVEL_INFO(2),  
-    LOG_LEVEL_WARN(3), 
-    LOG_LEVEL_ERROR(4);
+        private int mValue;
+        LogLevel(int value) {
+            mValue = value;
+        }
+        public boolean isEnabled(LogLevel configuredLevel) {
+            return mValue >= configuredLevel.getValue();
+        }
+        private int getValue() {
+            return mValue;
+        }
+    }
 
-    private int mValue;
-    LogLevel(int value) {
-      mValue = value;
-    }
-    public boolean isEnabled(LogLevel configuredLevel) {
-      return mValue >= configuredLevel.getValue();
-    }
-    private int getValue() {
-      return mValue;
-    }
-  }
+    public FennecNativeDriver(Activity activity, Solo robocop) {
+        mActivity = activity;
+        mSolo = robocop;
 
-  public FennecNativeDriver(Activity activity, Solo robocop){
-    this.activity = activity;
-    this.solo = robocop;
+        // Set up table of fennec_ids.
+        mLocators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt"));
 
-    // Set up table of fennec_ids.
-    locators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt"));
+        // Set up reflexive access of java classes and methods.
+        try {
+            mClassLoader = activity.getClassLoader();
+            mGel = mClassLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
+            mGe = mClassLoader.loadClass("org.mozilla.gecko.GeckoEvent");
+            mGas = mClassLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
+            Class [] parameters = new Class[2];
+            parameters[0] = String.class;
+            parameters[1] = mGel;
+            mRegisterGEL = mGas.getMethod("registerGeckoEventListener", parameters);
+            mUnregisterGEL = mGas.getMethod("unregisterGeckoEventListener", parameters);
+            parameters = new Class[1];
+            parameters[0] = mGe;
+            mSendGE = mGas.getMethod("sendEventToGecko", parameters);
 
-    // Set up reflexive access of java classes and methods.
-    try {
-      classLoader = activity.getClassLoader();
-      gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
-      ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
-      gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
-      Class [] parameters = new Class[2];
-      parameters[0] = String.class;
-      parameters[1] = gel;
-      registerGEL = gas.getMethod("registerGeckoEventListener", parameters);
-      unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
-      parameters = new Class[1];
-      parameters[0] = ge;
-      sendGE = gas.getMethod("sendEventToGecko", parameters);
+            Class gfx = mClassLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI");
+            _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording");
+            _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording");
+            _startCheckerboardRecording = gfx.getDeclaredMethod("startCheckerboardRecording");
+            _stopCheckerboardRecording = gfx.getDeclaredMethod("stopCheckerboardRecording");
 
-      Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI");
-      _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording");
-      _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording");
-      _startCheckerboardRecording = gfx.getDeclaredMethod("startCheckerboardRecording");
-      _stopCheckerboardRecording = gfx.getDeclaredMethod("stopCheckerboardRecording");
+            Class layerView = mClassLoader.loadClass("org.mozilla.gecko.gfx.LayerView");
+            _getPixels = layerView.getDeclaredMethod("getPixels");
+         } catch (ClassNotFoundException e) {
+             e.printStackTrace();
+         } catch (SecurityException e) {
+             e.printStackTrace();
+         } catch (NoSuchMethodException e) {
+             e.printStackTrace();
+         } catch (IllegalArgumentException e) {
+             e.printStackTrace();
+         }
+    }
 
-      Class layerView = classLoader.loadClass("org.mozilla.gecko.gfx.LayerView");
-      _getPixels = layerView.getDeclaredMethod("getPixels");
-     } catch (ClassNotFoundException e) {
-       e.printStackTrace();
-     } catch (SecurityException e) {
-       e.printStackTrace();
-     } catch (NoSuchMethodException e) {
-       e.printStackTrace();
-     } catch (IllegalArgumentException e) {
-       e.printStackTrace();
-     }
-  }
+    //Information on the location of the Gecko Frame.
+    private boolean mGeckoInfo = false;
+    private int mGeckoTop = 100;
+    private int mGeckoLeft = 0;
+    private int mGeckoHeight= 700;
+    private int mGeckoWidth = 1024;
 
-  //Information on the location of the Gecko Frame.
-  private boolean geckoInfo = false;
-  private int geckoTop = 100;
-  private int geckoLeft = 0;
-  private int geckoHeight= 700;
-  private int geckoWidth = 1024;
-
-  private void getGeckoInfo() {
-    View geckoLayout = activity.findViewById(Integer.decode((String)locators.get("gecko_layout")));
-    if (geckoLayout != null) {
-      int[] pos = new int[2];
-      geckoLayout.getLocationOnScreen(pos);
-      geckoTop = pos[1];
-      geckoLeft = pos[0];
-      geckoWidth = geckoLayout.getWidth();
-      geckoHeight = geckoLayout.getHeight();
-      geckoInfo = true;
-    } else {
-      throw new RoboCopException("Unable to find view gecko_layout");
+    private void getGeckoInfo() {
+        View geckoLayout = mActivity.findViewById(Integer.decode((String)mLocators.get("gecko_layout")));
+        if (geckoLayout != null) {
+            int[] pos = new int[2];
+            geckoLayout.getLocationOnScreen(pos);
+            mGeckoTop = pos[1];
+            mGeckoLeft = pos[0];
+            mGeckoWidth = geckoLayout.getWidth();
+            mGeckoHeight = geckoLayout.getHeight();
+            mGeckoInfo = true;
+        } else {
+            throw new RoboCopException("Unable to find view gecko_layout");
+        }
     }
-  }
 
-  public int getGeckoTop() {
-    if(!geckoInfo) {
-      getGeckoInfo();
+    public int getGeckoTop() {
+        if (!mGeckoInfo) {
+            getGeckoInfo();
+        }
+        return mGeckoTop;
     }
-    return geckoTop;
-  }
 
-  public int getGeckoLeft() {
-    if(!geckoInfo) {
-      getGeckoInfo();
+    public int getGeckoLeft() {
+        if (!mGeckoInfo) {
+            getGeckoInfo();
+        }
+        return mGeckoLeft;
     }
-    return geckoLeft;
-  }
 
-  public int getGeckoHeight() {
-    if(!geckoInfo) {
-      getGeckoInfo();
+    public int getGeckoHeight() {
+        if (!mGeckoInfo) {
+            getGeckoInfo();
+        }
+        return mGeckoHeight;
     }
-    return geckoHeight;
-  }
-  public int getGeckoWidth() {
-    if(!geckoInfo) {
-      getGeckoInfo();
+
+    public int getGeckoWidth() {
+        if (!mGeckoInfo) {
+            getGeckoInfo();
+        }
+        return mGeckoWidth;
     }
-    return geckoWidth;
-  }
 
-  public Element findElement(Activity activity, String name) {
-    if (name == null)
-      throw new IllegalArgumentException("Can not findElements when passed a null");
-    if (locators.containsKey(name)){
-      return new FennecNativeElement(Integer.decode((String)locators.get(name)), activity, solo);
+    public Element findElement(Activity activity, String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Can not findElements when passed a null");
+        }
+        if (mLocators.containsKey(name)) {
+            return new FennecNativeElement(Integer.decode((String)mLocators.get(name)), activity, mSolo);
+        }
+        throw new RoboCopException("Element does not exist in the list");
     }
-    throw new RoboCopException("Element does not exist in the list");
-  }
 
-  public void startFrameRecording() {
-    try {
-      Object [] params = null;
-      _startFrameRecording.invoke(null, params);
-    } catch (IllegalAccessException e) {
-      e.printStackTrace();
-    } catch (InvocationTargetException e) {
-      e.printStackTrace();
+    public void startFrameRecording() {
+        try {
+            Object [] params = null;
+            _startFrameRecording.invoke(null, params);
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+        }
     }
-  }
+
+    public int stopFrameRecording() {
+        Class [] parameters = new Class[1];
+        parameters[0] = null;
+        List frames;
 
-  public int stopFrameRecording() {
-    Class [] parameters = new Class[1];
-    parameters[0] = null;
-    List frames;
+        try {
+            Object [] params = null;
+            frames = (List)_stopFrameRecording.invoke(null, params);
+            Object [] framearray = frames.toArray();
+            Long last = new Long(0);
+            Long threshold = new Long(17);
+            int numDelays = 0;
+            for (int i=0; i < framearray.length; i++) {
+                Long val = (Long)framearray[i];
+                if ((val - last) > threshold) {
+                    numDelays++;
+                }
+                last = val;
+            }
+            return numDelays;
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+        }
 
-    try {
-      Object [] params = null;
-      frames = (List)_stopFrameRecording.invoke(null, params);
-      Object [] framearray = frames.toArray();
-      Long last = new Long(0);
-      Long threshold = new Long(17);
-      int numDelays = 0;
-      for (int i=0; i < framearray.length; i++) {
-        Long val = (Long)framearray[i];
-        if ((val - last) > threshold) {
-          numDelays++;
+        return 0;
+    }
+
+    public void startCheckerboardRecording() {
+        try {
+            Object [] params = null;
+            _startCheckerboardRecording.invoke(null, params);
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
         }
-        last = val;
-      }
-      return numDelays;
-    } catch (IllegalAccessException e) {
-      e.printStackTrace();
-    } catch (InvocationTargetException e) {
-      e.printStackTrace();
     }
 
-    return 0;
-  }
-
-  public void startCheckerboardRecording() {
-    try {
-      Object [] params = null;
-      _startCheckerboardRecording.invoke(null, params);
-    } catch (IllegalAccessException e) {
-      e.printStackTrace();
-    } catch (InvocationTargetException e) {
-      e.printStackTrace();
-    }
-  }
+    public float stopCheckerboardRecording() {
+        Class [] parameters = new Class[1];
+        parameters[0] = null;
+        List checkerboard;
 
-  public float stopCheckerboardRecording() {
-    Class [] parameters = new Class[1];
-    parameters[0] = null;
-    List checkerboard;
+        try {
+            Object [] params = null;
+            checkerboard = (List)_stopCheckerboardRecording.invoke(null, params);
+            Object [] amountarray = checkerboard.toArray();
+            double completeness = 0;
+            for (Object obj : amountarray) {
+                float val = (Float)obj;
+                completeness += (1.0 - (double)val) / (double)amountarray.length;
+            }
+            return (float)completeness;
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+        }
 
-    try {
-      Object [] params = null;
-      checkerboard = (List)_stopCheckerboardRecording.invoke(null, params);
-      Object [] amountarray = checkerboard.toArray();
-      double completeness = 0;
-      for (Object obj : amountarray) {
-        float val = (Float)obj;
-        completeness += (1.0 - (double)val) / (double)amountarray.length;
-      }
-      return (float)completeness;
-    } catch (IllegalAccessException e) {
-      e.printStackTrace();
-    } catch (InvocationTargetException e) {
-      e.printStackTrace();
+        return 0.0f;
     }
 
-    return 0.0f;
-  }
-
-  private GLSurfaceView getSurfaceView() {
-    for (View v : solo.getCurrentViews()) {
-      if (v instanceof GLSurfaceView) {
-        return (GLSurfaceView)v;
-      }
-    }
-    return null;
-  }
-
-  public int[][] getPaintedSurface() {
-    GLSurfaceView view = getSurfaceView();
-    if (view == null) {
-      return null;
-    }
-    IntBuffer pixelBuffer;
-    try {
-      pixelBuffer = (IntBuffer)_getPixels.invoke(view);
-    } catch (Exception e) {
-      e.printStackTrace();
-      return null;
+    private GLSurfaceView getSurfaceView() {
+        for (View v : mSolo.getCurrentViews()) {
+            if (v instanceof GLSurfaceView) {
+                return (GLSurfaceView)v;
+            }
+        }
+        return null;
     }
 
-    // now we need to (1) flip the image, because GL likes to do things up-side-down,
-    // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
-    int w = view.getWidth();
-    int h = view.getHeight();
-    pixelBuffer.position(0);
-    int[][] pixels = new int[h][w];
-    for (int y = h - 1; y >= 0; y--) {
-      for (int x = 0; x < w; x++) {
-        int agbr = pixelBuffer.get();
-        pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
-      }
-    }
-    return pixels;
-  }
-
-  class scrollHandler implements InvocationHandler {
-    public scrollHandler(){};
-    public Object invoke(Object proxy, Method method, Object[] args) {
-      try{
-        //Disect the JSON object into the appropriate variables 
-        JSONObject jo = ((JSONObject)args[1]);
-        scrollHeight = jo.getInt("y");
-        height = jo.getInt("cheight");
-        //We don't want a height of 0. That means it's a bad response.
-        if( height > 0) {
-          pageHeight = jo.getInt("height");
+    public int[][] getPaintedSurface() {
+        GLSurfaceView view = getSurfaceView();
+        if (view == null) {
+            return null;
+        }
+        IntBuffer pixelBuffer;
+        try {
+            pixelBuffer = (IntBuffer)_getPixels.invoke(view);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
         }
 
-      } catch( Throwable e) {
-        FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN, 
-          "WARNING: ScrollReceived, but read wrong!");
-      }
-      return null;
+        // now we need to (1) flip the image, because GL likes to do things up-side-down,
+        // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
+        int w = view.getWidth();
+        int h = view.getHeight();
+        pixelBuffer.position(0);
+        int[][] pixels = new int[h][w];
+        for (int y = h - 1; y >= 0; y--) {
+            for (int x = 0; x < w; x++) {
+                int agbr = pixelBuffer.get();
+                pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
+            }
+        }
+        return pixels;
     }
-  }
-  public int getScrollHeight() {
-    return scrollHeight;
-  }
-  public int getPageHeight() {
-    return pageHeight;
-  }
-  public int getHeight() {
-    return height;
-  }
+
+    public int mHeight=0;
+    public int mScrollHeight=0;
+    public int mPageHeight=10;
+
+    class scrollHandler implements InvocationHandler {
+        public scrollHandler(){};
+        public Object invoke(Object proxy, Method method, Object[] args) {
+            try {
+                // Disect the JSON object into the appropriate variables 
+                JSONObject jo = ((JSONObject)args[1]);
+                mScrollHeight = jo.getInt("y");
+                mHeight = jo.getInt("cheight");
+                // We don't want a height of 0. That means it's a bad response.
+                if (mHeight > 0) {
+                    mPageHeight = jo.getInt("height");
+                }
 
-  public int height=0;
-  public int scrollHeight=0;
-  public int pageHeight=10;
-  public void setupScrollHandling() {
-    //Setup scrollHandler to catch "robocop:scroll" events. 
-    try {
-      Class [] interfaces = new Class[1];
-      interfaces[0] = gel;
-      Object[] finalParams = new Object[2];
-      finalParams[0] = "robocop:scroll";
-      finalParams[1] = Proxy.newProxyInstance(classLoader, interfaces, new scrollHandler());
-      registerGEL.invoke(null, finalParams);
-    } catch (IllegalAccessException e) {
-      e.printStackTrace();
-    } catch (InvocationTargetException e) {
-      e.printStackTrace();
+            } catch( Throwable e) {
+                FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN, 
+                    "WARNING: ScrollReceived, but read wrong!");
+            }
+            return null;
+        }
+    }
+
+    public int getScrollHeight() {
+        return mScrollHeight;
+    }
+    public int getPageHeight() {
+        return mPageHeight;
+    }
+    public int getHeight() {
+        return mHeight;
+    }
+
+    public void setupScrollHandling() {
+        //Setup scrollHandler to catch "robocop:scroll" events. 
+        try {
+            Class [] interfaces = new Class[1];
+            interfaces[0] = mGel;
+            Object[] finalParams = new Object[2];
+            finalParams[0] = "robocop:scroll";
+            finalParams[1] = Proxy.newProxyInstance(mClassLoader, interfaces, new scrollHandler());
+            mRegisterGEL.invoke(null, finalParams);
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+        }
+
     }
 
-  }
-
-  //Takes a filename, loads the file, 
-  //  and returns a string version of the entire file.
-  public static String getFile(String filename)
-  {
-    StringBuilder text = new StringBuilder();
-
-    BufferedReader br = null;
-    try {
-      br = new BufferedReader(new FileReader(filename));
-      String line;
+    /**
+     *  Takes a filename, loads the file, and returns a string version of the entire file.
+     */
+    public static String getFile(String filename)
+    {
+        StringBuilder text = new StringBuilder();
 
-      while ((line = br.readLine()) != null) {
-        text.append(line);
-        text.append('\n');
-      }
-    } catch(IOException e) {
-      e.printStackTrace();
-    } finally {
-      try {
-        br.close();
-      } catch (IOException e) {
-      }
-    }
-    return text.toString();  
-  }
+        BufferedReader br = null;
+        try {
+            br = new BufferedReader(new FileReader(filename));
+            String line;
 
-  // Takes a string of "key=value" pairs split by \n and creates a hash table.
-  public static HashMap convertTextToTable(String data)
-  {
-    HashMap retVal = new HashMap();
-
-    String[] lines = data.split("\n");
-    for (int i = 0; i < lines.length; i++) {
-      String[] parts = lines[i].split("=");
-      retVal.put(parts[0].trim(), parts[1].trim());
+            while ((line = br.readLine()) != null) {
+                text.append(line);
+                text.append('\n');
+            }
+        } catch(IOException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                br.close();
+            } catch (IOException e) {
+            }
+        }
+        return text.toString();    
     }
-    return retVal;
-  }
 
-  // Set the filename used for logging. If the file already exists, delete it
-  // as a safe-guard against accidentally appending to an old log file.
-  public static void setLogFile(String filename) {
-    mLogFile = filename;
-    File file = new File(mLogFile);
-    if (file.exists()) {
-      file.delete();
-    }
-  }
+    /**
+     *  Takes a string of "key=value" pairs split by \n and creates a hash table.
+     */
+    public static HashMap convertTextToTable(String data)
+    {
+        HashMap retVal = new HashMap();
 
-  public static void setLogLevel(LogLevel level) {
-    mLogLevel = level;
-  }
-
-  public static void log(LogLevel level, String message) {
-    if (mLogFile == null) {
-      assert(false);
+        String[] lines = data.split("\n");
+        for (int i = 0; i < lines.length; i++) {
+            String[] parts = lines[i].split("=");
+            retVal.put(parts[0].trim(), parts[1].trim());
+        }
+        return retVal;
     }
 
-    if (level.isEnabled(mLogLevel)) {
-      File file = new File(mLogFile);
-      BufferedWriter bw = null;
+    /** 
+     *  Set the filename used for logging. If the file already exists, delete it
+     *  as a safe-guard against accidentally appending to an old log file.
+     */
+    public static void setLogFile(String filename) {
+        mLogFile = filename;
+        File file = new File(mLogFile);
+        if (file.exists()) {
+            file.delete();
+        }
+    }
 
-      try {
-        bw = new BufferedWriter(new FileWriter(mLogFile, true));
-        bw.write(message);
-        bw.newLine();
-      } catch(IOException e) {
-        Log.e("Robocop", "exception with file writer on: " + mLogFile);
-      } finally {
-        try {
-          if (bw != null) {
-            bw.flush();
-            bw.close();
-          }
-        } catch (IOException ex) {
-          ex.printStackTrace();
-        }
-      }
+    public static void setLogLevel(LogLevel level) {
+        mLogLevel = level;
     }
 
-    if (level == LogLevel.LOG_LEVEL_INFO) {
-      Log.i("Robocop", message);
-    } else if (level == LogLevel.LOG_LEVEL_DEBUG) {
-      Log.d("Robocop", message);
-    } else if (level == LogLevel.LOG_LEVEL_WARN) {
-      Log.w("Robocop", message);
-    } else if (level == LogLevel.LOG_LEVEL_ERROR) {
-      Log.e("Robocop", message);
+    public static void log(LogLevel level, String message) {
+        if (mLogFile == null) {
+            assert(false);
+        }
+
+        if (level.isEnabled(mLogLevel)) {
+            File file = new File(mLogFile);
+            BufferedWriter bw = null;
+
+            try {
+                bw = new BufferedWriter(new FileWriter(mLogFile, true));
+                bw.write(message);
+                bw.newLine();
+            } catch(IOException e) {
+                Log.e("Robocop", "exception with file writer on: " + mLogFile);
+            } finally {
+                try {
+                    if (bw != null) {
+                        bw.flush();
+                        bw.close();
+                    }
+                } catch (IOException ex) {
+                    ex.printStackTrace();
+                }
+            }
+        }
+
+        if (level == LogLevel.LOG_LEVEL_INFO) {
+            Log.i("Robocop", message);
+        } else if (level == LogLevel.LOG_LEVEL_DEBUG) {
+            Log.d("Robocop", message);
+        } else if (level == LogLevel.LOG_LEVEL_WARN) {
+            Log.w("Robocop", message);
+        } else if (level == LogLevel.LOG_LEVEL_ERROR) {
+            Log.e("Robocop", message);
+        }
     }
-  }
 
 }
--- a/build/mobile/robocop/FennecNativeElement.java.in
+++ b/build/mobile/robocop/FennecNativeElement.java.in
@@ -37,128 +37,127 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 package @ANDROID_PACKAGE_NAME@;
 
 import java.util.List;
 
 import android.app.Activity;
-import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.TextSwitcher;
 import android.app.Instrumentation;
 import com.jayway.android.robotium.solo.Solo;
 import java.util.concurrent.SynchronousQueue;
 
 public class FennecNativeElement implements Element {
-  private final Activity mActivity;
-  private Integer id;
-  private Solo robocop;
+    private final Activity mActivity;
+    private Integer mId;
+    private Solo mSolo;
 
-  public FennecNativeElement(Integer id, Activity activity, Solo solo){
-    this.id = id;
-    mActivity = activity;
-    robocop = solo;
-  }
+    public FennecNativeElement(Integer id, Activity activity, Solo solo) {
+        mId = id;
+        mActivity = activity;
+        mSolo = solo;
+    }
 
-  public Integer getId() {
-    return id;
-  }
+    public Integer getId() {
+        return mId;
+    }
 
-  public void click() {
-    final SynchronousQueue syncQueue = new SynchronousQueue();
-    mActivity.runOnUiThread(
-        new Runnable() {
-          public void run() {
-            View view = (View)mActivity.findViewById(id);
-            if(view != null) {
-              if (!view.performClick()) {
-                FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN,
-                  "Robocop called click on an element with no listener");
-              }
-            } else {
-              throw new RoboCopException("click: unable to find view "+id); 
-            }
-            syncQueue.offer(new Object());
-          }
-        });
-    try {
-      syncQueue.take();
-    } catch (InterruptedException e) {
-      e.printStackTrace();
+    public void click() {
+        final SynchronousQueue syncQueue = new SynchronousQueue();
+        mActivity.runOnUiThread(
+            new Runnable() {
+                public void run() {
+                    View view = (View)mActivity.findViewById(mId);
+                    if(view != null) {
+                        if (!view.performClick()) {
+                            FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN,
+                                "Robocop called click on an element with no listener");
+                        }
+                    } else {
+                        throw new RoboCopException("click: unable to find view "+mId); 
+                    }
+                    syncQueue.offer(new Object());
+                }
+            });
+        try {
+            syncQueue.take();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
     }
-  }
 
-  private Object text;
+    private Object mText;
 
-  public String getText() {
-    final SynchronousQueue syncQueue = new SynchronousQueue();
-    mActivity.runOnUiThread(
-        new Runnable() {
-          public void run() {
-            View v = mActivity.findViewById(id);
-            if(v instanceof EditText) {
-              EditText et = (EditText)v;
-              text = et.getEditableText();
-            }else if(v instanceof TextSwitcher) {
-              TextSwitcher ts = (TextSwitcher)v;
-              ts.getNextView();
-              text = ((TextView)ts.getCurrentView()).getText();
-            }else if(v instanceof ViewGroup) {
-              ViewGroup vg = (ViewGroup)v;
-              for(int i = 0; i < vg.getChildCount(); i++) {
-                if(vg.getChildAt(i) instanceof TextView) {
-                  text = ((TextView)vg.getChildAt(i)).getText();
+    public String getText() {
+        final SynchronousQueue syncQueue = new SynchronousQueue();
+        mActivity.runOnUiThread(
+            new Runnable() {
+                public void run() {
+                    View v = mActivity.findViewById(mId);
+                    if (v instanceof EditText) {
+                        EditText et = (EditText)v;
+                        mText = et.getEditableText();
+                    } else if (v instanceof TextSwitcher) {
+                        TextSwitcher ts = (TextSwitcher)v;
+                        ts.getNextView();
+                        mText = ((TextView)ts.getCurrentView()).getText();
+                    } else if (v instanceof ViewGroup) {
+                        ViewGroup vg = (ViewGroup)v;
+                        for (int i = 0; i < vg.getChildCount(); i++) {
+                            if (vg.getChildAt(i) instanceof TextView) {
+                                mText = ((TextView)vg.getChildAt(i)).getText();
+                            }
+                        }
+                    } else if (v instanceof TextView) {
+                        mText = ((TextView)v).getText(); 
+                    } else if (v == null) {
+                        throw new RoboCopException("getText: unable to find view "+mId); 
+                    } else {
+                        throw new RoboCopException("getText: unhandled type for view "+mId); 
+                    }
+                    syncQueue.offer(new Object());
+                } // end of run() method definition
+            } // end of anonymous Runnable object instantiation
+        );
+        try {     
+            // Wait for the UiThread code to finish running
+            syncQueue.take();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        if (mText == null) {
+            throw new RoboCopException("getText: Text is null for view "+mId);
+        }
+        return mText.toString();
+    }
+
+    private boolean mDisplayed;
+
+    public boolean isDisplayed() {
+        final SynchronousQueue syncQueue = new SynchronousQueue();
+        mDisplayed = false;
+        mActivity.runOnUiThread(
+            new Runnable() {
+                public void run() {
+                    View view = (View)mActivity.findViewById(mId);
+                    if (view != null) {
+                        mDisplayed = true;
+                    }
+                    syncQueue.offer(new Object());
                 }
-              } //end of for
-            } else if(v instanceof TextView) {
-              text = ((TextView)v).getText(); 
-            } else if(v == null) {
-              throw new RoboCopException("getText: unable to find view "+id); 
-            } else {
-              throw new RoboCopException("getText: unhandled type for view "+id); 
-            }
-            syncQueue.offer(new Object());
-          } // end of run() method definition
-        } // end of anonymous Runnable object instantiation
-    );
-    try {   
-      //Wait for the UiThread code to finish running
-      syncQueue.take();
-    } catch (InterruptedException e) {
-      e.printStackTrace();
-    }
-    if(text == null) {
-      throw new RoboCopException("getText: Text is null for view "+id);
+            });
+        try {
+            syncQueue.take();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        return mDisplayed;
     }
-    return text.toString();
-  }
-
-  private boolean displayed;
-
-  public boolean isDisplayed() {
-    final SynchronousQueue syncQueue = new SynchronousQueue();
-    displayed = false;
-    mActivity.runOnUiThread(
-        new Runnable() {
-          public void run() {
-            View view = (View)mActivity.findViewById(id);
-            if(view != null) {
-              displayed = true;
-            }
-            syncQueue.offer(new Object());
-          }
-        });
-    try {
-      syncQueue.take();
-    } catch (InterruptedException e) {
-      e.printStackTrace();
-    }
-    return displayed;
-  }
 
 }
--- a/build/mobile/robocop/FennecTalosAssert.java.in
+++ b/build/mobile/robocop/FennecTalosAssert.java.in
@@ -34,57 +34,45 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 package @ANDROID_PACKAGE_NAME@;
 
 
 public class FennecTalosAssert implements Assert {
-  
-  public FennecTalosAssert() {
-  }
-
-  // Write information to a logfile and logcat
-  public void dumpLog(String message)
-  {
-    FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
-  }
+    
+    public FennecTalosAssert() { }
 
-  // Set the filename used for dumpLog.
-  public void setLogFile(String filename)
-  {
-    FennecNativeDriver.setLogFile(filename);
-  }
+    /**
+     *  Write information to a logfile and logcat
+     */
+    public void dumpLog(String message) {
+        FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
+    }
 
-  public void setTestName(String testName)
-  {
-  }
+    /**
+     *  Set the filename used for dumpLog.
+     */
+    public void setLogFile(String filename) {
+        FennecNativeDriver.setLogFile(filename);
+    }
 
-
-  public void finalize()
-  {
-  }
+    public void setTestName(String testName) { }
 
-  public void ok(boolean condition, String name, String diag) {
-  }
+    public void finalize() { }
+
+    public void ok(boolean condition, String name, String diag) { }
 
-  public void is(Object a, Object b, String name) {
-  }
-  
-  public void isnot(Object a, Object b, String name) {
-  }
-
-  public void ispixel(int actual, int r, int g, int b, String name) {
-  }
+    public void is(Object a, Object b, String name) { }
+    
+    public void isnot(Object a, Object b, String name) { }
 
-  public void todo(boolean condition, String name, String diag) {
-  }
+    public void ispixel(int actual, int r, int g, int b, String name) { }
+
+    public void todo(boolean condition, String name, String diag) { }
 
-  public void todo_is(Object a, Object b, String name) {
-  }
-  
-  public void todo_isnot(Object a, Object b, String name) {
-  }
+    public void todo_is(Object a, Object b, String name) { }
+    
+    public void todo_isnot(Object a, Object b, String name) { }
 
-  public void info(String name, String message) {
-  }
+    public void info(String name, String message) { }
 }
--- a/build/mobile/robocop/RoboCopException.java.in
+++ b/build/mobile/robocop/RoboCopException.java.in
@@ -35,25 +35,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 ***** */
 
 package @ANDROID_PACKAGE_NAME@;
 
 public class RoboCopException extends RuntimeException {
-  
-  public RoboCopException(){
-    super();
-  }
-  
-  public RoboCopException(String message){
-    super(message);
-  }
-  
-  public RoboCopException(Throwable cause){
-    super(cause);
-  }
-  
-  public RoboCopException(String message, Throwable cause){
-    super(message, cause);
-  }
+    
+    public RoboCopException() {
+        super();
+    }
+    
+    public RoboCopException(String message) {
+        super(message);
+    }
+    
+    public RoboCopException(Throwable cause) {
+        super(cause);
+    }
+    
+    public RoboCopException(String message, Throwable cause) {
+        super(message, cause);
+    }
 }
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -288,16 +288,17 @@ MOZ_ENABLE_GNOME_COMPONENT = @MOZ_ENABLE
 MOZ_ENABLE_GIO = @MOZ_ENABLE_GIO@
 MOZ_GIO_CFLAGS = @MOZ_GIO_CFLAGS@
 MOZ_GIO_LIBS = @MOZ_GIO_LIBS@
 
 MOZ_NATIVE_NSPR = @MOZ_NATIVE_NSPR@
 MOZ_NATIVE_NSS = @MOZ_NATIVE_NSS@
 
 MOZ_B2G_RIL = @MOZ_B2G_RIL@
+MOZ_B2G_BT = @MOZ_B2G_BT@
 
 BUILD_CTYPES = @BUILD_CTYPES@
 
 COMPILE_ENVIRONMENT = @COMPILE_ENVIRONMENT@
 CROSS_COMPILE   = @CROSS_COMPILE@
 
 WCHAR_CFLAGS	= @WCHAR_CFLAGS@
 
--- a/configure.in
+++ b/configure.in
@@ -220,17 +220,24 @@ AC_SUBST(COMPILE_ENVIRONMENT)
 
 MOZ_ARG_WITH_STRING(l10n-base,
 [  --with-l10n-base=DIR    path to l10n repositories],
     L10NBASEDIR=$withval)
 if test -n "$L10NBASEDIR"; then
     if test "$L10NBASEDIR" = "yes" -o "$L10NBASEDIR" = "no"; then
         AC_MSG_ERROR([--with-l10n-base must specify a path])
     elif test -d "$L10NBASEDIR"; then
-        L10NBASEDIR=`cd "$L10NBASEDIR" && pwd`
+        case "$host_os" in
+        mingw*)
+            L10NBASEDIR=`cd "$L10NBASEDIR" && pwd -W`
+            ;;
+        *)
+            L10NBASEDIR=`cd "$L10NBASEDIR" && pwd`
+            ;;
+        esac
     else
         AC_MSG_ERROR([Invalid value --with-l10n-base, $L10NBASEDIR doesn't exist])
     fi
 fi
 AC_SUBST(L10NBASEDIR)
 
 dnl Check for Perl first -- needed for win32 SDK checks
 MOZ_PATH_PROGS(PERL, $PERL perl5 perl )
@@ -4915,16 +4922,17 @@ cairo-gonk)
     AC_DEFINE(MOZ_TOUCH)
     MOZ_WIDGET_TOOLKIT=gonk
     TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)'
     TK_LIBS='$(MOZ_CAIRO_LIBS)'
     MOZ_WEBGL=1
     MOZ_PDF_PRINTING=1
     MOZ_B2G_RIL=1
     MOZ_TOUCH=1
+    MOZ_B2G_BT=1
     ;;
 
 esac
 
 AC_SUBST(MOZ_OLD_LINKER)
 AC_SUBST(MOZ_PDF_PRINTING)
 if test "$MOZ_PDF_PRINTING"; then
    PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1"
@@ -7586,16 +7594,28 @@ MOZ_ARG_ENABLE_BOOL(b2g-ril,
     MOZ_B2G_RIL=1,
     MOZ_B2G_RIL= )
 if test -n "$MOZ_B2G_RIL"; then
    AC_DEFINE(MOZ_B2G_RIL)
 fi
 AC_SUBST(MOZ_B2G_RIL)
 
 dnl ========================================================
+dnl = Enable Bluetooth Interface for B2G (Gonk usually)
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(b2g-bt,
+[  --enable-b2g-bt      Set compile flags necessary for compiling Bluetooth API for B2G ],
+    MOZ_B2G_BT=1,
+    MOZ_B2G_BT= )
+if test -n "$MOZ_B2G_BT"; then
+   AC_DEFINE(MOZ_B2G_BT)
+fi
+AC_SUBST(MOZ_B2G_BT)
+
+dnl ========================================================
 dnl = Support for demangling undefined symbols
 dnl ========================================================
 if test -z "$SKIP_LIBRARY_CHECKS"; then
     AC_LANG_SAVE
     AC_LANG_CPLUSPLUS
     AC_CHECK_FUNCS(__cxa_demangle, HAVE_DEMANGLE=1, HAVE_DEMANGLE=)
     AC_LANG_RESTORE
 fi
deleted file mode 100644
--- a/content/base/public/nsIMessageWakeupService.idl
+++ /dev/null
@@ -1,74 +0,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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is the Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alon Zakai <azakai@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 ***** */
-
-#include "nsISupports.idl"
-
-/**
- * This service lets other components be woken up when particular
- * messageManager messages arrive. By using this wakeup service,
- * those components do not need to be started until they are
- * needed.
- *
- * The parentprocessmessagemanager is used for this, so messages
- * send from childprocessmessagemanagers will be heard.
- *
- * Components can request wakeups using the .manifest files, or
- * by someone else calling requestWakeup. For .manifest files,
- * the line should look something like
- *
- *   category wakeup-request nsComponent @mozilla.org/myservice;1,
- *            nsIMyInterface,getService,myMessage1,myMessage2[,..]
- *
- * Currently we require services to expose wrappedJSObject, but
- * that will be cleaned up in bug 593407, at which point the
- * service that will be woken up must implement
- * nsIFrameMessageListener.
- */
-[scriptable, uuid(968e31b6-b859-42f3-8140-014378fe1783)]
-interface nsIMessageWakeupService : nsISupports
-{
-  /**
-   * Requests that the wakeup service wake us up when a particular
-   * message arrives. At that time the service will be woken up
-   * and subscribed to receive further messages of that name as
-   * well.
-   */
-  boolean requestWakeup(in AString aMessageName,
-                        in AString aCid,
-                        in AString aIid,
-                        in AString aMethod);
-};
-
--- a/content/base/src/messageWakeupService.js
+++ b/content/base/src/messageWakeupService.js
@@ -41,17 +41,17 @@ const Ci = Components.interfaces;
 
 const CATEGORY_WAKEUP_REQUEST = "wakeup-request";
 
 function MessageWakeupService() { };
 
 MessageWakeupService.prototype =
 {
   classID:          Components.ID("{f9798742-4f7b-4188-86ba-48b116412b29}"),
-  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIMessageWakeupService, Ci.nsISupports, Ci.nsIObserver]),
+  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   messagesData: [],
 
   get messageManager() {
     if (!this._messageManager)
       this._messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"].
                              getService(Ci.nsIFrameMessageManager);
     return this._messageManager;
--- a/content/base/src/nsAttrAndChildArray.h
+++ b/content/base/src/nsAttrAndChildArray.h
@@ -131,16 +131,20 @@ public:
 
   bool CanFitMoreAttrs() const
   {
     return AttrSlotCount() < ATTRCHILD_ARRAY_MAX_ATTR_COUNT ||
            !AttrSlotIsTaken(ATTRCHILD_ARRAY_MAX_ATTR_COUNT - 1);
   }
 
   PRInt64 SizeOf() const;
+  bool HasMappedAttrs() const
+  {
+    return MappedAttrCount();
+  }
 
 private:
   nsAttrAndChildArray(const nsAttrAndChildArray& aOther) MOZ_DELETE;
   nsAttrAndChildArray& operator=(const nsAttrAndChildArray& aOther) MOZ_DELETE;
 
   void Clear();
 
   PRUint32 NonMappedAttrCount() const;
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -49,16 +49,18 @@
 #include "nsIHTMLDocument.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "prprf.h"
 
 namespace css = mozilla::css;
 
+using mozilla::SVGAttrValueWrapper;
+
 #define MISC_STR_PTR(_cont) \
   reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
 
 nsTArray<const nsAttrValue::EnumTable*>* nsAttrValue::sEnumTableArray = nsnull;
 
 nsAttrValue::nsAttrValue()
     : mBits(0)
 {
@@ -71,16 +73,22 @@ nsAttrValue::nsAttrValue(const nsAttrVal
 }
 
 nsAttrValue::nsAttrValue(const nsAString& aValue)
     : mBits(0)
 {
   SetTo(aValue);
 }
 
+nsAttrValue::nsAttrValue(nsIAtom* aValue)
+    : mBits(0)
+{
+  SetTo(aValue);
+}
+
 nsAttrValue::nsAttrValue(css::StyleRule* aValue, const nsAString* aSerialized)
     : mBits(0)
 {
   SetTo(aValue, aSerialized);
 }
 
 nsAttrValue::nsAttrValue(const nsIntMargin& aValue)
     : mBits(0)
@@ -255,17 +263,23 @@ nsAttrValue::SetTo(const nsAttrValue& aO
     case eIntMarginValue:
     {
       if (otherCont->mIntMargin)
         cont->mIntMargin = new nsIntMargin(*otherCont->mIntMargin);
       break;
     }
     default:
     {
-      NS_NOTREACHED("unknown type stored in MiscContainer");
+      if (IsSVGType(otherCont->mType)) {
+        // All SVG types are just pointers to classes and will therefore have
+        // the same size so it doesn't really matter which one we assign
+        cont->mSVGAngle = otherCont->mSVGAngle;
+      } else {
+        NS_NOTREACHED("unknown type stored in MiscContainer");
+      }
       break;
     }
   }
 
   void* otherPtr = MISC_STR_PTR(otherCont);
   if (otherPtr) {
     if (static_cast<ValueBaseType>(otherCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) ==
         eStringBase) {
@@ -286,23 +300,51 @@ nsAttrValue::SetTo(const nsAString& aVal
   ResetIfSet();
   nsStringBuffer* buf = GetStringBuffer(aValue);
   if (buf) {
     SetPtrValueAndType(buf, eStringBase);
   }
 }
 
 void
+nsAttrValue::SetTo(nsIAtom* aValue)
+{
+  ResetIfSet();
+  if (aValue) {
+    NS_ADDREF(aValue);
+    SetPtrValueAndType(aValue, eAtomBase);
+  }
+}
+
+void
 nsAttrValue::SetTo(PRInt16 aInt)
 {
   ResetIfSet();
   SetIntValueAndType(aInt, eInteger, nsnull);
 }
 
 void
+nsAttrValue::SetTo(PRInt32 aInt, const nsAString* aSerialized)
+{
+  ResetIfSet();
+  SetIntValueAndType(aInt, eInteger, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(double aValue, const nsAString* aSerialized)
+{
+  if (EnsureEmptyMiscContainer()) {
+    MiscContainer* cont = GetMiscContainer();
+    cont->mDoubleValue = aValue;
+    cont->mType = eDoubleValue;
+    SetMiscAtomOrString(aSerialized);
+  }
+}
+
+void
 nsAttrValue::SetTo(css::StyleRule* aValue, const nsAString* aSerialized)
 {
   if (EnsureEmptyMiscContainer()) {
     MiscContainer* cont = GetMiscContainer();
     NS_ADDREF(cont->mCSSStyleRule = aValue);
     cont->mType = eCSSStyleRule;
     SetMiscAtomOrString(aSerialized);
   }
@@ -327,16 +369,125 @@ nsAttrValue::SetToSerialized(const nsAtt
     aOther.ToString(val);
     SetTo(val);
   } else {
     SetTo(aOther);
   }
 }
 
 void
+nsAttrValue::SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized)
+{
+  SetSVGType(eSVGAngle, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized)
+{
+  SetSVGType(eSVGIntegerPair, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized)
+{
+  SetSVGType(eSVGLength, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGLengthList& aValue,
+                   const nsAString* aSerialized)
+{
+  // While an empty string will parse as a length list, there's no need to store
+  // it (and SetMiscAtomOrString will assert if we try)
+  if (aSerialized && aSerialized->IsEmpty()) {
+    aSerialized = nsnull;
+  }
+  SetSVGType(eSVGLengthList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGNumberList& aValue,
+                   const nsAString* aSerialized)
+{
+  // While an empty string will parse as a number list, there's no need to store
+  // it (and SetMiscAtomOrString will assert if we try)
+  if (aSerialized && aSerialized->IsEmpty()) {
+    aSerialized = nsnull;
+  }
+  SetSVGType(eSVGNumberList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized)
+{
+  SetSVGType(eSVGNumberPair, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGPathData& aValue,
+                   const nsAString* aSerialized)
+{
+  // While an empty string will parse as path data, there's no need to store it
+  // (and SetMiscAtomOrString will assert if we try)
+  if (aSerialized && aSerialized->IsEmpty()) {
+    aSerialized = nsnull;
+  }
+  SetSVGType(eSVGPathData, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGPointList& aValue,
+                   const nsAString* aSerialized)
+{
+  // While an empty string will parse as a point list, there's no need to store
+  // it (and SetMiscAtomOrString will assert if we try)
+  if (aSerialized && aSerialized->IsEmpty()) {
+    aSerialized = nsnull;
+  }
+  SetSVGType(eSVGPointList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGAnimatedPreserveAspectRatio& aValue,
+                   const nsAString* aSerialized)
+{
+  SetSVGType(eSVGPreserveAspectRatio, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGStringList& aValue,
+                   const nsAString* aSerialized)
+{
+  // While an empty string will parse as a string list, there's no need to store
+  // it (and SetMiscAtomOrString will assert if we try)
+  if (aSerialized && aSerialized->IsEmpty()) {
+    aSerialized = nsnull;
+  }
+  SetSVGType(eSVGStringList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGTransformList& aValue,
+                   const nsAString* aSerialized)
+{
+  // While an empty string will parse as a transform list, there's no need to
+  // store it (and SetMiscAtomOrString will assert if we try)
+  if (aSerialized && aSerialized->IsEmpty()) {
+    aSerialized = nsnull;
+  }
+  SetSVGType(eSVGTransformList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized)
+{
+  SetSVGType(eSVGViewBox, &aValue, aSerialized);
+}
+
+void
 nsAttrValue::SwapValueWith(nsAttrValue& aOther)
 {
   PtrBits tmp = aOther.mBits;
   aOther.mBits = mBits;
   mBits = tmp;
 }
 
 void
@@ -423,16 +574,83 @@ nsAttrValue::ToString(nsAString& aResult
       break;
     }
     case eDoubleValue:
     {
       aResult.Truncate();
       aResult.AppendFloat(GetDoubleValue());
       break;
     }
+    case eSVGAngle:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGAngle, aResult);
+      break;
+    }
+    case eSVGIntegerPair:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGIntegerPair,
+                                    aResult);
+      break;
+    }
+    case eSVGLength:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGLength, aResult);
+      break;
+    }
+    case eSVGLengthList:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGLengthList,
+                                    aResult);
+      break;
+    }
+    case eSVGNumberList:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGNumberList,
+                                    aResult);
+      break;
+    }
+    case eSVGNumberPair:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGNumberPair,
+                                    aResult);
+      break;
+    }
+    case eSVGPathData:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGPathData, aResult);
+      break;
+    }
+    case eSVGPointList:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGPointList, aResult);
+      break;
+    }
+    case eSVGPreserveAspectRatio:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGPreserveAspectRatio,
+                                    aResult);
+      break;
+    }
+    case eSVGStringList:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGStringList,
+                                    aResult);
+      break;
+    }
+    case eSVGTransformList:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGTransformList,
+                                    aResult);
+      break;
+    }
+    case eSVGViewBox:
+    {
+      SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGViewBox, aResult);
+      break;
+    }
     default:
     {
       aResult.Truncate();
       break;
     }
   }
 }
 
@@ -609,16 +827,20 @@ nsAttrValue::HashValue() const
       return cont->mDoubleValue;
     }
     case eIntMarginValue:
     {
       return NS_PTR_TO_INT32(cont->mIntMargin);
     }
     default:
     {
+      if (IsSVGType(cont->mType)) {
+        // All SVG types are just pointers to classes so we can treat them alike
+        return NS_PTR_TO_INT32(cont->mSVGAngle);
+      }
       NS_NOTREACHED("unknown type stored in MiscContainer");
       return 0;
     }
   }
 }
 
 bool
 nsAttrValue::Equals(const nsAttrValue& aOther) const
@@ -701,16 +923,26 @@ nsAttrValue::Equals(const nsAttrValue& a
       return thisCont->mDoubleValue == otherCont->mDoubleValue;
     }
     case eIntMarginValue:
     {
       return thisCont->mIntMargin == otherCont->mIntMargin;
     }
     default:
     {
+      if (IsSVGType(thisCont->mType)) {
+        // Currently this method is never called for nsAttrValue objects that
+        // point to SVG data types.
+        // If that changes then we probably want to add methods to the
+        // corresponding SVG types to compare their base values.
+        // As a shortcut, however, we can begin by comparing the pointers.
+        NS_ABORT_IF_FALSE(false, "Comparing nsAttrValues that point to SVG "
+          "data");
+        return false;
+      }
       NS_NOTREACHED("unknown type stored in MiscContainer");
       return false;
     }
   }
   if (needsStringComparison) {
     if (thisCont->mStringBits == otherCont->mStringBits) {
       return true;
     }
@@ -1346,16 +1578,32 @@ nsAttrValue::ResetMiscAtomOrString()
       static_cast<nsStringBuffer*>(ptr)->Release();
     } else {
       static_cast<nsIAtom*>(ptr)->Release();
     }
     cont->mStringBits = 0;
   }
 }
 
+void
+nsAttrValue::SetSVGType(ValueType aType, const void* aValue,
+                        const nsAString* aSerialized) {
+  NS_ABORT_IF_FALSE(IsSVGType(aType), "Not an SVG type");
+  if (EnsureEmptyMiscContainer()) {
+    MiscContainer* cont = GetMiscContainer();
+    // All SVG types are just pointers to classes so just setting any of them
+    // will do. We'll lose type-safety but the signature of the calling
+    // function should ensure we don't get anything unexpected, and once we
+    // stick aValue in a union we lose type information anyway.
+    cont->mSVGAngle = static_cast<const nsSVGAngle*>(aValue);
+    cont->mType = aType;
+    SetMiscAtomOrString(aSerialized);
+  }
+}
+
 bool
 nsAttrValue::EnsureEmptyMiscContainer()
 {
   MiscContainer* cont;
   if (BaseType() == eOtherBase) {
     ResetMiscAtomOrString();
     cont = GetMiscContainer();
     switch (cont->mType) {
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -46,16 +46,17 @@
 
 #include "nscore.h"
 #include "nsString.h"
 #include "nsStringBuffer.h"
 #include "nsColor.h"
 #include "nsCaseTreatment.h"
 #include "nsMargin.h"
 #include "nsCOMPtr.h"
+#include "SVGAttrValueWrapper.h"
 
 typedef PRUptrdiff PtrBits;
 class nsAString;
 class nsIAtom;
 class nsIDocument;
 template<class E, class A> class nsTArray;
 struct nsTArrayDefaultAllocator;
 
@@ -97,16 +98,17 @@ public:
 
 class nsAttrValue {
 public:
   typedef nsTArray< nsCOMPtr<nsIAtom> > AtomArray;
 
   nsAttrValue();
   nsAttrValue(const nsAttrValue& aOther);
   explicit nsAttrValue(const nsAString& aValue);
+  explicit nsAttrValue(nsIAtom* aValue);
   nsAttrValue(mozilla::css::StyleRule* aValue, const nsAString* aSerialized);
   explicit nsAttrValue(const nsIntMargin& aValue);
   ~nsAttrValue();
 
   inline const nsAttrValue& operator=(const nsAttrValue& aOther);
 
   static nsresult Init();
   static void Shutdown();
@@ -118,30 +120,64 @@ public:
     eAtom =         0x02, //   10
     eInteger =      0x03, // 0011
     eColor =        0x07, // 0111
     eEnum =         0x0B, // 1011  This should eventually die
     ePercent =      0x0F, // 1111
     // Values below here won't matter, they'll be always stored in the 'misc'
     // struct.
     eCSSStyleRule =    0x10
-    ,eAtomArray =      0x11 
+    ,eAtomArray =      0x11
     ,eDoubleValue  =   0x12
     ,eIntMarginValue = 0x13
+    ,eSVGTypesBegin =  0x14
+    ,eSVGAngle =       eSVGTypesBegin
+    ,eSVGIntegerPair = 0x15
+    ,eSVGLength =      0x16
+    ,eSVGLengthList =  0x17
+    ,eSVGNumberList =  0x18
+    ,eSVGNumberPair =  0x19
+    ,eSVGPathData   =  0x20
+    ,eSVGPointList  =  0x21
+    ,eSVGPreserveAspectRatio = 0x22
+    ,eSVGStringList =  0x23
+    ,eSVGTransformList = 0x24
+    ,eSVGViewBox =     0x25
+    ,eSVGTypesEnd =    0x34
   };
 
   ValueType Type() const;
 
   void Reset();
 
   void SetTo(const nsAttrValue& aOther);
   void SetTo(const nsAString& aValue);
+  void SetTo(nsIAtom* aValue);
   void SetTo(PRInt16 aInt);
+  void SetTo(PRInt32 aInt, const nsAString* aSerialized);
+  void SetTo(double aValue, const nsAString* aSerialized);
   void SetTo(mozilla::css::StyleRule* aValue, const nsAString* aSerialized);
   void SetTo(const nsIntMargin& aValue);
+  void SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized);
+  void SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized);
+  void SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized);
+  void SetTo(const mozilla::SVGLengthList& aValue,
+             const nsAString* aSerialized);
+  void SetTo(const mozilla::SVGNumberList& aValue,
+             const nsAString* aSerialized);
+  void SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized);
+  void SetTo(const mozilla::SVGPathData& aValue, const nsAString* aSerialized);
+  void SetTo(const mozilla::SVGPointList& aValue, const nsAString* aSerialized);
+  void SetTo(const mozilla::SVGAnimatedPreserveAspectRatio& aValue,
+             const nsAString* aSerialized);
+  void SetTo(const mozilla::SVGStringList& aValue,
+             const nsAString* aSerialized);
+  void SetTo(const mozilla::SVGTransformList& aValue,
+             const nsAString* aSerialized);
+  void SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized);
 
   /**
    * Sets this object with the string or atom representation of aValue.
    *
    * After calling this method, this object will have type eString unless the
    * type of aValue is eAtom, in which case this object will also have type
    * eAtom.
    */
@@ -363,36 +399,51 @@ private:
       PRInt32 mInteger;
       nscolor mColor;
       PRUint32 mEnumValue;
       PRInt32 mPercent;
       mozilla::css::StyleRule* mCSSStyleRule;
       AtomArray* mAtomArray;
       double mDoubleValue;
       nsIntMargin* mIntMargin;
+      const nsSVGAngle* mSVGAngle;
+      const nsSVGIntegerPair* mSVGIntegerPair;
+      const nsSVGLength2* mSVGLength;
+      const mozilla::SVGLengthList* mSVGLengthList;
+      const mozilla::SVGNumberList* mSVGNumberList;
+      const nsSVGNumberPair* mSVGNumberPair;
+      const mozilla::SVGPathData* mSVGPathData;
+      const mozilla::SVGPointList* mSVGPointList;
+      const mozilla::SVGAnimatedPreserveAspectRatio* mSVGPreserveAspectRatio;
+      const mozilla::SVGStringList* mSVGStringList;
+      const mozilla::SVGTransformList* mSVGTransformList;
+      const nsSVGViewBox* mSVGViewBox;
     };
   };
 
   inline ValueBaseType BaseType() const;
+  inline bool IsSVGType(ValueType aType) const;
 
   /**
    * Get the index of an EnumTable in the sEnumTableArray.
    * If the EnumTable is not in the sEnumTableArray, it is added.
    *
    * @param aTable   the EnumTable to get the index of.
    * @return         the index of the EnumTable.
    */
   PRInt16  GetEnumTableIndex(const EnumTable* aTable);
 
   inline void SetPtrValueAndType(void* aValue, ValueBaseType aType);
   void SetIntValueAndType(PRInt32 aValue, ValueType aType,
                           const nsAString* aStringValue);
   void SetColorValue(nscolor aColor, const nsAString& aString);
   void SetMiscAtomOrString(const nsAString* aValue);
   void ResetMiscAtomOrString();
+  void SetSVGType(ValueType aType, const void* aValue,
+                  const nsAString* aSerialized);
   inline void ResetIfSet();
 
   inline void* GetPtr() const;
   inline MiscContainer* GetMiscContainer() const;
   inline PRInt32 GetIntInternal() const;
 
   bool EnsureEmptyMiscContainer();
   bool EnsureEmptyAtomArray();
@@ -497,16 +548,22 @@ nsAttrValue::GetIntMarginValue(nsIntMarg
 }
 
 inline nsAttrValue::ValueBaseType
 nsAttrValue::BaseType() const
 {
   return static_cast<ValueBaseType>(mBits & NS_ATTRVALUE_BASETYPE_MASK);
 }
 
+inline bool
+nsAttrValue::IsSVGType(ValueType aType) const
+{
+  return aType >= eSVGTypesBegin && aType <= eSVGTypesEnd;
+}
+
 inline void
 nsAttrValue::SetPtrValueAndType(void* aValue, ValueBaseType aType)
 {
   NS_ASSERTION(!(NS_PTR_TO_INT32(aValue) & ~NS_ATTRVALUE_POINTERVALUE_MASK),
                "pointer not properly aligned, this will crash");
   mBits = reinterpret_cast<PtrBits>(aValue) | aType;
 }
 
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -84,16 +84,17 @@ using namespace mozilla;
 #define MAX_RECONNECTION_TIME_VALUE       PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
 
 nsEventSource::nsEventSource() :
   mStatus(PARSE_STATE_OFF),
   mFrozen(false),
   mErrorLoadOnRedirect(false),
   mGoingToDispatchAllMessages(false),
   mWithCredentials(false),
+  mWaitingForOnStopRequest(false),
   mLastConvertionResult(NS_OK),
   mReadyState(nsIEventSource::CONNECTING),
   mScriptLine(0),
   mInnerWindowID(0)
 {
 }
 
 nsEventSource::~nsEventSource()
@@ -103,23 +104,29 @@ nsEventSource::~nsEventSource()
 
 //-----------------------------------------------------------------------------
 // nsEventSource::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventSource)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsEventSource)
-  if (tmp->IsBlack()) {
+  bool isBlack = tmp->IsBlack();
+  if (isBlack || tmp->mWaitingForOnStopRequest) {
     if (tmp->mListenerManager) {
       tmp->mListenerManager->UnmarkGrayJSListeners();
       NS_UNMARK_LISTENER_WRAPPER(Open)
       NS_UNMARK_LISTENER_WRAPPER(Message)
       NS_UNMARK_LISTENER_WRAPPER(Error)
     }
+    if (!isBlack) {
+      xpc_UnmarkGrayObject(tmp->PreservingWrapper() ? 
+                           tmp->GetWrapperPreserveColor() :
+                           tmp->GetExpandoObjectPreserveColor());
+    }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsEventSource)
   return tmp->IsBlack();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
@@ -594,16 +601,18 @@ nsEventSource::OnDataAvailable(nsIReques
                                     aCount, &totalRead);
 }
 
 NS_IMETHODIMP
 nsEventSource::OnStopRequest(nsIRequest *aRequest,
                              nsISupports *aContext,
                              nsresult aStatusCode)
 {
+  mWaitingForOnStopRequest = false;
+
   if (mReadyState == nsIEventSource::CLOSED) {
     return NS_ERROR_ABORT;
   }
 
   if (NS_FAILED(aStatusCode)) {
     DispatchFailConnection();
     return aStatusCode;
   }
@@ -944,17 +953,21 @@ nsEventSource::InitChannelAndRequestEven
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIStreamListener> listener =
     new nsCORSListenerProxy(this, mPrincipal, mHttpChannel,
                             mWithCredentials, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Start reading from the channel
-  return mHttpChannel->AsyncOpen(listener, nsnull);
+  rv = mHttpChannel->AsyncOpen(listener, nsnull);
+  if (NS_SUCCEEDED(rv)) {
+    mWaitingForOnStopRequest = true;
+  }
+  return rv;
 }
 
 void
 nsEventSource::AnnounceConnection()
 {
   if (mReadyState == nsIEventSource::CLOSED) {
     return;
   }
--- a/content/base/src/nsEventSource.h
+++ b/content/base/src/nsEventSource.h
@@ -211,16 +211,17 @@ protected:
     PARSE_STATE_BEGIN_OF_LINE
   };
   ParserStatus mStatus;
 
   bool mFrozen;
   bool mErrorLoadOnRedirect;
   bool mGoingToDispatchAllMessages;
   bool mWithCredentials;
+  bool mWaitingForOnStopRequest;
 
   // used while reading the input streams
   nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
   nsresult mLastConvertionResult;
   nsString mLastFieldName;
   nsString mLastFieldValue;
 
   nsRefPtr<nsDOMEventListenerWrapper> mOnOpenListener;
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -434,24 +434,30 @@ nsWebSocket::~nsWebSocket()
 
   Disconnect();
   nsLayoutStatics::Release();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsWebSocket)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket)
-  if (tmp->IsBlack()) {
+  bool isBlack = tmp->IsBlack();
+  if (isBlack|| tmp->mKeepingAlive) {
     if (tmp->mListenerManager) {
       tmp->mListenerManager->UnmarkGrayJSListeners();
       NS_UNMARK_LISTENER_WRAPPER(Open)
       NS_UNMARK_LISTENER_WRAPPER(Error)
       NS_UNMARK_LISTENER_WRAPPER(Message)
       NS_UNMARK_LISTENER_WRAPPER(Close)
     }
+    if (!isBlack) {
+      xpc_UnmarkGrayObject(tmp->PreservingWrapper() ? 
+                           tmp->GetWrapperPreserveColor() :
+                           tmp->GetExpandoObjectPreserveColor());
+    }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsWebSocket)
   return tmp->IsBlack();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -440,18 +440,18 @@ NS_IMPL_RELEASE_INHERITED(nsXMLHttpReque
 nsXMLHttpRequest::nsXMLHttpRequest()
   : mResponseBodyDecodedPos(0),
     mResponseType(XML_HTTP_RESPONSE_TYPE_DEFAULT),
     mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNSENT),
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     mProgressSinceLastProgressEvent(false),
     mUploadProgress(0), mUploadProgressMax(0),
     mRequestSentTime(0), mTimeoutMilliseconds(0),
-    mErrorLoad(false), mProgressTimerIsActive(false),
-    mProgressEventWasDelayed(false),
+    mErrorLoad(false), mWaitingForOnStopRequest(false),
+    mProgressTimerIsActive(false), mProgressEventWasDelayed(false),
     mIsHtml(false),
     mWarnAboutMultipartHtml(false),
     mWarnAboutSyncHtml(false),
     mLoadLengthComputable(false), mLoadTotal(0),
     mFirstStartRequestSeen(false),
     mInLoadProgressEvent(false),
     mResultJSON(JSVAL_VOID),
     mResultArrayBuffer(nsnull)
@@ -597,28 +597,34 @@ void
 nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
 {
   mRequestObserver = aObserver;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
-  if (tmp->IsBlack()) {
+  bool isBlack = tmp->IsBlack();
+  if (isBlack || tmp->mWaitingForOnStopRequest) {
     if (tmp->mListenerManager) {
       tmp->mListenerManager->UnmarkGrayJSListeners();
       NS_UNMARK_LISTENER_WRAPPER(Load)
       NS_UNMARK_LISTENER_WRAPPER(Error)
       NS_UNMARK_LISTENER_WRAPPER(Abort)
       NS_UNMARK_LISTENER_WRAPPER(LoadStart)
       NS_UNMARK_LISTENER_WRAPPER(Progress)
       NS_UNMARK_LISTENER_WRAPPER(Loadend)
       NS_UNMARK_LISTENER_WRAPPER(UploadProgress)
       NS_UNMARK_LISTENER_WRAPPER(Readystatechange)
     }
+    if (!isBlack) {
+      xpc_UnmarkGrayObject(tmp->PreservingWrapper() ? 
+                           tmp->GetWrapperPreserveColor() :
+                           tmp->GetExpandoObjectPreserveColor());
+    }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXMLHttpRequest)
   return tmp->IsBlack();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
@@ -2135,16 +2141,18 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
 NS_IMETHODIMP
 nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
 {
   SAMPLE_LABEL("content", "nsXMLHttpRequest::OnStopRequest");
   if (!IsSameOrBaseChannel(request, mChannel)) {
     return NS_OK;
   }
 
+  mWaitingForOnStopRequest = false;
+
   nsresult rv = NS_OK;
 
   // If we're loading a multipart stream of XML documents, we'll get
   // an OnStopRequest() for the last part in the stream, and then
   // another one for the end of the initiating
   // "multipart/x-mixed-replace" stream too. So we must check that we
   // still have an xml parser stream listener before accessing it
   // here.
@@ -2769,16 +2777,19 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
 
   if (NS_FAILED(rv)) {
     // Drop our ref to the channel to avoid cycles
     mChannel = nsnull;
     mCORSPreflightChannel = nsnull;
     return rv;
   }
 
+  // Either AsyncOpen was called, or CORS will open the channel later.
+  mWaitingForOnStopRequest = true;
+
   // If we're synchronous, spin an event loop here and wait
   if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
     mState |= XML_HTTP_REQUEST_SYNCLOOPING;
 
     nsCOMPtr<nsIDocument> suspendedDoc;
     nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
     if (mOwner) {
       nsCOMPtr<nsIDOMWindow> topWindow;
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -360,17 +360,17 @@ protected:
   // Timeout support
   PRTime mRequestSentTime;
   PRUint32 mTimeoutMilliseconds;
   nsCOMPtr<nsITimer> mTimeoutTimer;
   void StartTimeoutTimer();
   void HandleTimeoutCallback();
 
   bool mErrorLoad;
-
+  bool mWaitingForOnStopRequest;
   bool mProgressTimerIsActive;
   bool mProgressEventWasDelayed;
   bool mIsHtml;
   bool mWarnAboutMultipartHtml;
   bool mWarnAboutSyncHtml;
   bool mLoadLengthComputable;
   PRUint64 mLoadTotal; // 0 if not known.
   PRUint64 mLoadTransferred;
--- a/content/base/test/test_bug338679.html
+++ b/content/base/test/test_bug338679.html
@@ -44,17 +44,17 @@ function attr_modified(ev) {
   is(ev.newValue, e_new,
      phase.name + (recursive ? " recursive" : "") + ": newValue");
   is(ev.prevValue, e_prev,
      phase.name + (recursive ? " recursive" : "") + ": prevValue");
 
   e_prev = e_new;
   if (!recursive) {
     recursive = true;
-    e_new = "width: 0pt;";
+    e_new = "width: 0px;";
     testDiv.style.width = "0";
   } else {
     recursive = false;
     setTimeout(nextTest, 0);
   }
 }
 
 /* tests */
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -2383,16 +2383,17 @@ class WebGLExtension
 {
 public:
     WebGLExtension(WebGLContext *baseContext)
         : WebGLContextBoundObject(baseContext)
     {}
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBGLEXTENSION
+    virtual ~WebGLExtension() {}
 };
 
 inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const {
     return mBoundFramebuffer ? mBoundFramebuffer->RectangleObject()
                              : static_cast<const WebGLRectangleObject*>(this);
 }
 
 /**
--- a/content/html/content/src/nsDOMValidityState.h
+++ b/content/html/content/src/nsDOMValidityState.h
@@ -37,17 +37,17 @@
 
 #ifndef nsDOMValidityState_h__
 #define nsDOMValidityState_h__
 
 #include "nsIDOMValidityState.h"
 #include "nsIConstraintValidation.h"
 
 
-class nsDOMValidityState : public nsIDOMValidityState
+class nsDOMValidityState MOZ_FINAL : public nsIDOMValidityState
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMVALIDITYSTATE
 
   friend class nsIConstraintValidation;
 
 protected:
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -180,17 +180,17 @@ static const nsAttrValue::EnumTable* kIn
 #define NS_INPUT_ELEMENT_STATE_IID                 \
 { /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */       \
   0xdc3b3d14,                                      \
   0x23e2,                                          \
   0x4479,                                          \
   {0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \
 }
 
-class nsHTMLInputElementState : public nsISupports
+class nsHTMLInputElementState MOZ_FINAL : public nsISupports
 {
   public:
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_INPUT_ELEMENT_STATE_IID)
     NS_DECL_ISUPPORTS
 
     bool IsCheckedSet() {
       return mCheckedSet;
     }
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -52,17 +52,17 @@
 #include "nsHTMLFormElement.h" // for ShouldShowInvalidUI()
 #include "nsIFile.h"
 
 class nsDOMFileList;
 class nsIRadioGroupContainer;
 class nsIRadioGroupVisitor;
 class nsIRadioVisitor;
 
-class UploadLastDir : public nsIObserver, public nsSupportsWeakReference {
+class UploadLastDir MOZ_FINAL : public nsIObserver, public nsSupportsWeakReference {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   /**
    * Fetch the last used directory for this location from the content
    * pref service, if it is available.
    *
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -238,20 +238,20 @@ public:
 };
 
 /**
  * There is a reference cycle involving this class: MediaLoadListener
  * holds a reference to the nsHTMLMediaElement, which holds a reference
  * to an nsIChannel, which holds a reference to this listener.
  * We break the reference cycle in OnStartRequest by clearing mElement.
  */
-class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener,
-                                              public nsIChannelEventSink,
-                                              public nsIInterfaceRequestor,
-                                              public nsIObserver
+class nsHTMLMediaElement::MediaLoadListener MOZ_FINAL : public nsIStreamListener,
+                                                        public nsIChannelEventSink,
+                                                        public nsIInterfaceRequestor,
+                                                        public nsIObserver
 {
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSICHANNELEVENTSINK
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIINTERFACEREQUESTOR
 
--- a/content/html/content/src/nsMediaError.h
+++ b/content/html/content/src/nsMediaError.h
@@ -32,18 +32,19 @@
  * 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 "nsIDOMMediaError.h"
 #include "nsISupports.h"
+#include "mozilla/Attributes.h"
 
-class nsMediaError : public nsIDOMMediaError
+class nsMediaError MOZ_FINAL : public nsIDOMMediaError
 {
 public:
   nsMediaError(PRUint16 aCode);
 
   // nsISupports
   NS_DECL_ISUPPORTS
 
   // nsIDOMMediaError
--- a/content/html/content/src/nsTimeRanges.h
+++ b/content/html/content/src/nsTimeRanges.h
@@ -40,17 +40,17 @@
 #define nsTimeRanges_h__
 
 #include "nsIDOMTimeRanges.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 // Implements media TimeRanges:
 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#timeranges
-class nsTimeRanges : public nsIDOMTimeRanges {
+class nsTimeRanges MOZ_FINAL : public nsIDOMTimeRanges {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMTIMERANGES
 
   nsTimeRanges();
   ~nsTimeRanges();
 
   void Add(double aStart, double aEnd);
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -211,16 +211,17 @@ nsresult nsBuiltinDecoder::Load(MediaRes
                                            cloneDonor->mDecoderStateMachine : nsnull))) {
     LOG(PR_LOG_DEBUG, ("%p Failed to init state machine!", this));
     return NS_ERROR_FAILURE;
   }
   {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mDecoderStateMachine->SetSeekable(mSeekable);
     mDecoderStateMachine->SetDuration(mDuration);
+    mDecoderStateMachine->SetVolume(mInitialVolume);
     
     if (mFrameBufferLength > 0) {
       // The valid mFrameBufferLength value was specified earlier
       mDecoderStateMachine->SetFrameBufferLength(mFrameBufferLength);
     }
   }
 
   ChangeState(PLAY_STATE_LOADING);
--- a/content/svg/content/src/DOMSVGLength.cpp
+++ b/content/svg/content/src/DOMSVGLength.cpp
@@ -151,18 +151,24 @@ DOMSVGLength::SetValue(float aUserUnitVa
   }
 
   // Although the value passed in is in user units, this method does not turn
   // this length into a user unit length. Instead it converts the user unit
   // value to this length's current unit and sets that, leaving this length's
   // unit as it is.
 
   if (HasOwner()) {
-    if (InternalItem().SetFromUserUnitValue(aUserUnitValue, Element(), Axis())) {
-      Element()->DidChangeLengthList(mAttrEnum, true);
+    if (InternalItem().GetValueInUserUnits(Element(), Axis()) ==
+        aUserUnitValue) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
+    if (InternalItem().SetFromUserUnitValue(aUserUnitValue, Element(), Axis()))
+    {
+      Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
       if (mList->mAList->IsAnimating()) {
         Element()->AnimationNeedsResample();
       }
       return NS_OK;
     }
   } else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER ||
              mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) {
     mValue = aUserUnitValue;
@@ -190,18 +196,22 @@ DOMSVGLength::SetValueInSpecifiedUnits(f
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (!NS_finite(aValue)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (HasOwner()) {
+    if (InternalItem().GetValueInCurrentUnits() == aValue) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
     InternalItem().SetValueInCurrentUnits(aValue);
-    Element()->DidChangeLengthList(mAttrEnum, true);
+    Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
     if (mList->mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
     return NS_OK;
   }
   mValue = aValue;
   return NS_OK;
 }
@@ -213,18 +223,22 @@ DOMSVGLength::SetValueAsString(const nsA
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   SVGLength value;
   if (!value.SetValueFromString(aValue)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
   if (HasOwner()) {
+    if (InternalItem() == value) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
     InternalItem() = value;
-    Element()->DidChangeLengthList(mAttrEnum, true);
+    Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
     if (mList->mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
     return NS_OK;
   }
   mValue = value.GetValueInCurrentUnits();
   mUnit = value.GetUnit();
   return NS_OK;
@@ -254,18 +268,23 @@ DOMSVGLength::NewValueSpecifiedUnits(PRU
   if (!NS_finite(aValue)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (!SVGLength::IsValidUnitType(aUnit)) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
   if (HasOwner()) {
+    if (InternalItem().GetUnit() == aUnit &&
+        InternalItem().GetValueInCurrentUnits() == aValue) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
     InternalItem().SetValueAndUnit(aValue, PRUint8(aUnit));
-    Element()->DidChangeLengthList(mAttrEnum, true);
+    Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
     if (mList->mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
     return NS_OK;
   }
   mUnit = PRUint8(aUnit);
   mValue = aValue;
   return NS_OK;
@@ -277,17 +296,22 @@ DOMSVGLength::ConvertToSpecifiedUnits(PR
   if (mIsAnimValItem) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (!SVGLength::IsValidUnitType(aUnit)) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
   if (HasOwner()) {
+    if (InternalItem().GetUnit() == aUnit) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
     if (InternalItem().ConvertToUnit(PRUint8(aUnit), Element(), Axis())) {
+      Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
       return NS_OK;
     }
   } else {
     SVGLength len(mValue, mUnit);
     if (len.ConvertToUnit(PRUint8(aUnit), nsnull, 0)) {
       mValue = len.GetValueInCurrentUnits();
       mUnit = aUnit;
       return NS_OK;
--- a/content/svg/content/src/DOMSVGLengthList.cpp
+++ b/content/svg/content/src/DOMSVGLengthList.cpp
@@ -166,24 +166,25 @@ DOMSVGLengthList::GetNumberOfItems(PRUin
 NS_IMETHODIMP
 DOMSVGLengthList::Clear()
 {
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (Length() > 0) {
+    nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum());
     // Notify any existing DOM items of removal *before* truncating the lists
     // so that they can find their SVGLength internal counterparts and copy
     // their values. This also notifies the animVal list:
     mAList->InternalBaseValListWillChangeTo(SVGLengthList());
 
     mItems.Clear();
     InternalList().Clear();
-    Element()->DidChangeLengthList(AttrEnum(), true);
+    Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
     if (mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -251,30 +252,31 @@ DOMSVGLengthList::InsertItemBefore(nsIDO
   }
 
   // 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;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum());
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(index);
 
   InternalList().InsertItem(index, domItem->ToSVGLength());
   mItems.InsertElementAt(index, domItem.get());
 
   // This MUST come after the insertion into InternalList(), or else under the
   // insertion into InternalList() the values read from domItem would be bad
   // data from InternalList() itself!:
   domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
 
   UpdateListIndicesFromIndex(mItems, index + 1);
 
-  Element()->DidChangeLengthList(AttrEnum(), true);
+  Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   *_retval = domItem.forget().get();
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -293,30 +295,31 @@ DOMSVGLengthList::ReplaceItem(nsIDOMSVGL
   }
   if (index >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
   if (domItem->HasOwner()) {
     domItem = domItem->Copy(); // must do this before changing anything!
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum());
   if (mItems[index]) {
     // Notify any existing DOM item of removal *before* modifying the lists so
     // that the DOM item can copy the *old* value at its index:
     mItems[index]->RemovingFromList();
   }
 
   InternalList()[index] = domItem->ToSVGLength();
   mItems[index] = domItem;
 
   // This MUST come after the ToSVGPoint() call, otherwise that call
   // would end up reading bad data from InternalList()!
   domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
 
-  Element()->DidChangeLengthList(AttrEnum(), true);
+  Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   NS_ADDREF(*_retval = domItem.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -327,16 +330,17 @@ DOMSVGLengthList::RemoveItem(PRUint32 in
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (index >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum());
   // Now that we know we're removing, keep animVal list in sync as necessary.
   // Do this *before* touching InternalList() so the removed item can get its
   // internal value.
   MaybeRemoveItemFromAnimValListAt(index);
 
   // We have to return the removed item, so make sure it exists:
   EnsureItemAt(index);
 
@@ -345,17 +349,17 @@ DOMSVGLengthList::RemoveItem(PRUint32 in
   mItems[index]->RemovingFromList();
   NS_ADDREF(*_retval = mItems[index]);
 
   InternalList().RemoveItem(index);
   mItems.RemoveElementAt(index);
 
   UpdateListIndicesFromIndex(mItems, index);
 
-  Element()->DidChangeLengthList(AttrEnum(), true);
+  Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMSVGLengthList::AppendItem(nsIDOMSVGLength *newItem,
--- a/content/svg/content/src/DOMSVGNumber.cpp
+++ b/content/svg/content/src/DOMSVGNumber.cpp
@@ -118,18 +118,22 @@ DOMSVGNumber::SetValue(float aValue)
 {
   if (mIsAnimValItem) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
 
   if (HasOwner()) {
+    if (InternalItem() == aValue) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(mAttrEnum);
     InternalItem() = aValue;
-    Element()->DidChangeNumberList(mAttrEnum, true);
+    Element()->DidChangeNumberList(mAttrEnum, emptyOrOldValue);
     if (mList->mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
     return NS_OK;
   }
   mValue = aValue;
   return NS_OK;
 }
--- a/content/svg/content/src/DOMSVGNumberList.cpp
+++ b/content/svg/content/src/DOMSVGNumberList.cpp
@@ -166,24 +166,25 @@ DOMSVGNumberList::GetNumberOfItems(PRUin
 NS_IMETHODIMP
 DOMSVGNumberList::Clear()
 {
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (Length() > 0) {
+    nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum());
     // Notify any existing DOM items of removal *before* truncating the lists
     // so that they can find their SVGNumber internal counterparts and copy
     // their values. This also notifies the animVal list:
     mAList->InternalBaseValListWillChangeTo(SVGNumberList());
 
     mItems.Clear();
     InternalList().Clear();
-    Element()->DidChangeNumberList(AttrEnum(), true);
+    Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
     if (mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -251,30 +252,31 @@ DOMSVGNumberList::InsertItemBefore(nsIDO
   }
 
   // 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;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum());
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(index);
 
   InternalList().InsertItem(index, domItem->ToSVGNumber());
   mItems.InsertElementAt(index, domItem.get());
 
   // This MUST come after the insertion into InternalList(), or else under the
   // insertion into InternalList() the values read from domItem would be bad
   // data from InternalList() itself!:
   domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
 
   UpdateListIndicesFromIndex(mItems, index + 1);
 
-  Element()->DidChangeNumberList(AttrEnum(), true);
+  Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   *_retval = domItem.forget().get();
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -293,30 +295,31 @@ DOMSVGNumberList::ReplaceItem(nsIDOMSVGN
   }
   if (index >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
   if (domItem->HasOwner()) {
     domItem = domItem->Clone(); // must do this before changing anything!
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum());
   if (mItems[index]) {
     // Notify any existing DOM item of removal *before* modifying the lists so
     // that the DOM item can copy the *old* value at its index:
     mItems[index]->RemovingFromList();
   }
 
   InternalList()[index] = domItem->ToSVGNumber();
   mItems[index] = domItem;
 
   // This MUST come after the ToSVGPoint() call, otherwise that call
   // would end up reading bad data from InternalList()!
   domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
 
-  Element()->DidChangeNumberList(AttrEnum(), true);
+  Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   NS_ADDREF(*_retval = domItem.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -335,27 +338,28 @@ DOMSVGNumberList::RemoveItem(PRUint32 in
   // Now that we know we're removing, keep animVal list in sync as necessary.
   // Do this *before* touching InternalList() so the removed item can get its
   // internal value.
   MaybeRemoveItemFromAnimValListAt(index);
 
   // We have to return the removed item, so make sure it exists:
   EnsureItemAt(index);
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum());
   // Notify the DOM item of removal *before* modifying the lists so that the
   // DOM item can copy its *old* value:
   mItems[index]->RemovingFromList();
   NS_ADDREF(*_retval = mItems[index]);
 
   InternalList().RemoveItem(index);
   mItems.RemoveElementAt(index);
 
   UpdateListIndicesFromIndex(mItems, index);
 
-  Element()->DidChangeNumberList(AttrEnum(), true);
+  Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMSVGNumberList::AppendItem(nsIDOMSVGNumber *newItem,
--- a/content/svg/content/src/DOMSVGPathSeg.cpp
+++ b/content/svg/content/src/DOMSVGPathSeg.cpp
@@ -240,19 +240,23 @@ DOMSVGPathSeg::IndexIsValid()
   NS_IMETHODIMP                                                               \
   DOMSVGPathSeg##segName::Set##propName(type a##propName)                     \
   {                                                                           \
     if (mIsAnimValItem) {                                                     \
       return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;                        \
     }                                                                         \
     NS_ENSURE_FINITE(float(a##propName), NS_ERROR_ILLEGAL_VALUE);             \
     if (HasOwner()) {                                                         \
+      if (InternalItem()[1+index] == float(a##propName)) {                    \
+        return NS_OK;                                                         \
+      }                                                                       \
+      NS_ABORT_IF_FALSE(IsInList(), "Will/DidChangePathSegList() is wrong");  \
+      nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList();       \
       InternalItem()[1+index] = float(a##propName);                           \
-      NS_ABORT_IF_FALSE(IsInList(), "DidChangePathSegList() is wrong");       \
-      Element()->DidChangePathSegList(true);                               \
+      Element()->DidChangePathSegList(emptyOrOldValue);                       \
       if (mList->AttrIsAnimating()) {                                         \
         Element()->AnimationNeedsResample();                                  \
       }                                                                       \
     } else {                                                                  \
       mArgs[index] = float(a##propName);                                      \
     }                                                                         \
     return NS_OK;                                                             \
   }
--- a/content/svg/content/src/DOMSVGPathSegList.cpp
+++ b/content/svg/content/src/DOMSVGPathSegList.cpp
@@ -264,33 +264,34 @@ DOMSVGPathSegList::GetNumberOfItems(PRUi
 NS_IMETHODIMP
 DOMSVGPathSegList::Clear()
 {
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (Length() > 0) {
+    nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList();
     // DOM list items that are to be removed must be removed before we change
     // the internal list, otherwise they wouldn't be able to copy their
     // internal counterparts' values!
 
     InternalListWillChangeTo(SVGPathData()); // clears mItems
 
     if (!AttrIsAnimating()) {
       // The anim val list is in sync with the base val list
       DOMSVGPathSegList *animList =
         GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
       if (animList) {
         animList->InternalListWillChangeTo(SVGPathData()); // clears its mItems
       }
     }
 
     InternalList().Clear();
-    Element()->DidChangePathSegList(true);
+    Element()->DidChangePathSegList(emptyOrOldValue);
     if (AttrIsAnimating()) {
       Element()->AnimationNeedsResample();
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -366,33 +367,34 @@ DOMSVGPathSegList::InsertItemBefore(nsID
   PRUint32 argCount = SVGPathSegUtils::ArgCountForType(domItem->Type());
 
   // Ensure we have enough memory so we can avoid complex error handling below:
   if (!mItems.SetCapacity(mItems.Length() + 1) ||
       !InternalList().mData.SetCapacity(InternalList().mData.Length() + 1 + argCount)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList();
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(aIndex, internalIndex, argCount);
 
   float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS];
   domItem->ToSVGPathSegEncodedData(segAsRaw);
 
   InternalList().mData.InsertElementsAt(internalIndex, segAsRaw, 1 + argCount);
   mItems.InsertElementAt(aIndex, ItemProxy(domItem.get(), internalIndex));
 
   // This MUST come after the insertion into InternalList(), or else under the
   // insertion into InternalList() the values read from domItem would be bad
   // data from InternalList() itself!:
   domItem->InsertingIntoList(this, aIndex, IsAnimValList());
 
   UpdateListIndicesFromIndex(aIndex + 1, argCount + 1);
 
-  Element()->DidChangePathSegList(true);
+  Element()->DidChangePathSegList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   *_retval = domItem.forget().get();
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -411,16 +413,17 @@ DOMSVGPathSegList::ReplaceItem(nsIDOMSVG
   }
   if (aIndex >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
   if (domItem->HasOwner()) {
     domItem = domItem->Clone(); // must do this before changing anything!
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList();
   if (ItemAt(aIndex)) {
     // Notify any existing DOM item of removal *before* modifying the lists so
     // that the DOM item can copy the *old* value at its index:
     ItemAt(aIndex)->RemovingFromList();
   }
 
   PRUint32 internalIndex = mItems[aIndex].mInternalDataIndex;
   // We use InternalList() to get oldArgCount since we may not have a DOM
@@ -446,17 +449,17 @@ DOMSVGPathSegList::ReplaceItem(nsIDOMSVG
 
   PRUint32 delta = newArgCount - oldArgCount;
   if (delta != 0) {
     for (PRUint32 i = aIndex + 1; i < Length(); ++i) {
       mItems[i].mInternalDataIndex += delta;
     }
   }
 
-  Element()->DidChangePathSegList(true);
+  Element()->DidChangePathSegList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   NS_ADDREF(*_retval = domItem.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -469,16 +472,17 @@ DOMSVGPathSegList::RemoveItem(PRUint32 a
   }
 
   if (aIndex >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
   // We have to return the removed item, so make sure it exists:
   EnsureItemAt(aIndex);
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList();
   // Notify the DOM item of removal *before* modifying the lists so that the
   // DOM item can copy its *old* value:
   ItemAt(aIndex)->RemovingFromList();
   NS_ADDREF(*_retval = ItemAt(aIndex));
 
   PRUint32 internalIndex = mItems[aIndex].mInternalDataIndex;
   PRUint32 segType = SVGPathSegUtils::DecodeType(InternalList().mData[internalIndex]);
   PRUint32 argCount = SVGPathSegUtils::ArgCountForType(segType);
@@ -488,17 +492,17 @@ DOMSVGPathSegList::RemoveItem(PRUint32 a
   // internal value.
   MaybeRemoveItemFromAnimValListAt(aIndex, argCount);
 
   InternalList().mData.RemoveElementsAt(internalIndex, 1 + argCount);
   mItems.RemoveElementAt(aIndex);
 
   UpdateListIndicesFromIndex(aIndex, -(argCount + 1));
 
-  Element()->DidChangePathSegList(true);
+  Element()->DidChangePathSegList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMSVGPathSegList::AppendItem(nsIDOMSVGPathSeg *aNewItem,
--- a/content/svg/content/src/DOMSVGPoint.cpp
+++ b/content/svg/content/src/DOMSVGPoint.cpp
@@ -93,18 +93,22 @@ 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()) {
+    if (InternalItem().mX == aX) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
     InternalItem().mX = aX;
-    Element()->DidChangePointList(true);
+    Element()->DidChangePointList(emptyOrOldValue);
     if (mList->AttrIsAnimating()) {
       Element()->AnimationNeedsResample();
     }
     return NS_OK;
   }
   mPt.mX = aX;
   return NS_OK;
 }
@@ -124,18 +128,22 @@ 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()) {
+    if (InternalItem().mY == aY) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
     InternalItem().mY = aY;
-    Element()->DidChangePointList(true);
+    Element()->DidChangePointList(emptyOrOldValue);
     if (mList->AttrIsAnimating()) {
       Element()->AnimationNeedsResample();
     }
     return NS_OK;
   }
   mPt.mY = aY;
   return NS_OK;
 }
--- a/content/svg/content/src/DOMSVGPointList.cpp
+++ b/content/svg/content/src/DOMSVGPointList.cpp
@@ -208,33 +208,34 @@ DOMSVGPointList::GetNumberOfItems(PRUint
 NS_IMETHODIMP
 DOMSVGPointList::Clear()
 {
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (Length() > 0) {
+    nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
     // 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(true);
+    Element()->DidChangePointList(emptyOrOldValue);
     if (AttrIsAnimating()) {
       Element()->AnimationNeedsResample();
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -302,30 +303,31 @@ DOMSVGPointList::InsertItemBefore(nsIDOM
   }
 
   // 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;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(aIndex);
 
   InternalList().InsertItem(aIndex, domItem->ToSVGPoint());
   mItems.InsertElementAt(aIndex, domItem.get());
 
   // This MUST come after the insertion into InternalList(), or else under the
   // insertion into InternalList() the values read from domItem would be bad
   // data from InternalList() itself!:
   domItem->InsertingIntoList(this, aIndex, IsAnimValList());
 
   UpdateListIndicesFromIndex(mItems, aIndex + 1);
 
-  Element()->DidChangePointList(true);
+  Element()->DidChangePointList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   *_retval = domItem.forget().get();
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -344,30 +346,31 @@ DOMSVGPointList::ReplaceItem(nsIDOMSVGPo
   }
   if (aIndex >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
   if (domItem->HasOwner() || domItem->IsReadonly()) {
     domItem = domItem->Clone(); // must do this before changing anything!
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
   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 ToSVGPoint() call, otherwise that call
   // would end up reading bad data from InternalList()!
   domItem->InsertingIntoList(this, aIndex, IsAnimValList());
 
-  Element()->DidChangePointList(true);
+  Element()->DidChangePointList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   NS_ADDREF(*_retval = domItem.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -378,16 +381,17 @@ DOMSVGPointList::RemoveItem(PRUint32 aIn
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (aIndex >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
   // Now that we know we're removing, keep animVal list in sync as necessary.
   // Do this *before* touching InternalList() so the removed item can get its
   // internal value.
   MaybeRemoveItemFromAnimValListAt(aIndex);
 
   // We have to return the removed item, so make sure it exists:
   EnsureItemAt(aIndex);
 
@@ -396,17 +400,17 @@ DOMSVGPointList::RemoveItem(PRUint32 aIn
   mItems[aIndex]->RemovingFromList();
   NS_ADDREF(*_retval = mItems[aIndex]);
 
   InternalList().RemoveItem(aIndex);
   mItems.RemoveElementAt(aIndex);
 
   UpdateListIndicesFromIndex(mItems, aIndex);
 
-  Element()->DidChangePointList(true);
+  Element()->DidChangePointList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMSVGPointList::AppendItem(nsIDOMSVGPoint *aNewItem,
--- a/content/svg/content/src/DOMSVGStringList.cpp
+++ b/content/svg/content/src/DOMSVGStringList.cpp
@@ -104,19 +104,22 @@ DOMSVGStringList::GetLength(PRUint32 *aL
 {
   return GetNumberOfItems(aLength);
 }
 
 NS_IMETHODIMP
 DOMSVGStringList::Clear()
 {
   if (InternalList().IsExplicitlySet()) {
+    nsAttrValue emptyOrOldValue =
+      mElement->WillChangeStringList(mIsConditionalProcessingAttribute,
+                                     mAttrEnum);
     InternalList().Clear();
     mElement->DidChangeStringList(mIsConditionalProcessingAttribute,
-                                  mAttrEnum);
+                                  mAttrEnum, emptyOrOldValue);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMSVGStringList::Initialize(const nsAString & newItem, nsAString & _retval)
 {
   if (InternalList().IsExplicitlySet()) {
@@ -146,19 +149,23 @@ DOMSVGStringList::InsertItemBefore(const
   }
   index = NS_MIN(index, InternalList().Length());
 
   // Ensure we have enough memory so we can avoid complex error handling below:
   if (!InternalList().SetCapacity(InternalList().Length() + 1)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  nsAttrValue emptyOrOldValue =
+    mElement->WillChangeStringList(mIsConditionalProcessingAttribute,
+                                   mAttrEnum);
   InternalList().InsertItem(index, newItem);
 
-  mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum);
+  mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum,
+                                emptyOrOldValue);
   _retval = newItem;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMSVGStringList::ReplaceItem(const nsAString & newItem,
                               PRUint32 index,
                               nsAString & _retval)
@@ -166,33 +173,41 @@ DOMSVGStringList::ReplaceItem(const nsAS
   if (newItem.IsEmpty()) { // takes care of DOMStringIsNull too
     return NS_ERROR_DOM_SVG_INVALID_VALUE_ERR;
   }
   if (index >= InternalList().Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   _retval = InternalList()[index];
+  nsAttrValue emptyOrOldValue =
+    mElement->WillChangeStringList(mIsConditionalProcessingAttribute,
+                                   mAttrEnum);
   InternalList().ReplaceItem(index, newItem);
 
-  mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum);
+  mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum,
+                                emptyOrOldValue);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMSVGStringList::RemoveItem(PRUint32 index,
                              nsAString & _retval)
 {
   if (index >= InternalList().Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
+  nsAttrValue emptyOrOldValue =
+    mElement->WillChangeStringList(mIsConditionalProcessingAttribute,
+                                   mAttrEnum);
   InternalList().RemoveItem(index);
 
-  mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum);
+  mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum,
+                                emptyOrOldValue);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMSVGStringList::AppendItem(const nsAString & newItem,
                              nsAString & _retval)
 {
   return InsertItemBefore(newItem, InternalList().Length(), _retval);
--- a/content/svg/content/src/DOMSVGStringList.h
+++ b/content/svg/content/src/DOMSVGStringList.h
@@ -106,18 +106,16 @@ private:
                    bool aIsConditionalProcessingAttribute, PRUint8 aAttrEnum)
     : mElement(aElement)
     , mAttrEnum(aAttrEnum)
     , mIsConditionalProcessingAttribute(aIsConditionalProcessingAttribute)
   {}
 
   ~DOMSVGStringList();
 
-  void DidChangeStringList(PRUint8 aAttrEnum, bool aDoSetAttr);
-
   SVGStringList &InternalList();
 
   // Strong ref to our element to keep it alive.
   nsRefPtr<nsSVGElement> mElement;
 
   PRUint8 mAttrEnum;
 
   bool    mIsConditionalProcessingAttribute;
--- a/content/svg/content/src/DOMSVGTests.cpp
+++ b/content/svg/content/src/DOMSVGTests.cpp
@@ -43,23 +43,28 @@
 #include "nsStyleUtil.h"
 #include "nsSVGUtils.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 
 NS_IMPL_ISUPPORTS1(DOMSVGTests, nsIDOMSVGTests)
 
-DOMSVGTests::StringListInfo DOMSVGTests::sStringListInfo[3] =
+nsIAtom** DOMSVGTests::sStringListNames[3] =
 {
-  { &nsGkAtoms::requiredFeatures, false },
-  { &nsGkAtoms::requiredExtensions, false },
-  { &nsGkAtoms::systemLanguage, true }
+  &nsGkAtoms::requiredFeatures,
+  &nsGkAtoms::requiredExtensions,
+  &nsGkAtoms::systemLanguage,
 };
 
+DOMSVGTests::DOMSVGTests()
+{
+  mStringListAttributes[LANGUAGE].SetIsCommaSeparated(true);
+}
+
 /* readonly attribute nsIDOMSVGStringList requiredFeatures; */
 NS_IMETHODIMP
 DOMSVGTests::GetRequiredFeatures(nsIDOMSVGStringList * *aRequiredFeatures)
 {
   nsCOMPtr<nsSVGElement> element = do_QueryInterface(this);
   *aRequiredFeatures = DOMSVGStringList::GetDOMWrapper(
                          &mStringListAttributes[FEATURES], element, true, FEATURES).get();
   return NS_OK;
@@ -91,18 +96,18 @@ DOMSVGTests::HasExtension(const nsAStrin
 {
   *_retval = nsSVGFeatures::HasExtension(extension);
   return NS_OK;
 }
 
 bool
 DOMSVGTests::IsConditionalProcessingAttribute(const nsIAtom* aAttribute) const
 {
-  for (PRUint32 i = 0; i < ArrayLength(sStringListInfo); i++) {
-    if (aAttribute == *sStringListInfo[i].mName) {
+  for (PRUint32 i = 0; i < ArrayLength(sStringListNames); i++) {
+    if (aAttribute == *sStringListNames[i]) {
       return true;
     }
   }
   return false;
 }
 
 PRInt32
 DOMSVGTests::GetBestLanguagePreferenceRank(const nsSubstring& aAcceptLangs) const
@@ -217,73 +222,59 @@ DOMSVGTests::PassesConditionalProcessing
   return true;
 }
 
 bool
 DOMSVGTests::ParseConditionalProcessingAttribute(nsIAtom* aAttribute,
                                                  const nsAString& aValue,
                                                  nsAttrValue& aResult)
 {
-  for (PRUint32 i = 0; i < ArrayLength(sStringListInfo); i++) {
-    if (aAttribute == *sStringListInfo[i].mName) {
-      nsresult rv = mStringListAttributes[i].SetValue(
-                      aValue, sStringListInfo[i].mIsCommaSeparated);
+  for (PRUint32 i = 0; i < ArrayLength(sStringListNames); i++) {
+    if (aAttribute == *sStringListNames[i]) {
+      nsresult rv = mStringListAttributes[i].SetValue(aValue);
       if (NS_FAILED(rv)) {
         mStringListAttributes[i].Clear();
       }
       MaybeInvalidate();
       return true;
     }
   }
   return false;
 }
 
 void
-DOMSVGTests::GetValue(PRUint8 aAttrEnum, nsAString& aValue) const
-{
-  NS_ABORT_IF_FALSE(aAttrEnum >= 0 && aAttrEnum < ArrayLength(sStringListInfo),
-                    "aAttrEnum out of range");
-  mStringListAttributes[aAttrEnum].GetValue(
-    aValue, sStringListInfo[aAttrEnum].mIsCommaSeparated);
-}
-
-void
 DOMSVGTests::UnsetAttr(const nsIAtom* aAttribute)
 {
-  for (PRUint32 i = 0; i < ArrayLength(sStringListInfo); i++) {
-    if (aAttribute == *sStringListInfo[i].mName) {
+  for (PRUint32 i = 0; i < ArrayLength(sStringListNames); i++) {
+    if (aAttribute == *sStringListNames[i]) {
       mStringListAttributes[i].Clear();
       MaybeInvalidate();
       return;
     }
   }
 }
 
-void
-DOMSVGTests::DidChangeStringList(PRUint8 aAttrEnum)
+nsIAtom*
+DOMSVGTests::GetAttrName(PRUint8 aAttrEnum) const
 {
-  NS_ASSERTION(aAttrEnum < ArrayLength(sStringListInfo), "aAttrEnum out of range");
-
-  nsCOMPtr<nsSVGElement> element = do_QueryInterface(this);
+  return *sStringListNames[aAttrEnum];
+}
 
-  nsAutoString serializedValue;
-  GetValue(aAttrEnum, serializedValue);
-
-  nsAttrValue attrValue(serializedValue);
-  element->SetParsedAttr(kNameSpaceID_None,
-                         *sStringListInfo[aAttrEnum].mName,
-                         nsnull, attrValue, true);
-
-  MaybeInvalidate();
+void
+DOMSVGTests::GetAttrValue(PRUint8 aAttrEnum, nsAttrValue& aValue) const
+{
+  NS_ABORT_IF_FALSE(aAttrEnum >= 0 && aAttrEnum < ArrayLength(sStringListNames),
+                    "aAttrEnum out of range");
+  aValue.SetTo(mStringListAttributes[aAttrEnum], nsnull);
 }
 
 void
 DOMSVGTests::MaybeInvalidate()
 {
   nsCOMPtr<nsSVGElement> element = do_QueryInterface(this);
 
   nsIContent* parent = element->GetFlattenedTreeParent();
-  
+
   if (parent &&
       parent->NodeInfo()->Equals(nsGkAtoms::svgSwitch, kNameSpaceID_SVG)) {
     static_cast<nsSVGSwitchElement*>(parent)->MaybeInvalidate();
   }
 }
--- a/content/svg/content/src/DOMSVGTests.h
+++ b/content/svg/content/src/DOMSVGTests.h
@@ -54,16 +54,18 @@ class DOMSVGTests : public nsIDOMSVGTest
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMSVGTESTS
 
   friend class mozilla::DOMSVGStringList;
   typedef mozilla::SVGStringList SVGStringList;
 
+  DOMSVGTests();
+
   /**
    * Compare the language name(s) in a systemLanguage attribute to the
    * user's language preferences, as defined in
    * http://www.w3.org/TR/SVG11/struct.html#SystemLanguageAttribute
    * We have a match if a language name in the users language preferences
    * exactly equals one of the language names or exactly equals a prefix of
    * one of the language names in the systemLanguage attribute.
    * @returns 2 * the lowest index in the aAcceptLangs that matches + 1
@@ -99,34 +101,24 @@ public:
   bool IsConditionalProcessingAttribute(const nsIAtom* aAttribute) const;
 
   bool ParseConditionalProcessingAttribute(
          nsIAtom* aAttribute,
          const nsAString& aValue,
          nsAttrValue& aResult);
 
   /**
-   * Serialises the conditional processing attribute.
-   */
-  void GetValue(PRUint8 aAttrEnum, nsAString& aValue) const;
-
-  /**
    * Unsets a conditional processing attribute.
    */
   void UnsetAttr(const nsIAtom* aAttribute);
 
-  void DidChangeStringList(PRUint8 aAttrEnum);
+  nsIAtom* GetAttrName(PRUint8 aAttrEnum) const;
+  void GetAttrValue(PRUint8 aAttrEnum, nsAttrValue &aValue) const;
 
   void MaybeInvalidate();
 
 private:
-
-  struct StringListInfo {
-    nsIAtom** mName;
-    bool      mIsCommaSeparated;
-  };
-
   enum { FEATURES, EXTENSIONS, LANGUAGE };
   SVGStringList mStringListAttributes[3];
-  static StringListInfo sStringListInfo[3];
+  static nsIAtom** sStringListNames[3];
 };
 
 #endif // MOZILLA_DOMSVGTESTS_H__
--- a/content/svg/content/src/DOMSVGTransform.cpp
+++ b/content/svg/content/src/DOMSVGTransform.cpp
@@ -168,97 +168,128 @@ DOMSVGTransform::SetMatrix(nsIDOMSVGMatr
 {
   if (mIsAnimValItem)
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
 
   nsCOMPtr<DOMSVGMatrix> domMatrix = do_QueryInterface(matrix);
   if (!domMatrix)
     return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
 
-  Transform().SetMatrix(domMatrix->Matrix());
-  NotifyElementOfChange();
-
+  SetMatrix(domMatrix->Matrix());
   return NS_OK;
 }
 
 /* void setTranslate (in float tx, in float ty); */
 NS_IMETHODIMP
 DOMSVGTransform::SetTranslate(float tx, float ty)
 {
   if (mIsAnimValItem) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
   NS_ENSURE_FINITE2(tx, ty, NS_ERROR_ILLEGAL_VALUE);
 
+  if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_TRANSLATE &&
+      Matrix().x0 == tx && Matrix().y0 == ty) {
+    return NS_OK;
+  }
+
+  nsAttrValue emptyOrOldValue = NotifyElementWillChange();
   Transform().SetTranslate(tx, ty);
-  NotifyElementOfChange();
+  NotifyElementDidChange(emptyOrOldValue);
 
   return NS_OK;
 }
 
 /* void setScale (in float sx, in float sy); */
 NS_IMETHODIMP
 DOMSVGTransform::SetScale(float sx, float sy)
 {
   if (mIsAnimValItem) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
   NS_ENSURE_FINITE2(sx, sy, NS_ERROR_ILLEGAL_VALUE);
 
+  if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SCALE &&
+      Matrix().xx == sx && Matrix().yy == sy) {
+    return NS_OK;
+  }
+
+  nsAttrValue emptyOrOldValue = NotifyElementWillChange();
   Transform().SetScale(sx, sy);
-  NotifyElementOfChange();
+  NotifyElementDidChange(emptyOrOldValue);
 
   return NS_OK;
 }
 
 /* void setRotate (in float angle, in float cx, in float cy); */
 NS_IMETHODIMP
 DOMSVGTransform::SetRotate(float angle, float cx, float cy)
 {
   if (mIsAnimValItem) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
   NS_ENSURE_FINITE3(angle, cx, cy, NS_ERROR_ILLEGAL_VALUE);
 
+  if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_ROTATE) {
+    float currentCx, currentCy;
+    Transform().GetRotationOrigin(currentCx, currentCy);
+    if (Transform().Angle() == angle && currentCx == cx && currentCy == cy) {
+      return NS_OK;
+    }
+  }
+
+  nsAttrValue emptyOrOldValue = NotifyElementWillChange();
   Transform().SetRotate(angle, cx, cy);
-  NotifyElementOfChange();
+  NotifyElementDidChange(emptyOrOldValue);
 
   return NS_OK;
 }
 
 /* void setSkewX (in float angle); */
 NS_IMETHODIMP
 DOMSVGTransform::SetSkewX(float angle)
 {
   if (mIsAnimValItem) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
   NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE);
 
+  if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SKEWX &&
+      Transform().Angle() == angle) {
+    return NS_OK;
+  }
+
+  nsAttrValue emptyOrOldValue = NotifyElementWillChange();
   nsresult rv = Transform().SetSkewX(angle);
   if (NS_FAILED(rv))
     return rv;
-  NotifyElementOfChange();
+  NotifyElementDidChange(emptyOrOldValue);
 
   return NS_OK;
 }
 
 /* void setSkewY (in float angle); */
 NS_IMETHODIMP
 DOMSVGTransform::SetSkewY(float angle)
 {
   if (mIsAnimValItem) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
   NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE);
 
+  if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SKEWY &&
+      Transform().Angle() == angle) {
+    return NS_OK;
+  }
+
+  nsAttrValue emptyOrOldValue = NotifyElementWillChange();
   nsresult rv = Transform().SetSkewY(angle);
   if (NS_FAILED(rv))
     return rv;
-  NotifyElementOfChange();
+  NotifyElementDidChange(emptyOrOldValue);
 
   return NS_OK;
 }
 
 
 //----------------------------------------------------------------------
 // List management methods:
 
@@ -319,36 +350,43 @@ DOMSVGTransform::IndexIsValid()
 //----------------------------------------------------------------------
 // Interface for DOMSVGMatrix's use
 
 void
 DOMSVGTransform::SetMatrix(const gfxMatrix& aMatrix)
 {
   NS_ABORT_IF_FALSE(!mIsAnimValItem,
       "Attempting to modify read-only transform");
+
+  if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_MATRIX &&
+      SVGTransform::MatricesEqual(Matrix(), aMatrix)) {
+    return;
+  }
+
+  nsAttrValue emptyOrOldValue = NotifyElementWillChange();
   Transform().SetMatrix(aMatrix);
-  NotifyElementOfChange();
+  NotifyElementDidChange(emptyOrOldValue);
 }
 
 void
 DOMSVGTransform::ClearMatrixTearoff(DOMSVGMatrix* aMatrix)
 {
   NS_ABORT_IF_FALSE(mMatrixTearoff == aMatrix,
       "Unexpected matrix pointer to be cleared");
   mMatrixTearoff = nsnull;
 }
 
 
 //----------------------------------------------------------------------
 // Implementation helpers
 
 void
-DOMSVGTransform::NotifyElementOfChange()
+DOMSVGTransform::NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue)
 {
   if (HasOwner()) {
-    Element()->DidChangeTransformList(true);
+    Element()->DidChangeTransformList(aEmptyOrOldValue);
     if (mList->mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
   }
 }
 
 } // namespace mozilla
--- a/content/svg/content/src/DOMSVGTransform.h
+++ b/content/svg/content/src/DOMSVGTransform.h
@@ -201,17 +201,18 @@ private:
 #endif
 
   const SVGTransform& Transform() const {
     return HasOwner() ? InternalItem() : *mTransform;
   }
   SVGTransform& Transform() {
     return HasOwner() ? InternalItem() : *mTransform;
   }
-  void NotifyElementOfChange();
+  inline nsAttrValue NotifyElementWillChange();
+  void NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue);
 
   nsRefPtr<DOMSVGTransformList> 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:MOZ_SVG_LIST_INDEX_BIT_COUNT;
   bool mIsAnimValItem:1;
@@ -230,13 +231,23 @@ private:
   // If this extra pointer member proves undesirable, it can be replaced with
   // a hashmap (nsSVGAttrTearoffTable) to map from DOMSVGTransform to
   // DOMSVGMatrix.
   DOMSVGMatrix* mMatrixTearoff;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGTransform, MOZILLA_DOMSVGTRANSFORM_IID)
 
+nsAttrValue
+DOMSVGTransform::NotifyElementWillChange()
+{
+  nsAttrValue result;
+  if (HasOwner()) {
+    result = Element()->WillChangeTransformList();
+  }
+  return result;
+}
+
 } // namespace mozilla
 
 #undef MOZ_SVG_LIST_INDEX_BIT_COUNT
 
 #endif // MOZILLA_DOMSVGTRANSFORM_H__
--- a/content/svg/content/src/DOMSVGTransformList.cpp
+++ b/content/svg/content/src/DOMSVGTransformList.cpp
@@ -179,24 +179,25 @@ DOMSVGTransformList::GetLength(PRUint32 
 NS_IMETHODIMP
 DOMSVGTransformList::Clear()
 {
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (Length() > 0) {
+    nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList();
     // Notify any existing DOM items of removal *before* truncating the lists
     // so that they can find their SVGTransform internal counterparts and copy
     // their values. This also notifies the animVal list:
     mAList->InternalBaseValListWillChangeLengthTo(0);
 
     mItems.Clear();
     InternalList().Clear();
-    Element()->DidChangeTransformList(true);
+    Element()->DidChangeTransformList(emptyOrOldValue);
     if (mAList->IsAnimating()) {
       Element()->AnimationNeedsResample();
     }
   }
   return NS_OK;
 }
 
 /* nsIDOMSVGTransform initialize (in nsIDOMSVGTransform newItem); */
@@ -267,30 +268,31 @@ DOMSVGTransformList::InsertItemBefore(ns
   }
 
   // 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;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList();
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(index);
 
   InternalList().InsertItem(index, domItem->ToSVGTransform());
   mItems.InsertElementAt(index, domItem.get());
 
   // This MUST come after the insertion into InternalList(), or else under the
   // insertion into InternalList() the values read from domItem would be bad
   // data from InternalList() itself!:
   domItem->InsertingIntoList(this, index, IsAnimValList());
 
   UpdateListIndicesFromIndex(mItems, index + 1);
 
-  Element()->DidChangeTransformList(true);
+  Element()->DidChangeTransformList(emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   *_retval = domItem.forget().get();
   return NS_OK;
 }
 
 /* nsIDOMSVGTransform replaceItem (in nsIDOMSVGTransform newItem,
@@ -311,30 +313,31 @@ DOMSVGTransformList::ReplaceItem(nsIDOMS
   }
   if (index >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
   if (domItem->HasOwner()) {
     domItem = domItem->Clone(); // must do this before changing anything!
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList();
   if (mItems[index]) {
     // Notify any existing DOM item of removal *before* modifying the lists so
     // that the DOM item can copy the *old* value at its index:
     mItems[index]->RemovingFromList();
   }
 
   InternalList()[index] = domItem->ToSVGTransform();
   mItems[index] = domItem;
 
   // This MUST come after the ToSVGPoint() call, otherwise that call
   // would end up reading bad data from InternalList()!
   domItem->InsertingIntoList(this, index, IsAnimValList());
 
-  Element()->DidChangeTransformList(true);
+  Element()->DidChangeTransformList(emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   NS_ADDREF(*_retval = domItem.get());
   return NS_OK;
 }
 
 /* nsIDOMSVGTransform removeItem (in unsigned long index); */
@@ -345,16 +348,17 @@ DOMSVGTransformList::RemoveItem(PRUint32
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (index >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList();
   // Now that we know we're removing, keep animVal list in sync as necessary.
   // Do this *before* touching InternalList() so the removed item can get its
   // internal value.
   MaybeRemoveItemFromAnimValListAt(index);
 
   // We have to return the removed item, so make sure it exists:
   EnsureItemAt(index);
 
@@ -363,17 +367,17 @@ DOMSVGTransformList::RemoveItem(PRUint32
   mItems[index]->RemovingFromList();
   NS_ADDREF(*_retval = mItems[index]);
 
   InternalList().RemoveItem(index);
   mItems.RemoveElementAt(index);
 
   UpdateListIndicesFromIndex(mItems, index);
 
-  Element()->DidChangeTransformList(true);
+  Element()->DidChangeTransformList(emptyOrOldValue);
   if (mAList->IsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   return NS_OK;
 }
 
 /* nsIDOMSVGTransform appendItem (in nsIDOMSVGTransform newItem); */
 NS_IMETHODIMP
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -141,16 +141,17 @@ CPPSRCS		= \
 		SVGTransformList.cpp \
 		SVGTransformListParser.cpp \
 		nsSVGAnimateElement.cpp \
 		nsSVGAnimateTransformElement.cpp \
 		nsSVGAnimateMotionElement.cpp \
 		nsSVGAnimationElement.cpp \
 		nsSVGMpathElement.cpp \
 		nsSVGSetElement.cpp \
+		SVGAttrValueWrapper.cpp \
 		SVGIntegerPairSMILType.cpp \
 		SVGLengthListSMILType.cpp \
 		SVGMotionSMILType.cpp \
 		SVGMotionSMILAttr.cpp \
 		SVGMotionSMILAnimationFunction.cpp \
 		SVGMotionSMILPathUtils.cpp \
 		SVGNumberListSMILType.cpp \
 		SVGNumberPairSMILType.cpp \
@@ -164,16 +165,17 @@ CPPSRCS		= \
 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 =  			\
 	nsSVGFeatures.h            \
 	nsSVGRect.h                \
+	SVGAttrValueWrapper.h      \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES += 	\
 		-I$(srcdir)/../../../xml/content/src \
 		-I$(srcdir)/../../../../dom \
 		-I$(srcdir)/../../../base/src \
--- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
+++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
@@ -245,17 +245,18 @@ SVGAnimatedPreserveAspectRatio::SetBaseV
 
   // We don't need to call DidChange* here - we're only called by
   // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
   // which takes care of notifying.
   return NS_OK;
 }
 
 void
-SVGAnimatedPreserveAspectRatio::GetBaseValueString(nsAString & aValueAsString)
+SVGAnimatedPreserveAspectRatio::GetBaseValueString(
+  nsAString& aValueAsString) const
 {
   nsAutoString tmpString;
 
   aValueAsString.Truncate();
 
   if (mBaseVal.mDefer) {
     aValueAsString.AppendLiteral("defer ");
   }
@@ -271,39 +272,49 @@ SVGAnimatedPreserveAspectRatio::GetBaseV
     aValueAsString.Append(tmpString);
   }
 }
 
 nsresult
 SVGAnimatedPreserveAspectRatio::SetBaseAlign(PRUint16 aAlign,
                                              nsSVGElement *aSVGElement)
 {
+  if (mIsBaseSet && mBaseVal.GetAlign() == aAlign) {
+    return NS_OK;
+  }
+
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio();
   nsresult rv = mBaseVal.SetAlign(aAlign);
   NS_ENSURE_SUCCESS(rv, rv);
   mIsBaseSet = true;
 
   mAnimVal.mAlign = mBaseVal.mAlign;
-  aSVGElement->DidChangePreserveAspectRatio(true);
+  aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue);
   if (mIsAnimated) {
     aSVGElement->AnimationNeedsResample();
   }
   
   return NS_OK;
 }
 
 nsresult
 SVGAnimatedPreserveAspectRatio::SetBaseMeetOrSlice(PRUint16 aMeetOrSlice,
                                                    nsSVGElement *aSVGElement)
 {
+  if (mIsBaseSet && mBaseVal.GetMeetOrSlice() == aMeetOrSlice) {
+    return NS_OK;
+  }
+
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio();
   nsresult rv = mBaseVal.SetMeetOrSlice(aMeetOrSlice);
   NS_ENSURE_SUCCESS(rv, rv);
   mIsBaseSet = true;
 
   mAnimVal.mMeetOrSlice = mBaseVal.mMeetOrSlice;
-  aSVGElement->DidChangePreserveAspectRatio(true);
+  aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue);
   if (mIsAnimated) {
     aSVGElement->AnimationNeedsResample();
   }
   
   return NS_OK;
 }
 
 void
--- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
+++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
@@ -113,17 +113,17 @@ public:
     mBaseVal.mDefer = false;
     mAnimVal = mBaseVal;
     mIsAnimated = false;
     mIsBaseSet = false;
   }
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement);
-  void GetBaseValueString(nsAString& aValue);
+  void GetBaseValueString(nsAString& aValue) const;
 
   nsresult SetBaseAlign(PRUint16 aAlign, nsSVGElement *aSVGElement);
   nsresult SetBaseMeetOrSlice(PRUint16 aMeetOrSlice, nsSVGElement *aSVGElement);
   void SetAnimValue(PRUint64 aPackedValue, nsSVGElement *aSVGElement);
 
   const SVGPreserveAspectRatio &GetBaseValue() const
     { return mBaseVal; }
   const SVGPreserveAspectRatio &GetAnimValue() const
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGAttrValueWrapper.cpp
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** 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 Mozilla Japan.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "SVGAttrValueWrapper.h"
+#include "nsSVGAngle.h"
+#include "nsSVGIntegerPair.h"
+#include "nsSVGLength2.h"
+#include "nsSVGNumberPair.h"
+#include "nsSVGViewBox.h"
+#include "SVGAnimatedPreserveAspectRatio.h"
+#include "SVGLengthList.h"
+#include "SVGNumberList.h"
+#include "SVGPathData.h"
+#include "SVGPointList.h"
+#include "SVGStringList.h"
+#include "SVGTransformList.h"
+
+using namespace mozilla;
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGAngle* aAngle, nsAString& aResult)
+{
+  aAngle->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGIntegerPair* aIntegerPair,
+                              nsAString& aResult)
+{
+  aIntegerPair->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGLength2* aLength, nsAString& aResult)
+{
+  aLength->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGLengthList* aLengthList,
+                              nsAString& aResult)
+{
+  aLengthList->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGNumberList* aNumberList,
+                              nsAString& aResult)
+{
+  aNumberList->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGNumberPair* aNumberPair,
+                              nsAString& aResult)
+{
+  aNumberPair->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGPathData* aPathData, nsAString& aResult)
+{
+  aPathData->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGPointList* aPointList,
+                              nsAString& aResult)
+{
+  aPointList->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(
+  const SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio,
+  nsAString& aResult)
+{
+  aPreserveAspectRatio->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGStringList* aStringList,
+                              nsAString& aResult)
+{
+  aStringList->GetValue(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGTransformList* aTransformList,
+                              nsAString& aResult)
+{
+  aTransformList->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGViewBox* aViewBox, nsAString& aResult)
+{
+  aViewBox->GetBaseValueString(aResult);
+}
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGAttrValueWrapper.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** 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 Mozilla Japan.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef MOZILLA_SVGATTRVALUEWRAPPER_H__
+#define MOZILLA_SVGATTRVALUEWRAPPER_H__
+
+/**
+ * Utility wrapper for handling SVG types used inside nsAttrValue so that these
+ * types don't need to be exported outside the SVG module.
+ */
+
+#include "nsString.h"
+
+class nsSVGAngle;
+class nsSVGIntegerPair;
+class nsSVGLength2;
+class nsSVGNumberPair;
+class nsSVGViewBox;
+
+namespace mozilla {
+class SVGLengthList;
+class SVGNumberList;
+class SVGPathData;
+class SVGPointList;
+class SVGAnimatedPreserveAspectRatio;
+class SVGStringList;
+class SVGTransformList;
+}
+
+namespace mozilla {
+
+class SVGAttrValueWrapper
+{
+public:
+  static void ToString(const nsSVGAngle* aAngle, nsAString& aResult);
+  static void ToString(const nsSVGIntegerPair* aIntegerPair,
+                       nsAString& aResult);
+  static void ToString(const nsSVGLength2* aLength, nsAString& aResult);
+  static void ToString(const mozilla::SVGLengthList* aLengthList,
+                       nsAString& aResult);
+  static void ToString(const mozilla::SVGNumberList* aNumberList,
+                       nsAString& aResult);
+  static void ToString(const nsSVGNumberPair* aNumberPair, nsAString& aResult);
+  static void ToString(const mozilla::SVGPathData* aPathData,
+                       nsAString& aResult);
+  static void ToString(const mozilla::SVGPointList* aPointList,
+                       nsAString& aResult);
+  static void ToString(
+    const mozilla::SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio,
+    nsAString& aResult);
+  static void ToString(const mozilla::SVGStringList* aStringList,
+                       nsAString& aResult);
+  static void ToString(const mozilla::SVGTransformList* aTransformList,
+                       nsAString& aResult);
+  static void ToString(const nsSVGViewBox* aViewBox, nsAString& aResult);
+};
+
+} /* namespace mozilla */
+
+#endif // MOZILLA_SVGATTRVALUEWRAPPER_H__
--- a/content/svg/content/src/SVGStringList.cpp
+++ b/content/svg/content/src/SVGStringList.cpp
@@ -57,37 +57,37 @@ SVGStringList::CopyFrom(const SVGStringL
     return NS_ERROR_OUT_OF_MEMORY;
   }
   mStrings = rhs.mStrings;
   mIsSet = true;
   return NS_OK;
 }
 
 void
-SVGStringList::GetValue(nsAString& aValue, bool aIsCommaSeparated) const
+SVGStringList::GetValue(nsAString& aValue) const
 {
   aValue.Truncate();
   PRUint32 last = mStrings.Length() - 1;
   for (PRUint32 i = 0; i < mStrings.Length(); ++i) {
     aValue.Append(mStrings[i]);
     if (i != last) {
-      if (aIsCommaSeparated) {
+      if (mIsCommaSeparated) {
         aValue.Append(',');
       }
       aValue.Append(' ');
     }
   }
 }
 
 nsresult
-SVGStringList::SetValue(const nsAString& aValue, bool aIsCommaSeparated)
+SVGStringList::SetValue(const nsAString& aValue)
 {
   SVGStringList temp;
 
-  if (aIsCommaSeparated) {
+  if (mIsCommaSeparated) {
     nsCharSeparatedTokenizerTemplate<IsSVGWhitespace>
       tokenizer(aValue, ',');
 
     while (tokenizer.hasMoreTokens()) {
       if (!temp.AppendItem(tokenizer.nextToken())) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
     }
--- a/content/svg/content/src/SVGStringList.h
+++ b/content/svg/content/src/SVGStringList.h
@@ -49,28 +49,31 @@ namespace mozilla {
  * The DOM wrapper class for this class is DOMSVGStringList.
  */
 class SVGStringList
 {
   friend class DOMSVGStringList;
 
 public:
 
-  SVGStringList() : mIsSet(false) {}
+  SVGStringList() : mIsSet(false), mIsCommaSeparated(false) {}
   ~SVGStringList(){}
 
-  nsresult SetValue(const nsAString& aValue, bool aIsCommaSeparated);
+  void SetIsCommaSeparated(bool aIsCommaSeparated) {
+    mIsCommaSeparated = aIsCommaSeparated;
+  }
+  nsresult SetValue(const nsAString& aValue);
 
   void Clear() {
     mStrings.Clear();
     mIsSet = false;
   }
 
   /// This may return an incomplete string on OOM, but that's acceptable.
-  void GetValue(nsAString& aValue, bool aIsCommaSeparated) const;
+  void GetValue(nsAString& aValue) const;
 
   bool IsEmpty() const {
     return mStrings.IsEmpty();
   }
 
   PRUint32 Length() const {
     return mStrings.Length();
   }
@@ -86,19 +89,18 @@ public:
   bool SetCapacity(PRUint32 size) {
     return mStrings.SetCapacity(size);
   }
 
   void Compact() {
     mStrings.Compact();
   }
 
-  // Returns true if the animated value of this stringlist has been explicitly
-  // set by taking on the base value which has been explicitly set by markup
-  // or a DOM call, false otherwise.
+  // Returns true if the value of this stringlist has been explicitly
+  // set by markup or a DOM call, false otherwise.
   bool IsExplicitlySet() const
     { return mIsSet; }
 
   // 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
   // SVGAnimatedStringList and having that class act as an intermediary so it
@@ -163,13 +165,14 @@ private:
 
 protected:
 
   /* See SVGLengthList for the rationale for using nsTArray<float> instead
    * of nsTArray<float, 1>.
    */
   nsTArray<nsString> mStrings;
   bool mIsSet;
+  bool mIsCommaSeparated;
 };
 
 } // namespace mozilla
 
 #endif // MOZILLA_SVGSTRINGLIST_H__
--- a/content/svg/content/src/SVGTransform.h
+++ b/content/svg/content/src/SVGTransform.h
@@ -92,27 +92,27 @@ public:
   const gfxMatrix& Matrix() const { return mMatrix; }
   void SetMatrix(const gfxMatrix& aMatrix);
   void SetTranslate(float aTx, float aTy);
   void SetScale(float aSx, float aSy);
   void SetRotate(float aAngle, float aCx, float aCy);
   nsresult SetSkewX(float aAngle);
   nsresult SetSkewY(float aAngle);
 
-protected:
   static bool MatricesEqual(const gfxMatrix& a, const gfxMatrix& b)
   {
     return a.xx == b.xx &&
            a.yx == b.yx &&
            a.xy == b.xy &&
            a.yy == b.yy &&
            a.x0 == b.x0 &&
            a.y0 == b.y0;
   }
 
+protected:
   gfxMatrix mMatrix;
   float mAngle, mOriginX, mOriginY;
   PRUint16 mType;
 };
 
 /*
  * A slightly more light-weight version of SVGTransform for SMIL animation.
  *
--- a/content/svg/content/src/nsSVGAngle.cpp
+++ b/content/svg/content/src/nsSVGAngle.cpp
@@ -67,17 +67,17 @@ public:
   NS_IMETHOD GetUnitType(PRUint16* aResult)
     { *aResult = mVal.mBaseValUnit; return NS_OK; }
 
   NS_IMETHOD GetValue(float* aResult)
     { *aResult = mVal.GetBaseValue(); return NS_OK; }
   NS_IMETHOD SetValue(float aValue)
     {
       NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
-      mVal.SetBaseValue(aValue, nsnull);
+      mVal.SetBaseValue(aValue, nsnull, true);
       return NS_OK;
     }
 
   NS_IMETHOD GetValueInSpecifiedUnits(float* aResult)
     { *aResult = mVal.mBaseVal; return NS_OK; }
   NS_IMETHOD SetValueInSpecifiedUnits(float aValue)
     {
       NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
@@ -253,60 +253,82 @@ nsSVGAngle::GetDegreesPerUnit(PRUint8 aU
     return 0;
   }
 }
 
 void
 nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue,
                                          nsSVGElement *aSVGElement)
 {
+  if (mBaseVal == aValue) {
+    return;
+  }
+
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
   mBaseVal = aValue;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeAngle(mAttrEnum, true);
+  aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
 }
 
 nsresult
 nsSVGAngle::ConvertToSpecifiedUnits(PRUint16 unitType,
                                     nsSVGElement *aSVGElement)
 {
   if (!IsValidUnitType(unitType))
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
+  if (mBaseValUnit == PRUint8(unitType))
+    return NS_OK;
+
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
+
   float valueInUserUnits = mBaseVal * GetDegreesPerUnit(mBaseValUnit);
   mBaseValUnit = PRUint8(unitType);
-  SetBaseValue(valueInUserUnits, aSVGElement);
+  // Setting aDoSetAttr to false here will ensure we don't call
+  // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
+  SetBaseValue(valueInUserUnits, aSVGElement, false);
+
+  aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
+
   return NS_OK;
 }
 
 nsresult
 nsSVGAngle::NewValueSpecifiedUnits(PRUint16 unitType,
                                    float valueInSpecifiedUnits,
                                    nsSVGElement *aSVGElement)
 {
   NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
 
   if (!IsValidUnitType(unitType))
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
+  if (mBaseVal == valueInSpecifiedUnits && mBaseValUnit == PRUint8(unitType))
+    return NS_OK;
+
+  nsAttrValue emptyOrOldValue;
+  if (aSVGElement) {
+    emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
+  }
   mBaseVal = valueInSpecifiedUnits;
   mBaseValUnit = PRUint8(unitType);
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
     mAnimValUnit = mBaseValUnit;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
   if (aSVGElement) {
-    aSVGElement->DidChangeAngle(mAttrEnum, true);
+    aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
   }
   return NS_OK;
 }
 
 nsresult
 nsSVGAngle::ToDOMBaseVal(nsIDOMSVGAngle **aResult, nsSVGElement *aSVGElement)
 {
   *aResult = new DOMBaseVal(this, aSVGElement);
@@ -337,57 +359,73 @@ nsSVGAngle::SetBaseValueString(const nsA
 {
   float value = 0;
   PRUint16 unitType = 0;
   
   nsresult rv = GetValueFromString(aValueAsString, &value, &unitType);
   if (NS_FAILED(rv)) {
     return rv;
   }
+  if (mBaseVal == value && mBaseValUnit == PRUint8(unitType)) {
+    return NS_OK;
+  }
 
+  nsAttrValue emptyOrOldValue;
+  if (aDoSetAttr) {
+    emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
+  }
   mBaseVal = value;
   mBaseValUnit = PRUint8(unitType);
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
     mAnimValUnit = mBaseValUnit;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
 
-  // We don't need to call DidChange* here - we're only called by
-  // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
-  // which takes care of notifying.
+  if (aDoSetAttr) {
+    aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
+  }
   return NS_OK;
 }
 
 void
-nsSVGAngle::GetBaseValueString(nsAString & aValueAsString)
+nsSVGAngle::GetBaseValueString(nsAString & aValueAsString) const
 {
   GetValueString(aValueAsString, mBaseVal, mBaseValUnit);
 }
 
 void
-nsSVGAngle::GetAnimValueString(nsAString & aValueAsString)
+nsSVGAngle::GetAnimValueString(nsAString & aValueAsString) const
 {
   GetValueString(aValueAsString, mAnimVal, mAnimValUnit);
 }
 
 void
-nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement)
+nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement,
+                         bool aDoSetAttr)
 {
+  if (mBaseVal == aValue * GetDegreesPerUnit(mBaseValUnit)) {
+    return;
+  }
+  nsAttrValue emptyOrOldValue;
+  if (aSVGElement && aDoSetAttr) {
+    emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
+  }
+
   mBaseVal = aValue / GetDegreesPerUnit(mBaseValUnit);
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  if (aSVGElement) {
-    aSVGElement->DidChangeAngle(mAttrEnum, true);
+  if (aSVGElement && aDoSetAttr) {
+    aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
   }
 }
 
 void
 nsSVGAngle::SetAnimValue(float aValue, PRUint8 aUnit, nsSVGElement *aSVGElement)
 {
   mAnimVal = aValue;
   mAnimValUnit = aUnit;
--- a/content/svg/content/src/nsSVGAngle.h
+++ b/content/svg/content/src/nsSVGAngle.h
@@ -62,25 +62,25 @@ public:
     mAnimValUnit = mBaseValUnit = aUnitType;
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
   }
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement,
                               bool aDoSetAttr);
-  void GetBaseValueString(nsAString& aValue);
-  void GetAnimValueString(nsAString& aValue);
+  void GetBaseValueString(nsAString& aValue) const;
+  void GetAnimValueString(nsAString& aValue) const;
 
   float GetBaseValue() const
     { return mBaseVal * GetDegreesPerUnit(mBaseValUnit); }
   float GetAnimValue() const
     { return mAnimVal * GetDegreesPerUnit(mAnimValUnit); }
 
-  void SetBaseValue(float aValue, nsSVGElement *aSVGElement);
+  void SetBaseValue(float aValue, nsSVGElement *aSVGElement, bool aDoSetAttr);
   void SetAnimValue(float aValue, PRUint8 aUnit, nsSVGElement *aSVGElement);
 
   PRUint8 GetBaseValueUnit() const { return mBaseValUnit; }
   PRUint8 GetAnimValueUnit() const { return mAnimValUnit; }
   float GetBaseValInSpecifiedUnits() const { return mBaseVal; }
   float GetAnimValInSpecifiedUnits() const { return mAnimVal; }
 
   static nsresult ToDOMSVGAngle(nsIDOMSVGAngle **aResult);
@@ -120,17 +120,17 @@ public:
     nsRefPtr<nsSVGElement> mSVGElement;
     
     NS_IMETHOD GetUnitType(PRUint16* aResult)
       { *aResult = mVal->mBaseValUnit; return NS_OK; }
 
     NS_IMETHOD GetValue(float* aResult)
       { *aResult = mVal->GetBaseValue(); return NS_OK; }
     NS_IMETHOD SetValue(float aValue)
-      { mVal->SetBaseValue(aValue, mSVGElement); return NS_OK; }
+      { mVal->SetBaseValue(aValue, mSVGElement, true); return NS_OK; }
 
     NS_IMETHOD GetValueInSpecifiedUnits(float* aResult)
       { *aResult = mVal->mBaseVal; return NS_OK; }
     NS_IMETHOD SetValueInSpecifiedUnits(float aValue)
       { mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement);
         return NS_OK; }
 
     NS_IMETHOD SetValueAsString(const nsAString& aValue)
--- a/content/svg/content/src/nsSVGBoolean.cpp
+++ b/content/svg/content/src/nsSVGBoolean.cpp
@@ -65,23 +65,36 @@ GetValueFromString(const nsAString &aVal
   }
   if (aValueAsString.EqualsLiteral("false")) {
     *aValue = false;
     return NS_OK;
   }
   return NS_ERROR_DOM_SYNTAX_ERR;
 }
 
+static nsresult
+GetValueFromAtom(const nsIAtom* aValueAsAtom, bool *aValue)
+{
+  if (aValueAsAtom == nsGkAtoms::_true) {
+    *aValue = true;
+    return NS_OK;
+  }
+  if (aValueAsAtom == nsGkAtoms::_false) {
+    *aValue = false;
+    return NS_OK;
+  }
+  return NS_ERROR_DOM_SYNTAX_ERR;
+}
+
 nsresult
-nsSVGBoolean::SetBaseValueString(const nsAString &aValueAsString,
-                                 nsSVGElement *aSVGElement)
+nsSVGBoolean::SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement)
 {
   bool val;
 
-  nsresult rv = GetValueFromString(aValueAsString, &val);
+  nsresult rv = GetValueFromAtom(aValue, &val);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   mBaseVal = val;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
@@ -90,40 +103,38 @@ nsSVGBoolean::SetBaseValueString(const n
   }
 
   // We don't need to call DidChange* here - we're only called by
   // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
   // which takes care of notifying.
   return NS_OK;
 }
 
-void
-nsSVGBoolean::GetBaseValueString(nsAString & aValueAsString)
+nsIAtom*
+nsSVGBoolean::GetBaseValueAtom() const
 {
-  aValueAsString.Assign(mBaseVal
-                        ? NS_LITERAL_STRING("true")
-                        : NS_LITERAL_STRING("false"));
+  return mBaseVal ? nsGkAtoms::_true : nsGkAtoms::_false;
 }
 
 void
-nsSVGBoolean::SetBaseValue(bool aValue,
-                           nsSVGElement *aSVGElement)
+nsSVGBoolean::SetBaseValue(bool aValue, nsSVGElement *aSVGElement)
 {
   NS_PRECONDITION(aValue == true || aValue == false, "Boolean out of range");
 
-  if (aValue != mBaseVal) {
-    mBaseVal = aValue;
-    if (!mIsAnimated) {
-      mAnimVal = mBaseVal;
-    }
-    else {
-      aSVGElement->AnimationNeedsResample();
-    }
-    aSVGElement->DidChangeBoolean(mAttrEnum, true);
+  if (aValue == mBaseVal) {
+    return;
   }
+
+  mBaseVal = aValue;
+  if (!mIsAnimated) {
+    mAnimVal = mBaseVal;
+  } else {
+    aSVGElement->AnimationNeedsResample();
+  }
+  aSVGElement->DidChangeBoolean(mAttrEnum);
 }
 
 void
 nsSVGBoolean::SetAnimValue(bool aValue, nsSVGElement *aSVGElement)
 {
   mAnimVal = aValue;
   mIsAnimated = true;
   aSVGElement->DidAnimateBoolean(mAttrEnum);
--- a/content/svg/content/src/nsSVGBoolean.h
+++ b/content/svg/content/src/nsSVGBoolean.h
@@ -53,19 +53,18 @@ class nsSVGBoolean
 
 public:
   void Init(PRUint8 aAttrEnum = 0xff, bool aValue = false) {
     mAnimVal = mBaseVal = aValue;
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
   }
 
-  nsresult SetBaseValueString(const nsAString& aValue,
-                              nsSVGElement *aSVGElement);
-  void GetBaseValueString(nsAString& aValue);
+  nsresult SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement);
+  nsIAtom* GetBaseValueAtom() const;
 
   void SetBaseValue(bool aValue, nsSVGElement *aSVGElement);
   bool GetBaseValue() const
     { return mBaseVal; }
 
   void SetAnimValue(bool aValue, nsSVGElement *aSVGElement);
   bool GetAnimValue() const
     { return mAnimVal; }
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -84,16 +84,17 @@
 #include "DOMSVGTests.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsSVGRect.h"
 #include "nsIFrame.h"
 #include "prdtoa.h"
 #include <stdarg.h>
 #include "nsSMILMappedAttribute.h"
 #include "SVGMotionSMILAttr.h"
+#include "nsAttrValueOrString.h"
 
 using namespace mozilla;
 
 // This is needed to ensure correct handling of calls to the
 // vararg-list methods in this file:
 //   nsSVGElement::GetAnimated{Length,Number,Integer}Values
 // See bug 547964 for details:
 PR_STATIC_ASSERT(sizeof(void*) == sizeof(nsnull));
@@ -257,16 +258,25 @@ nsSVGElement::BindToTree(nsIDocument* aD
 
   return NS_OK;
 }
 
 nsresult
 nsSVGElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                            const nsAttrValue* aValue, bool aNotify)
 {
+  // We don't currently use nsMappedAttributes within SVG. If this changes, we
+  // need to be very careful because some nsAttrValues used by SVG point to
+  // member data of SVG elements and if an nsAttrValue outlives the SVG element
+  // whose data it points to (by virtue of being stored in
+  // mAttrsAndChildren->mMappedAttributes, meaning it's shared between
+  // elements), the pointer will dangle. See bug 724680.
+  NS_ABORT_IF_FALSE(!mAttrsAndChildren.HasMappedAttrs(),
+    "Unexpected use of nsMappedAttributes within SVG");
+
   // If this is an svg presentation attribute we need to map it into
   // the content stylerule.
   // XXX For some reason incremental mapping doesn't work, so for now
   // just delete the style rule and lazily reconstruct it in
   // GetContentStyleRule()
   if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) {
     mContentStyleRule = nsnull;
   }
@@ -285,191 +295,230 @@ nsSVGElement::AfterSetAttr(PRInt32 aName
 bool
 nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
                              nsIAtom* aAttribute,
                              const nsAString& aValue,
                              nsAttrValue& aResult)
 {
   nsresult rv = NS_OK;
   bool foundMatch = false;
+  bool didSetResult = false;
+
   if (aNamespaceID == kNameSpaceID_None) {
 
     // Check for nsSVGLength2 attribute
     LengthAttributesInfo lengthInfo = GetLengthInfo();
 
     PRUint32 i;
     for (i = 0; i < lengthInfo.mLengthCount; i++) {
       if (aAttribute == *lengthInfo.mLengthInfo[i].mName) {
         rv = lengthInfo.mLengths[i].SetBaseValueString(aValue, this, false);
         if (NS_FAILED(rv)) {
           lengthInfo.Reset(i);
+        } else {
+          aResult.SetTo(lengthInfo.mLengths[i], &aValue);
+          didSetResult = true;
         }
         foundMatch = true;
         break;
       }
     }
 
     if (!foundMatch) {
       // Check for SVGAnimatedLengthList attribute
       LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
       for (i = 0; i < lengthListInfo.mLengthListCount; i++) {
         if (aAttribute == *lengthListInfo.mLengthListInfo[i].mName) {
           rv = lengthListInfo.mLengthLists[i].SetBaseValueString(aValue);
           if (NS_FAILED(rv)) {
             lengthListInfo.Reset(i);
+          } else {
+            aResult.SetTo(lengthListInfo.mLengthLists[i].GetBaseValue(),
+                          &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for SVGAnimatedNumberList attribute
       NumberListAttributesInfo numberListInfo = GetNumberListInfo();
       for (i = 0; i < numberListInfo.mNumberListCount; i++) {
         if (aAttribute == *numberListInfo.mNumberListInfo[i].mName) {
           rv = numberListInfo.mNumberLists[i].SetBaseValueString(aValue);
           if (NS_FAILED(rv)) {
             numberListInfo.Reset(i);
+          } else {
+            aResult.SetTo(numberListInfo.mNumberLists[i].GetBaseValue(),
+                          &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for SVGAnimatedPointList attribute
       if (GetPointListAttrName() == aAttribute) {
         SVGAnimatedPointList* pointList = GetAnimatedPointList();
         if (pointList) {
-          rv = pointList->SetBaseValueString(aValue);
-          if (NS_FAILED(rv)) {
-            // The spec says we parse everything up to the failure, so we don't
-            // call pointList->ClearBaseValue()
-          }
+          pointList->SetBaseValueString(aValue);
+          // The spec says we parse everything up to the failure, so we DON'T
+          // need to check the result of SetBaseValueString or call
+          // pointList->ClearBaseValue() if it fails
+          aResult.SetTo(pointList->GetBaseValue(), &aValue);
+          didSetResult = true;
           foundMatch = true;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for SVGAnimatedPathSegList attribute
       if (GetPathDataAttrName() == aAttribute) {
         SVGAnimatedPathSegList* segList = GetAnimPathSegList();
         if (segList) {
-          rv = segList->SetBaseValueString(aValue);
-          if (NS_FAILED(rv)) {
-            // The spec says we parse everything up to the failure, so we don't
-            // call segList->ClearBaseValue()
-          }
+          segList->SetBaseValueString(aValue);
+          // The spec says we parse everything up to the failure, so we DON'T
+          // need to check the result of SetBaseValueString or call
+          // segList->ClearBaseValue() if it fails
+          aResult.SetTo(segList->GetBaseValue(), &aValue);
+          didSetResult = true;
           foundMatch = true;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGNumber2 attribute
       NumberAttributesInfo numberInfo = GetNumberInfo();
       for (i = 0; i < numberInfo.mNumberCount; i++) {
         if (aAttribute == *numberInfo.mNumberInfo[i].mName) {
           rv = numberInfo.mNumbers[i].SetBaseValueString(aValue, this);
           if (NS_FAILED(rv)) {
             numberInfo.Reset(i);
+          } else {
+            aResult.SetTo(numberInfo.mNumbers[i].GetBaseValue(), &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGNumberPair attribute
       NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
       for (i = 0; i < numberPairInfo.mNumberPairCount; i++) {
         if (aAttribute == *numberPairInfo.mNumberPairInfo[i].mName) {
           rv = numberPairInfo.mNumberPairs[i].SetBaseValueString(aValue, this);
           if (NS_FAILED(rv)) {
             numberPairInfo.Reset(i);
+          } else {
+            aResult.SetTo(numberPairInfo.mNumberPairs[i], &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGInteger attribute
       IntegerAttributesInfo integerInfo = GetIntegerInfo();
       for (i = 0; i < integerInfo.mIntegerCount; i++) {
         if (aAttribute == *integerInfo.mIntegerInfo[i].mName) {
           rv = integerInfo.mIntegers[i].SetBaseValueString(aValue, this);
           if (NS_FAILED(rv)) {
             integerInfo.Reset(i);
+          } else {
+            aResult.SetTo(integerInfo.mIntegers[i].GetBaseValue(), &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGIntegerPair attribute
       IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
       for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) {
         if (aAttribute == *integerPairInfo.mIntegerPairInfo[i].mName) {
-          rv = integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this);
+          rv =
+            integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this);
           if (NS_FAILED(rv)) {
             integerPairInfo.Reset(i);
+          } else {
+            aResult.SetTo(integerPairInfo.mIntegerPairs[i], &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGAngle attribute
       AngleAttributesInfo angleInfo = GetAngleInfo();
       for (i = 0; i < angleInfo.mAngleCount; i++) {
         if (aAttribute == *angleInfo.mAngleInfo[i].mName) {
           rv = angleInfo.mAngles[i].SetBaseValueString(aValue, this, false);
           if (NS_FAILED(rv)) {
             angleInfo.Reset(i);
+          } else {
+            aResult.SetTo(angleInfo.mAngles[i], &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGBoolean attribute
       BooleanAttributesInfo booleanInfo = GetBooleanInfo();
       for (i = 0; i < booleanInfo.mBooleanCount; i++) {
         if (aAttribute == *booleanInfo.mBooleanInfo[i].mName) {
-          rv = booleanInfo.mBooleans[i].SetBaseValueString(aValue, this);
+          nsCOMPtr<nsIAtom> valAtom = do_GetAtom(aValue);
+          rv = booleanInfo.mBooleans[i].SetBaseValueAtom(valAtom, this);
           if (NS_FAILED(rv)) {
             booleanInfo.Reset(i);
+          } else {
+            aResult.SetTo(valAtom);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGEnum attribute
       EnumAttributesInfo enumInfo = GetEnumInfo();
       for (i = 0; i < enumInfo.mEnumCount; i++) {
         if (aAttribute == *enumInfo.mEnumInfo[i].mName) {
-          rv = enumInfo.mEnums[i].SetBaseValueString(aValue, this);
+          nsCOMPtr<nsIAtom> valAtom = do_GetAtom(aValue);
+          rv = enumInfo.mEnums[i].SetBaseValueAtom(valAtom, this);
           if (NS_FAILED(rv)) {
             enumInfo.Reset(i);
+          } else {
+            aResult.SetTo(valAtom);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
@@ -481,55 +530,67 @@ nsSVGElement::ParseAttribute(PRInt32 aNa
       }
     }
 
     if (!foundMatch) {
       // Check for StringList attribute
       StringListAttributesInfo stringListInfo = GetStringListInfo();
       for (i = 0; i < stringListInfo.mStringListCount; i++) {
         if (aAttribute == *stringListInfo.mStringListInfo[i].mName) {
-          rv = stringListInfo.mStringLists[i].SetValue(aValue, false);
+          rv = stringListInfo.mStringLists[i].SetValue(aValue);
           if (NS_FAILED(rv)) {
             stringListInfo.Reset(i);
+          } else {
+            aResult.SetTo(stringListInfo.mStringLists[i], &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
           break;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for nsSVGViewBox attribute
       if (aAttribute == nsGkAtoms::viewBox) {
         nsSVGViewBox* viewBox = GetViewBox();
         if (viewBox) {
           rv = viewBox->SetBaseValueString(aValue, this);
           if (NS_FAILED(rv)) {
             viewBox->Init();
+          } else {
+            aResult.SetTo(*viewBox, &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
         }
       // Check for SVGAnimatedPreserveAspectRatio attribute
       } else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
         SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
           GetPreserveAspectRatio();
         if (preserveAspectRatio) {
           rv = preserveAspectRatio->SetBaseValueString(aValue, this);
           if (NS_FAILED(rv)) {
             preserveAspectRatio->Init();
+          } else {
+            aResult.SetTo(*preserveAspectRatio, &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
         }
       // Check for SVGAnimatedTransformList attribute
       } else if (GetTransformListAttrName() == aAttribute) {
         SVGAnimatedTransformList *transformList = GetAnimatedTransformList();
         if (transformList) {
           rv = transformList->SetBaseValueString(aValue);
           if (NS_FAILED(rv)) {
             transformList->ClearBaseValue();
+          } else {
+            aResult.SetTo(transformList->GetBaseValue(), &aValue);
+            didSetResult = true;
           }
           foundMatch = true;
         }
       }
     }
   }
 
   if (!foundMatch) {
@@ -545,17 +606,19 @@ nsSVGElement::ParseAttribute(PRInt32 aNa
     }
   }
 
   if (foundMatch) {
     if (NS_FAILED(rv)) {
       ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
       return false;
     }
-    aResult.SetTo(aValue);
+    if (!didSetResult) {
+      aResult.SetTo(aValue);
+    }
     return true;
   }
 
   return nsSVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                           aResult);
 }
 
 void
@@ -579,198 +642,195 @@ nsSVGElement::UnsetAttrInternal(PRInt32 
       return;
     }
     
     // Check if this is a length attribute going away
     LengthAttributesInfo lenInfo = GetLengthInfo();
 
     for (PRUint32 i = 0; i < lenInfo.mLengthCount; i++) {
       if (aName == *lenInfo.mLengthInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         lenInfo.Reset(i);
-        DidChangeLength(i, false);
         return;
       }
     }
 
     // Check if this is a length list attribute going away
     LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
 
     for (PRUint32 i = 0; i < lengthListInfo.mLengthListCount; i++) {
       if (aName == *lengthListInfo.mLengthListInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         lengthListInfo.Reset(i);
-        DidChangeLengthList(i, false);
         return;
       }
     }
 
     // Check if this is a number list attribute going away
     NumberListAttributesInfo numberListInfo = GetNumberListInfo();
 
     for (PRUint32 i = 0; i < numberListInfo.mNumberListCount; i++) {
       if (aName == *numberListInfo.mNumberListInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         numberListInfo.Reset(i);
-        DidChangeNumberList(i, false);
         return;
       }
     }
 
     // Check if this is a point list attribute going away
     if (GetPointListAttrName() == aName) {
       SVGAnimatedPointList *pointList = GetAnimatedPointList();
       if (pointList) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         pointList->ClearBaseValue();
         return;
       }
     }
 
     // Check if this is a path segment list attribute going away
     if (GetPathDataAttrName() == aName) {
       SVGAnimatedPathSegList *segList = GetAnimPathSegList();
       if (segList) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         segList->ClearBaseValue();
-        DidChangePathSegList(false);
         return;
       }
     }
 
     // Check if this is a number attribute going away
     NumberAttributesInfo numInfo = GetNumberInfo();
 
     for (PRUint32 i = 0; i < numInfo.mNumberCount; i++) {
       if (aName == *numInfo.mNumberInfo[i].mName) {
         numInfo.Reset(i);
-        DidChangeNumber(i, false);
         return;
       }
     }
 
     // Check if this is a number pair attribute going away
     NumberPairAttributesInfo numPairInfo = GetNumberPairInfo();
 
     for (PRUint32 i = 0; i < numPairInfo.mNumberPairCount; i++) {
       if (aName == *numPairInfo.mNumberPairInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         numPairInfo.Reset(i);
-        DidChangeNumberPair(i, false);
         return;
       }
     }
 
     // Check if this is an integer attribute going away
     IntegerAttributesInfo intInfo = GetIntegerInfo();
 
     for (PRUint32 i = 0; i < intInfo.mIntegerCount; i++) {
       if (aName == *intInfo.mIntegerInfo[i].mName) {
         intInfo.Reset(i);
-        DidChangeInteger(i, false);
         return;
       }
     }
 
     // Check if this is an integer pair attribute going away
     IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo();
 
     for (PRUint32 i = 0; i < intPairInfo.mIntegerPairCount; i++) {
       if (aName == *intPairInfo.mIntegerPairInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         intPairInfo.Reset(i);
-        DidChangeIntegerPair(i, false);
         return;
       }
     }
 
     // Check if this is an angle attribute going away
     AngleAttributesInfo angleInfo = GetAngleInfo();
 
     for (PRUint32 i = 0; i < angleInfo.mAngleCount; i++) {
       if (aName == *angleInfo.mAngleInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         angleInfo.Reset(i);
-        DidChangeAngle(i, false);
         return;
       }
     }
 
     // Check if this is a boolean attribute going away
     BooleanAttributesInfo boolInfo = GetBooleanInfo();
 
     for (PRUint32 i = 0; i < boolInfo.mBooleanCount; i++) {
       if (aName == *boolInfo.mBooleanInfo[i].mName) {
         boolInfo.Reset(i);
-        DidChangeBoolean(i, false);
         return;
       }
     }
 
     // Check if this is an enum attribute going away
     EnumAttributesInfo enumInfo = GetEnumInfo();
 
     for (PRUint32 i = 0; i < enumInfo.mEnumCount; i++) {
       if (aName == *enumInfo.mEnumInfo[i].mName) {
         enumInfo.Reset(i);
-        DidChangeEnum(i, false);
         return;
       }
     }
 
     // Check if this is a nsViewBox attribute going away
     if (aName == nsGkAtoms::viewBox) {
       nsSVGViewBox* viewBox = GetViewBox();
       if (viewBox) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         viewBox->Init();
-        DidChangeViewBox(false);
         return;
       }
     }
 
     // Check if this is a preserveAspectRatio attribute going away
     if (aName == nsGkAtoms::preserveAspectRatio) {
       SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
         GetPreserveAspectRatio();
-
       if (preserveAspectRatio) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         preserveAspectRatio->Init();
-        DidChangePreserveAspectRatio(false);
         return;
       }
     }
 
     // Check if this is a transform list attribute going away
     if (GetTransformListAttrName() == aName) {
       SVGAnimatedTransformList *transformList = GetAnimatedTransformList();
       if (transformList) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         transformList->ClearBaseValue();
-        DidChangeTransformList(false);
         return;
       }
     }
 
     // Check for conditional processing attributes
     nsCOMPtr<DOMSVGTests> tests(do_QueryInterface(this));
     if (tests && tests->IsConditionalProcessingAttribute(aName)) {
+      MaybeSerializeAttrBeforeRemoval(aName, aNotify);
       tests->UnsetAttr(aName);
       return;
     }
 
     // Check if this is a string list attribute going away
     StringListAttributesInfo stringListInfo = GetStringListInfo();
 
     for (PRUint32 i = 0; i < stringListInfo.mStringListCount; i++) {
       if (aName == *stringListInfo.mStringListInfo[i].mName) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         stringListInfo.Reset(i);
         return;
       }
     }
   }
 
   // Check if this is a string attribute going away
   StringAttributesInfo stringInfo = GetStringInfo();
 
   for (PRUint32 i = 0; i < stringInfo.mStringCount; i++) {
     if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID &&
         aName == *stringInfo.mStringInfo[i].mName) {
       stringInfo.Reset(i);
-      DidChangeString(i);
       return;
     }
   }
 }
 
 nsresult
 nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                         bool aNotify)
@@ -1242,16 +1302,147 @@ css::StyleRule*
 nsSVGElement::GetAnimatedContentStyleRule()
 {
   return
     static_cast<css::StyleRule*>(GetProperty(SMIL_MAPPED_ATTR_ANIMVAL,
                                              SMIL_MAPPED_ATTR_STYLERULE_ATOM,
                                              nsnull));
 }
 
+/**
+ * Helper methods for the type-specific WillChangeXXX methods.
+ *
+ * This method sends out appropriate pre-change notifications so that selector
+ * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
+ * matching) work, and it returns an nsAttrValue that _may_ contain the
+ * attribute's pre-change value.
+ *
+ * The nsAttrValue returned by this method depends on whether there are
+ * mutation event listeners listening for changes to this element's attributes.
+ * If not, then the object returned is empty. If there are, then the
+ * nsAttrValue returned contains a serialized copy of the attribute's value
+ * prior to the change, and this object should be passed to the corresponding
+ * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
+ * SVG type - see comment below). This is necessary so that the 'prevValue'
+ * property of the mutation event that is dispatched will correctly contain the
+ * old value.
+ *
+ * The reason we need to serialize the old value if there are mutation
+ * event listeners is because the underlying nsAttrValue for the attribute
+ * points directly to a parsed representation of the attribute (e.g. an
+ * SVGAnimatedLengthList*) that is a member of the SVG element. That object
+ * will have changed by the time DidChangeXXX has been called, so without the
+ * serialization of the old attribute value that we provide, DidChangeXXX
+ * would have no way to get the old value to pass to SetAttrAndNotify.
+ *
+ * We only return the old value when there are mutation event listeners because
+ * it's not needed otherwise, and because it's expensive to serialize the old
+ * value. This is especially true for list type attributes, which may be built
+ * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
+ * before the script finally finishes setting the attribute.
+ *
+ * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
+ * and filter out redundant changes. Before calling WillChangeXXX, the caller
+ * should check whether the new and old values are actually the same, and skip
+ * calling Will/DidChangeXXX if they are.
+ *
+ * Also note that not all SVG types use this scheme. For types that can be
+ * represented by an nsAttrValue without pointing back to an SVG object (e.g.
+ * enums, booleans, integers) we can simply use SetParsedAttr which will do all
+ * of the above for us. For such types there is no matching WillChangeXXX
+ * method, only DidChangeXXX which calls SetParsedAttr.
+ */
+nsAttrValue
+nsSVGElement::WillChangeValue(nsIAtom* aName)
+{
+  // We need an empty attr value:
+  //   a) to pass to BeforeSetAttr when GetParsedAttr returns nsnull
+  //   b) to store the old value in the case we have mutation listeners
+  // We can use the same value for both purposes since (a) happens before (b).
+  // Also, we should be careful to always return this value to benefit from
+  // return value optimization.
+  nsAttrValue emptyOrOldAttrValue;
+  const nsAttrValue* attrValue = GetParsedAttr(aName);
+
+  // This is not strictly correct--the attribute value parameter for
+  // BeforeSetAttr should reflect the value that *will* be set but that implies
+  // allocating, e.g. an extra nsSVGLength2, and isn't necessary at the moment
+  // since no SVG elements overload BeforeSetAttr. For now we just pass the
+  // current value.
+  nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue
+                                                  : emptyOrOldAttrValue);
+  DebugOnly<nsresult> rv =
+    BeforeSetAttr(kNameSpaceID_None, aName, &attrStringOrValue,
+                  kNotifyDocumentObservers);
+  // SVG elements aren't expected to overload BeforeSetAttr in such a way that
+  // it may fail. So long as this is the case we don't need to check and pass on
+  // the return value which simplifies the calling code significantly.
+  NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr");
+
+  // We only need to set the old value if we have listeners since otherwise it
+  // isn't used.
+  if (attrValue &&
+      nsContentUtils::HasMutationListeners(this,
+                                           NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
+                                           this)) {
+    emptyOrOldAttrValue.SetToSerialized(*attrValue);
+  }
+
+  PRUint8 modType = attrValue
+                  ? static_cast<PRUint8>(nsIDOMMutationEvent::MODIFICATION)
+                  : static_cast<PRUint8>(nsIDOMMutationEvent::ADDITION);
+  nsNodeUtils::AttributeWillChange(this, kNameSpaceID_None, aName, modType);
+
+  return emptyOrOldAttrValue;
+}
+
+/**
+ * Helper methods for the type-specific DidChangeXXX methods.
+ *
+ * aEmptyOrOldValue will normally be the object returned from the corresponding
+ * WillChangeXXX call. This is because:
+ * a) WillChangeXXX will ensure the object is set when we have mutation
+ *    listeners, and
+ * b) WillChangeXXX will ensure the object represents a serialized version of
+ *    the old attribute value so that the value doesn't change when the
+ *    underlying SVG type is updated.
+ */
+void
+nsSVGElement::DidChangeValue(nsIAtom* aName,
+                             const nsAttrValue& aEmptyOrOldValue,
+                             nsAttrValue& aNewValue)
+{
+  bool hasListeners =
+    nsContentUtils::HasMutationListeners(this,
+                                         NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
+                                         this);
+  PRUint8 modType = HasAttr(kNameSpaceID_None, aName)
+                  ? static_cast<PRUint8>(nsIDOMMutationEvent::MODIFICATION)
+                  : static_cast<PRUint8>(nsIDOMMutationEvent::ADDITION);
+  SetAttrAndNotify(kNameSpaceID_None, aName, nsnull, aEmptyOrOldValue,
+                   aNewValue, modType, hasListeners, kNotifyDocumentObservers,
+                   kCallAfterSetAttr);
+}
+
+void
+nsSVGElement::MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify)
+{
+  if (!aNotify ||
+      !nsContentUtils::HasMutationListeners(this,
+                                            NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
+                                            this)) {
+    return;
+  }
+
+  nsAutoString serializedValue;
+  mAttrsAndChildren.GetAttr(aName)->ToString(serializedValue);
+  nsAttrValue attrValue(serializedValue);
+  mAttrsAndChildren.SetAndTakeAttr(aName, attrValue);
+}
+
 /* static */
 nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr)
 {
   if (aAttr == nsGkAtoms::onload)
     return nsGkAtoms::onSVGLoad;
   if (aAttr == nsGkAtoms::onunload)
     return nsGkAtoms::onSVGUnload;
   if (aAttr == nsGkAtoms::onabort)
@@ -1324,35 +1515,37 @@ nsSVGElement::SetLength(nsIAtom* aName, 
       lengthInfo.mLengths[i] = aLength;
       DidAnimateLength(i);
       return;
     }
   }
   NS_ABORT_IF_FALSE(false, "no length found to set");
 }
 
-void
-nsSVGElement::DidChangeLength(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeLength(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
+  return WillChangeValue(*GetLengthInfo().mLengthInfo[aAttrEnum].mName);
+}
 
+void
+nsSVGElement::DidChangeLength(PRUint8 aAttrEnum,
+                              const nsAttrValue& aEmptyOrOldValue)
+{
   LengthAttributesInfo info = GetLengthInfo();
 
   NS_ASSERTION(info.mLengthCount > 0,
                "DidChangeLength on element with no length attribs");
-
   NS_ASSERTION(aAttrEnum < info.mLengthCount, "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mLengths[aAttrEnum].GetBaseValueString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(info.mLengths[aAttrEnum], nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, *info.mLengthInfo[aAttrEnum].mName, nsnull,
-                attrValue, true);
+  DidChangeValue(*info.mLengthInfo[aAttrEnum].mName, aEmptyOrOldValue,
+                 newValue);
 }
 
 void
 nsSVGElement::DidAnimateLength(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
 
   if (frame) {
@@ -1419,34 +1612,37 @@ nsSVGElement::GetLengthListInfo()
 
 void
 nsSVGElement::LengthListAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   mLengthLists[aAttrEnum].ClearBaseValue(aAttrEnum);
   // caller notifies
 }
 
-void
-nsSVGElement::DidChangeLengthList(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeLengthList(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
+  return WillChangeValue(*GetLengthListInfo().mLengthListInfo[aAttrEnum].mName);
+}
 
+void
+nsSVGElement::DidChangeLengthList(PRUint8 aAttrEnum,
+                                  const nsAttrValue& aEmptyOrOldValue)
+{
   LengthListAttributesInfo info = GetLengthListInfo();
 
   NS_ASSERTION(info.mLengthListCount > 0,
                "DidChangeLengthList on element with no length list attribs");
   NS_ASSERTION(aAttrEnum < info.mLengthListCount, "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mLengthLists[aAttrEnum].GetBaseValue().GetValueAsString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(info.mLengthLists[aAttrEnum].GetBaseValue(), nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, *info.mLengthListInfo[aAttrEnum].mName,
-                nsnull, attrValue, true);
+  DidChangeValue(*info.mLengthListInfo[aAttrEnum].mName, aEmptyOrOldValue,
+                 newValue);
 }
 
 void
 nsSVGElement::DidAnimateLengthList(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
 
   if (frame) {
@@ -1501,34 +1697,38 @@ nsSVGElement::GetNumberListInfo()
 void
 nsSVGElement::NumberListAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   NS_ABORT_IF_FALSE(aAttrEnum < mNumberListCount, "Bad attr enum");
   mNumberLists[aAttrEnum].ClearBaseValue(aAttrEnum);
   // caller notifies
 }
 
-void
-nsSVGElement::DidChangeNumberList(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeNumberList(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
+  return WillChangeValue(*GetNumberListInfo().mNumberListInfo[aAttrEnum].mName);
+}
 
+void
+nsSVGElement::DidChangeNumberList(PRUint8 aAttrEnum,
+                                  const nsAttrValue& aEmptyOrOldValue)
+{
   NumberListAttributesInfo info = GetNumberListInfo();
 
   NS_ABORT_IF_FALSE(info.mNumberListCount > 0,
-                    "DidChangeNumberList on element with no number list attribs");
-  NS_ABORT_IF_FALSE(aAttrEnum < info.mNumberListCount, "aAttrEnum out of range");
+    "DidChangeNumberList on element with no number list attribs");
+  NS_ABORT_IF_FALSE(aAttrEnum < info.mNumberListCount,
+    "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mNumberLists[aAttrEnum].GetBaseValue().GetValueAsString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(info.mNumberLists[aAttrEnum].GetBaseValue(), nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, *info.mNumberListInfo[aAttrEnum].mName,
-                nsnull, attrValue, true);
+  DidChangeValue(*info.mNumberListInfo[aAttrEnum].mName, aEmptyOrOldValue,
+                 newValue);
 }
 
 void
 nsSVGElement::DidAnimateNumberList(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
 
   if (frame) {
@@ -1560,30 +1760,34 @@ nsSVGElement::GetAnimatedNumberList(nsIA
     if (aAttrName == *info.mNumberListInfo[i].mName) {
       return &info.mNumberLists[i];
     }
   }
   NS_ABORT_IF_FALSE(false, "Bad caller");
   return nsnull;
 }
 
-void
-nsSVGElement::DidChangePointList(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangePointList()
 {
-  NS_ABORT_IF_FALSE(GetPointListAttrName(), "Changing non-existent point list?");
+  NS_ABORT_IF_FALSE(GetPointListAttrName(),
+                    "Changing non-existent point list?");
+  return WillChangeValue(GetPointListAttrName());
+}
 
-  if (!aDoSetAttr)
-    return;
+void
+nsSVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue)
+{
+  NS_ABORT_IF_FALSE(GetPointListAttrName(),
+                    "Changing non-existent point list?");
 
-  nsAutoString serializedValue;
-  GetAnimatedPointList()->GetBaseValue().GetValueAsString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, GetPointListAttrName(), nsnull,
-                attrValue, true);
+  DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue);
 }
 
 void
 nsSVGElement::DidAnimatePointList()
 {
   NS_ABORT_IF_FALSE(GetPointListAttrName(),
                     "Animating non-existent path data?");
 
@@ -1591,28 +1795,34 @@ nsSVGElement::DidAnimatePointList()
 
   if (frame) {
     frame->AttributeChanged(kNameSpaceID_None,
                             GetPointListAttrName(),
                             nsIDOMMutationEvent::MODIFICATION);
   }
 }
 
-void
-nsSVGElement::DidChangePathSegList(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangePathSegList()
 {
-  if (!aDoSetAttr)
-    return;
+  NS_ABORT_IF_FALSE(GetPathDataAttrName(),
+                    "Changing non-existent path seg list?");
+  return WillChangeValue(GetPathDataAttrName());
+}
 
-  nsAutoString serializedValue;
-  GetAnimPathSegList()->GetBaseValue().GetValueAsString(serializedValue);
+void
+nsSVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue)
+{
+  NS_ABORT_IF_FALSE(GetPathDataAttrName(),
+                    "Changing non-existent path seg list?");
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, GetPathDataAttrName(), nsnull,
-                attrValue, true);
+  nsAttrValue newValue;
+  newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nsnull);
+
+  DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue);
 }
 
 void
 nsSVGElement::DidAnimatePathSegList()
 {
   NS_ABORT_IF_FALSE(GetPathDataAttrName(),
                     "Animating non-existent path data?");
 
@@ -1633,32 +1843,27 @@ nsSVGElement::GetNumberInfo()
 
 void nsSVGElement::NumberAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   mNumbers[aAttrEnum].Init(aAttrEnum,
                            mNumberInfo[aAttrEnum].mDefaultValue);
 }
 
 void
-nsSVGElement::DidChangeNumber(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsSVGElement::DidChangeNumber(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
-
   NumberAttributesInfo info = GetNumberInfo();
 
   NS_ASSERTION(info.mNumberCount > 0,
                "DidChangeNumber on element with no number attribs");
-
   NS_ASSERTION(aAttrEnum < info.mNumberCount, "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mNumbers[aAttrEnum].GetBaseValueString(serializedValue);
+  nsAttrValue attrValue;
+  attrValue.SetTo(info.mNumbers[aAttrEnum].GetBaseValue(), nsnull);
 
-  nsAttrValue attrValue(serializedValue);
   SetParsedAttr(kNameSpaceID_None, *info.mNumberInfo[aAttrEnum].mName, nsnull,
                 attrValue, true);
 }
 
 void
 nsSVGElement::DidAnimateNumber(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
@@ -1700,35 +1905,37 @@ nsSVGElement::GetNumberPairInfo()
 
 void nsSVGElement::NumberPairAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   mNumberPairs[aAttrEnum].Init(aAttrEnum,
                                mNumberPairInfo[aAttrEnum].mDefaultValue1,
                                mNumberPairInfo[aAttrEnum].mDefaultValue2);
 }
 
-void
-nsSVGElement::DidChangeNumberPair(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeNumberPair(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
+  return WillChangeValue(*GetNumberPairInfo().mNumberPairInfo[aAttrEnum].mName);
+}
 
+void
+nsSVGElement::DidChangeNumberPair(PRUint8 aAttrEnum,
+                                  const nsAttrValue& aEmptyOrOldValue)
+{
   NumberPairAttributesInfo info = GetNumberPairInfo();
 
   NS_ASSERTION(info.mNumberPairCount > 0,
                "DidChangePairNumber on element with no number pair attribs");
-
   NS_ASSERTION(aAttrEnum < info.mNumberPairCount, "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mNumberPairs[aAttrEnum].GetBaseValueString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(info.mNumberPairs[aAttrEnum], nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, *info.mNumberPairInfo[aAttrEnum].mName, nsnull,
-                attrValue, true);
+  DidChangeValue(*info.mNumberPairInfo[aAttrEnum].mName, aEmptyOrOldValue,
+                 newValue);
 }
 
 void
 nsSVGElement::DidAnimateNumberPair(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
 
   if (frame) {
@@ -1747,32 +1954,27 @@ nsSVGElement::GetIntegerInfo()
 
 void nsSVGElement::IntegerAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   mIntegers[aAttrEnum].Init(aAttrEnum,
                             mIntegerInfo[aAttrEnum].mDefaultValue);
 }
 
 void
-nsSVGElement::DidChangeInteger(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsSVGElement::DidChangeInteger(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
-
   IntegerAttributesInfo info = GetIntegerInfo();
 
   NS_ASSERTION(info.mIntegerCount > 0,
                "DidChangeInteger on element with no integer attribs");
-
   NS_ASSERTION(aAttrEnum < info.mIntegerCount, "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mIntegers[aAttrEnum].GetBaseValueString(serializedValue);
+  nsAttrValue attrValue;
+  attrValue.SetTo(info.mIntegers[aAttrEnum].GetBaseValue(), nsnull);
 
-  nsAttrValue attrValue(serializedValue);
   SetParsedAttr(kNameSpaceID_None, *info.mIntegerInfo[aAttrEnum].mName, nsnull,
                 attrValue, true);
 }
 
 void
 nsSVGElement::DidAnimateInteger(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
@@ -1814,35 +2016,38 @@ nsSVGElement::GetIntegerPairInfo()
 
 void nsSVGElement::IntegerPairAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   mIntegerPairs[aAttrEnum].Init(aAttrEnum,
                                 mIntegerPairInfo[aAttrEnum].mDefaultValue1,
                                 mIntegerPairInfo[aAttrEnum].mDefaultValue2);
 }
 
-void
-nsSVGElement::DidChangeIntegerPair(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeIntegerPair(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
+  return WillChangeValue(
+    *GetIntegerPairInfo().mIntegerPairInfo[aAttrEnum].mName);
+}
 
+void
+nsSVGElement::DidChangeIntegerPair(PRUint8 aAttrEnum,
+                                   const nsAttrValue& aEmptyOrOldValue)
+{
   IntegerPairAttributesInfo info = GetIntegerPairInfo();
 
   NS_ASSERTION(info.mIntegerPairCount > 0,
                "DidChangeIntegerPair on element with no integer pair attribs");
-
   NS_ASSERTION(aAttrEnum < info.mIntegerPairCount, "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mIntegerPairs[aAttrEnum].GetBaseValueString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(info.mIntegerPairs[aAttrEnum], nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, *info.mIntegerPairInfo[aAttrEnum].mName, nsnull,
-                attrValue, true);
+  DidChangeValue(*info.mIntegerPairInfo[aAttrEnum].mName, aEmptyOrOldValue,
+                 newValue);
 }
 
 void
 nsSVGElement::DidAnimateIntegerPair(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
   
   if (frame) {
@@ -1861,35 +2066,36 @@ nsSVGElement::GetAngleInfo()
 
 void nsSVGElement::AngleAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   mAngles[aAttrEnum].Init(aAttrEnum, 
                           mAngleInfo[aAttrEnum].mDefaultValue,
                           mAngleInfo[aAttrEnum].mDefaultUnitType);
 }
 
-void
-nsSVGElement::DidChangeAngle(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeAngle(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
+  return WillChangeValue(*GetAngleInfo().mAngleInfo[aAttrEnum].mName);
+}
 
+void
+nsSVGElement::DidChangeAngle(PRUint8 aAttrEnum,
+                             const nsAttrValue& aEmptyOrOldValue)
+{
   AngleAttributesInfo info = GetAngleInfo();
 
   NS_ASSERTION(info.mAngleCount > 0,
                "DidChangeAngle on element with no angle attribs");
-
   NS_ASSERTION(aAttrEnum < info.mAngleCount, "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mAngles[aAttrEnum].GetBaseValueString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(info.mAngles[aAttrEnum], nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, *info.mAngleInfo[aAttrEnum].mName, nsnull,
-                attrValue, true);
+  DidChangeValue(*info.mAngleInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue);
 }
 
 void
 nsSVGElement::DidAnimateAngle(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
 
   if (frame) {
@@ -1908,32 +2114,25 @@ nsSVGElement::GetBooleanInfo()
 
 void nsSVGElement::BooleanAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   mBooleans[aAttrEnum].Init(aAttrEnum,
                             mBooleanInfo[aAttrEnum].mDefaultValue);
 }
 
 void
-nsSVGElement::DidChangeBoolean(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsSVGElement::DidChangeBoolean(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
-
   BooleanAttributesInfo info = GetBooleanInfo();
 
   NS_ASSERTION(info.mBooleanCount > 0,
                "DidChangeBoolean on element with no boolean attribs");
-
   NS_ASSERTION(aAttrEnum < info.mBooleanCount, "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mBooleans[aAttrEnum].GetBaseValueString(serializedValue);
-
-  nsAttrValue attrValue(serializedValue);
+  nsAttrValue attrValue(info.mBooleans[aAttrEnum].GetBaseValueAtom());
   SetParsedAttr(kNameSpaceID_None, *info.mBooleanInfo[aAttrEnum].mName, nsnull,
                 attrValue, true);
 }
 
 void
 nsSVGElement::DidAnimateBoolean(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
@@ -1954,32 +2153,25 @@ nsSVGElement::GetEnumInfo()
 
 void nsSVGElement::EnumAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   mEnums[aAttrEnum].Init(aAttrEnum,
                          mEnumInfo[aAttrEnum].mDefaultValue);
 }
 
 void
-nsSVGElement::DidChangeEnum(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsSVGElement::DidChangeEnum(PRUint8 aAttrEnum)
 {
-  if (!aDoSetAttr)
-    return;
-
   EnumAttributesInfo info = GetEnumInfo();
 
   NS_ASSERTION(info.mEnumCount > 0,
                "DidChangeEnum on element with no enum attribs");
-
   NS_ASSERTION(aAttrEnum < info.mEnumCount, "aAttrEnum out of range");
 
-  nsAutoString serializedValue;
-  info.mEnums[aAttrEnum].GetBaseValueString(serializedValue, this);
-
-  nsAttrValue attrValue(serializedValue);
+  nsAttrValue attrValue(info.mEnums[aAttrEnum].GetBaseValueAtom(this));
   SetParsedAttr(kNameSpaceID_None, *info.mEnumInfo[aAttrEnum].mName, nsnull,
                 attrValue, true);
 }
 
 void
 nsSVGElement::DidAnimateEnum(PRUint8 aAttrEnum)
 {
   nsIFrame* frame = GetPrimaryFrame();
@@ -1993,32 +2185,33 @@ nsSVGElement::DidAnimateEnum(PRUint8 aAt
 }
 
 nsSVGViewBox *
 nsSVGElement::GetViewBox()
 {
   return nsnull;
 }
 
-void
-nsSVGElement::DidChangeViewBox(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeViewBox()
 {
-  if (!aDoSetAttr)
-    return;
+  return WillChangeValue(nsGkAtoms::viewBox);
+}
 
+void
+nsSVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue)
+{
   nsSVGViewBox *viewBox = GetViewBox();
 
   NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib");
 
-  nsAutoString serializedValue;
-  viewBox->GetBaseValueString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(*viewBox, nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, nsGkAtoms::viewBox, nsnull,
-                attrValue, true);
+  DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue);
 }
 
 void
 nsSVGElement::DidAnimateViewBox()
 {
   nsIFrame* frame = GetPrimaryFrame();
   
   if (frame) {
@@ -2029,64 +2222,66 @@ nsSVGElement::DidAnimateViewBox()
 }
 
 SVGAnimatedPreserveAspectRatio *
 nsSVGElement::GetPreserveAspectRatio()
 {
   return nsnull;
 }
 
-void
-nsSVGElement::DidChangePreserveAspectRatio(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangePreserveAspectRatio()
 {
-  if (!aDoSetAttr)
-    return;
+  return WillChangeValue(nsGkAtoms::preserveAspectRatio);
+}
 
+void
+nsSVGElement::DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue)
+{
   SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
     GetPreserveAspectRatio();
 
   NS_ASSERTION(preserveAspectRatio,
-               "DidChangePreserveAspectRatio on element with no preserveAspectRatio attrib");
+               "DidChangePreserveAspectRatio on element with no "
+               "preserveAspectRatio attrib");
 
-  nsAutoString serializedValue;
-  preserveAspectRatio->GetBaseValueString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(*preserveAspectRatio, nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio, nsnull,
-                attrValue, true);
+  DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue);
 }
 
 void
 nsSVGElement::DidAnimatePreserveAspectRatio()
 {
   nsIFrame* frame = GetPrimaryFrame();
   
   if (frame) {
     frame->AttributeChanged(kNameSpaceID_None,
                             nsGkAtoms::preserveAspectRatio,
                             nsIDOMMutationEvent::MODIFICATION);
   }
 }
 
-void
-nsSVGElement::DidChangeTransformList(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeTransformList()
 {
-  if (!aDoSetAttr)
-    return;
+  return WillChangeValue(GetTransformListAttrName());
+}
 
-  SVGAnimatedTransformList* transformList = GetAnimatedTransformList();
-  NS_ABORT_IF_FALSE(transformList,
-                    "DidChangeTransformList on element with no transform list");
+void
+nsSVGElement::DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue)
+{
+  NS_ABORT_IF_FALSE(GetTransformListAttrName(),
+                    "Changing non-existent transform list?");
 
-  nsAutoString serializedValue;
-  transformList->GetBaseValue().GetValueAsString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(GetAnimatedTransformList()->GetBaseValue(), nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, GetTransformListAttrName(), nsnull,
-                attrValue, true);
+  DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue);
 }
 
 void
 nsSVGElement::DidAnimateTransformList()
 {
   NS_ABORT_IF_FALSE(GetTransformListAttrName(),
                     "Animating non-existent transform data?");
 
@@ -2150,39 +2345,59 @@ nsSVGElement::DidAnimateString(PRUint8 a
 }
 
 nsSVGElement::StringListAttributesInfo
 nsSVGElement::GetStringListInfo()
 {
   return StringListAttributesInfo(nsnull, nsnull, 0);
 }
 
+nsAttrValue
+nsSVGElement::WillChangeStringList(bool aIsConditionalProcessingAttribute,
+                                   PRUint8 aAttrEnum)
+{
+  nsIAtom* name;
+  if (aIsConditionalProcessingAttribute) {
+    nsCOMPtr<DOMSVGTests> tests(do_QueryInterface(this));
+    name = tests->GetAttrName(aAttrEnum);
+  } else {
+    name = *GetStringListInfo().mStringListInfo[aAttrEnum].mName;
+  }
+  return WillChangeValue(name);
+}
+
 void
 nsSVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
-                                  PRUint8 aAttrEnum)
+                                  PRUint8 aAttrEnum,
+                                  const nsAttrValue& aEmptyOrOldValue)
 {
+  nsIAtom* name;
+  nsAttrValue newValue;
+  nsCOMPtr<DOMSVGTests> tests;
+
   if (aIsConditionalProcessingAttribute) {
-    nsCOMPtr<DOMSVGTests> tests(do_QueryInterface(this));
-    tests->DidChangeStringList(aAttrEnum);
-    return;
+    tests = do_QueryInterface(this);
+    name = tests->GetAttrName(aAttrEnum);
+    tests->GetAttrValue(aAttrEnum, newValue);
+  } else {
+    StringListAttributesInfo info = GetStringListInfo();
+
+    NS_ASSERTION(info.mStringListCount > 0,
+                 "DidChangeStringList on element with no string list attribs");
+    NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range");
+
+    name = *info.mStringListInfo[aAttrEnum].mName;
+    newValue.SetTo(info.mStringLists[aAttrEnum], nsnull);
   }
 
-  StringListAttributesInfo info = GetStringListInfo();
-
-  NS_ASSERTION(info.mStringListCount > 0,
-               "DidChangeStringList on element with no string list attribs");
-
-  NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range");
+  DidChangeValue(name, aEmptyOrOldValue, newValue);
 
-  nsAutoString serializedValue;
-  info.mStringLists[aAttrEnum].GetValue(serializedValue, this);
-
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, *info.mStringListInfo[aAttrEnum].mName,
-                nsnull, attrValue, true);
+  if (aIsConditionalProcessingAttribute) {
+    tests->MaybeInvalidate();
+  }
 }
 
 void
 nsSVGElement::StringListAttributesInfo::Reset(PRUint8 aAttrEnum)
 {
   mStringLists[aAttrEnum].Clear();
   // caller notifies
 }
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -164,34 +164,54 @@ public:
 
   bool IsStringAnimatable(PRUint8 aAttrEnum) {
     return GetStringInfo().mStringInfo[aAttrEnum].mIsAnimatable;
   }
   bool NumberAttrAllowsPercentage(PRUint8 aAttrEnum) {
     return GetNumberInfo().mNumberInfo[aAttrEnum].mPercentagesAllowed;
   }
   void SetLength(nsIAtom* aName, const nsSVGLength2 &aLength);
-  virtual void DidChangeLength(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangeNumber(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangeNumberPair(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangeInteger(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangeIntegerPair(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangeAngle(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangeBoolean(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangeEnum(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangeViewBox(bool aDoSetAttr);
-  virtual void DidChangePreserveAspectRatio(bool aDoSetAttr);
-  virtual void DidChangeNumberList(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangeLengthList(PRUint8 aAttrEnum, bool aDoSetAttr);
-  virtual void DidChangePointList(bool aDoSetAttr);
-  virtual void DidChangePathSegList(bool aDoSetAttr);
-  virtual void DidChangeTransformList(bool aDoSetAttr);
-  virtual void DidChangeString(PRUint8 aAttrEnum) {}
+
+  nsAttrValue WillChangeLength(PRUint8 aAttrEnum);
+  nsAttrValue WillChangeNumberPair(PRUint8 aAttrEnum);
+  nsAttrValue WillChangeIntegerPair(PRUint8 aAttrEnum);
+  nsAttrValue WillChangeAngle(PRUint8 aAttrEnum);
+  nsAttrValue WillChangeViewBox();
+  nsAttrValue WillChangePreserveAspectRatio();
+  nsAttrValue WillChangeNumberList(PRUint8 aAttrEnum);
+  nsAttrValue WillChangeLengthList(PRUint8 aAttrEnum);
+  nsAttrValue WillChangePointList();
+  nsAttrValue WillChangePathSegList();
+  nsAttrValue WillChangeTransformList();
+  nsAttrValue WillChangeStringList(bool aIsConditionalProcessingAttribute,
+                                   PRUint8 aAttrEnum);
+
+  void DidChangeLength(PRUint8 aAttrEnum, const nsAttrValue& aEmptyOrOldValue);
+  void DidChangeNumber(PRUint8 aAttrEnum);
+  void DidChangeNumberPair(PRUint8 aAttrEnum,
+                           const nsAttrValue& aEmptyOrOldValue);
+  void DidChangeInteger(PRUint8 aAttrEnum);
+  void DidChangeIntegerPair(PRUint8 aAttrEnum,
+                            const nsAttrValue& aEmptyOrOldValue);
+  void DidChangeAngle(PRUint8 aAttrEnum, const nsAttrValue& aEmptyOrOldValue);
+  void DidChangeBoolean(PRUint8 aAttrEnum);
+  void DidChangeEnum(PRUint8 aAttrEnum);
+  void DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue);
+  void DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue);
+  void DidChangeNumberList(PRUint8 aAttrEnum,
+                           const nsAttrValue& aEmptyOrOldValue);
+  void DidChangeLengthList(PRUint8 aAttrEnum,
+                           const nsAttrValue& aEmptyOrOldValue);
+  void DidChangePointList(const nsAttrValue& aEmptyOrOldValue);
+  void DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue);
+  void DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue);
+  void DidChangeString(PRUint8 aAttrEnum) {}
   void DidChangeStringList(bool aIsConditionalProcessingAttribute,
-                           PRUint8 aAttrEnum);
+                           PRUint8 aAttrEnum,
+                           const nsAttrValue& aEmptyOrOldValue);
 
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateNumber(PRUint8 aAttrEnum);
   virtual void DidAnimateNumberPair(PRUint8 aAttrEnum);
   virtual void DidAnimateInteger(PRUint8 aAttrEnum);
   virtual void DidAnimateIntegerPair(PRUint8 aAttrEnum);
   virtual void DidAnimateAngle(PRUint8 aAttrEnum);
   virtual void DidAnimateBoolean(PRUint8 aAttrEnum);
@@ -244,31 +264,46 @@ public:
   virtual nsIAtom* GetPathDataAttrName() const {
     return nsnull;
   }
   virtual nsIAtom* GetTransformListAttrName() const {
     return nsnull;
   }
 
 protected:
+#ifdef DEBUG
+  // We define BeforeSetAttr here and mark it MOZ_FINAL to ensure it is NOT used
+  // by SVG elements.
+  // This is because we're not currently passing the correct value for aValue to
+  // BeforeSetAttr since it would involve allocating extra SVG value types.
+  // See the comment in nsSVGElement::WillChangeValue.
+  virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify) MOZ_FINAL { return NS_OK; }
+#endif // DEBUG
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify);
   virtual bool ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute,
                                 const nsAString& aValue, nsAttrValue& aResult);
   static nsresult ReportAttributeParseFailure(nsIDocument* aDocument,
                                               nsIAtom* aAttribute,
                                               const nsAString& aValue);
 
   // Hooks for subclasses
   virtual bool IsEventName(nsIAtom* aName);
 
   void UpdateContentStyleRule();
   void UpdateAnimatedContentStyleRule();
   mozilla::css::StyleRule* GetAnimatedContentStyleRule();
 
+  nsAttrValue WillChangeValue(nsIAtom* aName);
+  void DidChangeValue(nsIAtom* aName, const nsAttrValue& aEmptyOrOldValue,
+                      nsAttrValue& aNewValue);
+  void MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify);
+
   static nsIAtom* GetEventNameForAttr(nsIAtom* aAttr);
 
   struct LengthInfo {
     nsIAtom** mName;
     float     mDefaultValue;
     PRUint8   mDefaultUnitType;
     PRUint8   mCtxType;
   };
--- a/content/svg/content/src/nsSVGEnum.cpp
+++ b/content/svg/content/src/nsSVGEnum.cpp
@@ -62,25 +62,22 @@ nsSVGEnum::GetMapping(nsSVGElement *aSVG
 
   NS_ASSERTION(info.mEnumCount > 0 && mAttrEnum < info.mEnumCount,
                "mapping request for a non-attrib enum");
 
   return info.mEnumInfo[mAttrEnum].mMapping;
 }
 
 nsresult
-nsSVGEnum::SetBaseValueString(const nsAString& aValue,
-                              nsSVGElement *aSVGElement)
+nsSVGEnum::SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement)
 {
-  nsCOMPtr<nsIAtom> valAtom = do_GetAtom(aValue);
-
   nsSVGEnumMapping *mapping = GetMapping(aSVGElement);
 
   while (mapping && mapping->mKey) {
-    if (valAtom == *(mapping->mKey)) {
+    if (aValue == *(mapping->mKey)) {
       mIsBaseSet = true;
       if (mBaseVal != mapping->mVal) {
         mBaseVal = mapping->mVal;
         if (!mIsAnimated) {
           mAnimVal = mBaseVal;
         }
         else {
           aSVGElement->AnimationNeedsResample();
@@ -94,29 +91,29 @@ nsSVGEnum::SetBaseValueString(const nsAS
     mapping++;
   }
 
   // only a warning since authors may mistype attribute values
   NS_WARNING("unknown enumeration key");
   return NS_ERROR_DOM_SYNTAX_ERR;
 }
 
-void
-nsSVGEnum::GetBaseValueString(nsAString& aValue, nsSVGElement *aSVGElement)
+nsIAtom*
+nsSVGEnum::GetBaseValueAtom(nsSVGElement *aSVGElement)
 {
   nsSVGEnumMapping *mapping = GetMapping(aSVGElement);
 
   while (mapping && mapping->mKey) {
     if (mBaseVal == mapping->mVal) {
-      (*mapping->mKey)->ToString(aValue);
-      return;
+      return *mapping->mKey;
     }
     mapping++;
   }
   NS_ERROR("unknown enumeration value");
+  return nsGkAtoms::_empty;
 }
 
 nsresult
 nsSVGEnum::SetBaseValue(PRUint16 aValue,
                         nsSVGElement *aSVGElement)
 {
   nsSVGEnumMapping *mapping = GetMapping(aSVGElement);
 
@@ -126,17 +123,17 @@ nsSVGEnum::SetBaseValue(PRUint16 aValue,
       if (mBaseVal != PRUint8(aValue)) {
         mBaseVal = PRUint8(aValue);
         if (!mIsAnimated) {
           mAnimVal = mBaseVal;
         }
         else {
           aSVGElement->AnimationNeedsResample();
         }
-        aSVGElement->DidChangeEnum(mAttrEnum, true);
+        aSVGElement->DidChangeEnum(mAttrEnum);
       }
       return NS_OK;
     }
     mapping++;
   }
   return NS_ERROR_DOM_SYNTAX_ERR;
 }
 
--- a/content/svg/content/src/nsSVGEnum.h
+++ b/content/svg/content/src/nsSVGEnum.h
@@ -60,21 +60,18 @@ class nsSVGEnum
 public:
   void Init(PRUint8 aAttrEnum, PRUint16 aValue) {
     mAnimVal = mBaseVal = PRUint8(aValue);
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
     mIsBaseSet = false;
   }
 
-  nsresult SetBaseValueString(const nsAString& aValue,
-                              nsSVGElement *aSVGElement);
-  void GetBaseValueString(nsAString& aValue,
-                          nsSVGElement *aSVGElement);
-
+  nsresult SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement);
+  nsIAtom* GetBaseValueAtom(nsSVGElement *aSVGElement);
   nsresult SetBaseValue(PRUint16 aValue,
                         nsSVGElement *aSVGElement);
   PRUint16 GetBaseValue() const
     { return mBaseVal; }
 
   void SetAnimValue(PRUint16 aValue, nsSVGElement *aSVGElement);
   PRUint16 GetAnimValue() const
     { return mAnimVal; }
--- a/content/svg/content/src/nsSVGInteger.cpp
+++ b/content/svg/content/src/nsSVGInteger.cpp
@@ -101,28 +101,35 @@ nsSVGInteger::SetBaseValueString(const n
 void
 nsSVGInteger::GetBaseValueString(nsAString & aValueAsString)
 {
   aValueAsString.Truncate();
   aValueAsString.AppendInt(mBaseVal);
 }
 
 void
-nsSVGInteger::SetBaseValue(int aValue,
-                           nsSVGElement *aSVGElement)
+nsSVGInteger::SetBaseValue(int aValue, nsSVGElement *aSVGElement)
 {
+  // We can't just rely on SetParsedAttrValue (as called by DidChangeInteger)
+  // detecting redundant changes since it will compare false if the existing
+  // attribute value has an associated serialized version (a string value) even
+  // if the integers match due to the way integers are stored in nsAttrValue.
+  if (aValue == mBaseVal && mIsBaseSet) {
+    return;
+  }
+
   mBaseVal = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeInteger(mAttrEnum, true);
+  aSVGElement->DidChangeInteger(mAttrEnum);
 }
 
 void
 nsSVGInteger::SetAnimValue(int aValue, nsSVGElement *aSVGElement)
 {
   mAnimVal = aValue;
   mIsAnimated = true;
   aSVGElement->DidAnimateInteger(mAttrEnum);
--- a/content/svg/content/src/nsSVGIntegerPair.cpp
+++ b/content/svg/content/src/nsSVGIntegerPair.cpp
@@ -123,57 +123,67 @@ nsSVGIntegerPair::SetBaseValueString(con
 
   // We don't need to call DidChange* here - we're only called by
   // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
   // which takes care of notifying.
   return NS_OK;
 }
 
 void
-nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString)
+nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString) const
 {
   aValueAsString.Truncate();
   aValueAsString.AppendInt(mBaseVal[0]);
   if (mBaseVal[0] != mBaseVal[1]) {
     aValueAsString.AppendLiteral(", ");
     aValueAsString.AppendInt(mBaseVal[1]);
   }
 }
 
 void
 nsSVGIntegerPair::SetBaseValue(PRInt32 aValue, PairIndex aPairIndex,
                                nsSVGElement *aSVGElement)
 {
   PRUint32 index = (aPairIndex == eFirst ? 0 : 1);
+  if (mIsBaseSet && mBaseVal[index] == aValue) {
+    return;
+  }
+
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeIntegerPair(mAttrEnum);
   mBaseVal[index] = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[index] = aValue;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeIntegerPair(mAttrEnum, true);
+  aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue);
 }
 
 void
 nsSVGIntegerPair::SetBaseValues(PRInt32 aValue1, PRInt32 aValue2,
                                 nsSVGElement *aSVGElement)
 {
+  if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) {
+    return;
+  }
+
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeIntegerPair(mAttrEnum);
   mBaseVal[0] = aValue1;
   mBaseVal[1] = aValue2;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[0] = aValue1;
     mAnimVal[1] = aValue2;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeIntegerPair(mAttrEnum, true);
+  aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue);
 }
 
 void
 nsSVGIntegerPair::SetAnimValue(const PRInt32 aValue[2], nsSVGElement *aSVGElement)
 {
   mAnimVal[0] = aValue[0];
   mAnimVal[1] = aValue[1];
   mIsAnimated = true;
--- a/content/svg/content/src/nsSVGIntegerPair.h
+++ b/content/svg/content/src/nsSVGIntegerPair.h
@@ -61,17 +61,17 @@ public:
     mAnimVal[1] = mBaseVal[1] = aValue2;
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
     mIsBaseSet = false;
   }
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement);
-  void GetBaseValueString(nsAString& aValue);
+  void GetBaseValueString(nsAString& aValue) const;
 
   void SetBaseValue(PRInt32 aValue, PairIndex aIndex, nsSVGElement *aSVGElement);
   void SetBaseValues(PRInt32 aValue1, PRInt32 aValue2, nsSVGElement *aSVGElement);
   PRInt32 GetBaseValue(PairIndex aIndex) const
     { return mBaseVal[aIndex == eFirst ? 0 : 1]; }
   void SetAnimValue(const PRInt32 aValue[2], nsSVGElement *aSVGElement);
   PRInt32 GetAnimValue(PairIndex aIndex) const
     { return mAnimVal[aIndex == eFirst ? 0 : 1]; }
--- a/content/svg/content/src/nsSVGLength2.cpp
+++ b/content/svg/content/src/nsSVGLength2.cpp
@@ -302,64 +302,94 @@ nsSVGLength2::GetUnitScaleFactor(nsIFram
   default:
     NS_NOTREACHED("Unknown unit type");
     return 0;
   }
 }
 
 void
 nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue,
-                                           nsSVGElement *aSVGElement)
+                                           nsSVGElement *aSVGElement,
+                                           bool aDoSetAttr)
 {
+  if (mIsBaseSet && mBaseVal == aValue) {
+    return;
+  }
+
+  nsAttrValue emptyOrOldValue;
+  if (aDoSetAttr) {
+    emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
+  }
   mBaseVal = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeLength(mAttrEnum, true);
+  if (aDoSetAttr) {
+    aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
+  }
 }
 
 nsresult
 nsSVGLength2::ConvertToSpecifiedUnits(PRUint16 unitType,
                                       nsSVGElement *aSVGElement)
 {
   if (!IsValidUnitType(unitType))
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
-  float valueInUserUnits = 
+  if (mIsBaseSet && mSpecifiedUnitType == PRUint8(unitType))
+    return NS_OK;
+
+  // Even though we're not changing the visual effect this length will have
+  // on the document, we still need to send out notifications in case we have
+  // mutation listeners, since the actual string value of the attribute will
+  // change.
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
+
+  float valueInUserUnits =
     mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType);
   mSpecifiedUnitType = PRUint8(unitType);
-  SetBaseValue(valueInUserUnits, aSVGElement);
+  // Setting aDoSetAttr to false here will ensure we don't call
+  // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
+  SetBaseValue(valueInUserUnits, aSVGElement, false);
+
+  aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
 
   return NS_OK;
 }
 
 nsresult
 nsSVGLength2::NewValueSpecifiedUnits(PRUint16 unitType,
                                      float valueInSpecifiedUnits,
                                      nsSVGElement *aSVGElement)
 {
   NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
 
   if (!IsValidUnitType(unitType))
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
+  if (mIsBaseSet && mBaseVal == valueInSpecifiedUnits &&
+      mSpecifiedUnitType == PRUint8(unitType)) {
+    return NS_OK;
+  }
+
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
   mBaseVal = valueInSpecifiedUnits;
   mIsBaseSet = true;
   mSpecifiedUnitType = PRUint8(unitType);
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeLength(mAttrEnum, true);
+  aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
   return NS_OK;
 }
 
 nsresult
 nsSVGLength2::ToDOMBaseVal(nsIDOMSVGLength **aResult, nsSVGElement *aSVGElement)
 {
   *aResult = sBaseSVGLengthTearoffTable.GetTearoff(this);
   if (!*aResult) {
@@ -402,54 +432,66 @@ nsSVGLength2::DOMAnimVal::~DOMAnimVal()
 
 nsresult
 nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString,
                                  nsSVGElement *aSVGElement,
                                  bool aDoSetAttr)
 {
   float value;
   PRUint16 unitType;
-  
+
   nsresult rv = GetValueFromString(aValueAsString, &value, &unitType);
   if (NS_FAILED(rv)) {
     return rv;
   }
-  
+
+  if (mIsBaseSet && mBaseVal == value &&
+      mSpecifiedUnitType == PRUint8(unitType)) {
+    return NS_OK;
+  }
+
+  nsAttrValue emptyOrOldValue;
+  if (aDoSetAttr) {
+    emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
+  }
   mBaseVal = value;
   mIsBaseSet = true;
   mSpecifiedUnitType = PRUint8(unitType);
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
 
-  aSVGElement->DidChangeLength(mAttrEnum, aDoSetAttr);
+  if (aDoSetAttr) {
+    aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
+  }
   return NS_OK;
 }
 
 void
-nsSVGLength2::GetBaseValueString(nsAString & aValueAsString)
+nsSVGLength2::GetBaseValueString(nsAString & aValueAsString) const
 {
   GetValueString(aValueAsString, mBaseVal, mSpecifiedUnitType);
 }
 
 void
-nsSVGLength2::GetAnimValueString(nsAString & aValueAsString)
+nsSVGLength2::GetAnimValueString(nsAString & aValueAsString) const
 {
   GetValueString(aValueAsString, mAnimVal, mSpecifiedUnitType);
 }
 
 void
-nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement)
+nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement,
+                           bool aDoSetAttr)
 {
   SetBaseValueInSpecifiedUnits(aValue * GetUnitScaleFactor(aSVGElement,
                                                            mSpecifiedUnitType),
-                               aSVGElement);
+                               aSVGElement, aDoSetAttr);
 }
 
 void
 nsSVGLength2::SetAnimValueInSpecifiedUnits(float aValue,
                                            nsSVGElement* aSVGElement)
 {
   mAnimVal = aValue;
   mIsAnimated = true;
--- a/content/svg/content/src/nsSVGLength2.h
+++ b/content/svg/content/src/nsSVGLength2.h
@@ -77,18 +77,18 @@ public:
     mIsAnimated = aLength.mIsAnimated;
     mIsBaseSet = aLength.mIsBaseSet;
     return *this;
   }
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement,
                               bool aDoSetAttr);
-  void GetBaseValueString(nsAString& aValue);
-  void GetAnimValueString(nsAString& aValue);
+  void GetBaseValueString(nsAString& aValue) const;
+  void GetAnimValueString(nsAString& aValue) const;
 
   float GetBaseValue(nsSVGElement* aSVGElement) const
     { return mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); }
   float GetAnimValue(nsSVGElement* aSVGElement) const
     { return mAnimVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); }
   float GetAnimValue(nsIFrame* aFrame) const
     { return mAnimVal / GetUnitScaleFactor(aFrame, mSpecifiedUnitType); }
 
@@ -143,18 +143,19 @@ private:
   static float GetEmLength(nsSVGElement *aSVGElement)
     { return nsSVGUtils::GetFontSize(aSVGElement); }
   static float GetExLength(nsSVGElement *aSVGElement)
     { return nsSVGUtils::GetFontXHeight(aSVGElement); }
   float GetUnitScaleFactor(nsSVGElement *aSVGElement, PRUint8 aUnitType) const;
   float GetUnitScaleFactor(nsSVGSVGElement *aCtx, PRUint8 aUnitType) const;
 
   // SetBaseValue and SetAnimValue set the value in user units
-  void SetBaseValue(float aValue, nsSVGElement *aSVGElement);
-  void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement);
+  void SetBaseValue(float aValue, nsSVGElement *aSVGElement, bool aDoSetAttr);
+  void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement,
+                                    bool aDoSetAttr);
   void SetAnimValue(float aValue, nsSVGElement *aSVGElement);
   void SetAnimValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement);
   nsresult NewValueSpecifiedUnits(PRUint16 aUnitType, float aValue,
                                   nsSVGElement *aSVGElement);
   nsresult ConvertToSpecifiedUnits(PRUint16 aUnitType, nsSVGElement *aSVGElement);
   nsresult ToDOMBaseVal(nsIDOMSVGLength **aResult, nsSVGElement* aSVGElement);
   nsresult ToDOMAnimVal(nsIDOMSVGLength **aResult, nsSVGElement* aSVGElement);
 
@@ -175,28 +176,28 @@ private:
 
     NS_IMETHOD GetValue(float* aResult)
       { *aResult = mVal->GetBaseValue(mSVGElement); return NS_OK; }
     NS_IMETHOD SetValue(float aValue)
       {
         if (!NS_finite(aValue)) {
           return NS_ERROR_ILLEGAL_VALUE;
         }
-        mVal->SetBaseValue(aValue, mSVGElement);
+        mVal->SetBaseValue(aValue, mSVGElement, true);
         return NS_OK;
       }
 
     NS_IMETHOD GetValueInSpecifiedUnits(float* aResult)
       { *aResult = mVal->mBaseVal; return NS_OK; }
     NS_IMETHOD SetValueInSpecifiedUnits(float aValue)
       {
         if (!NS_finite(aValue)) {
           return NS_ERROR_ILLEGAL_VALUE;
         }
-        mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement);
+        mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true);
         return NS_OK;
       }
 
     NS_IMETHOD SetValueAsString(const nsAString& aValue)
       { return mVal->SetBaseValueString(aValue, mSVGElement, true); }
     NS_IMETHOD GetValueAsString(nsAString& aValue)
       { mVal->GetBaseValueString(aValue); return NS_OK; }
 
--- a/content/svg/content/src/nsSVGMarkerElement.cpp
+++ b/content/svg/content/src/nsSVGMarkerElement.cpp
@@ -219,17 +219,17 @@ NS_IMETHODIMP nsSVGMarkerElement::SetOri
 {
   if (!angle)
     return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
 
   float f;
   nsresult rv = angle->GetValue(&f);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_FINITE(f, NS_ERROR_DOM_SVG_WRONG_TYPE_ERR);
-  mAngleAttributes[ORIENT].SetBaseValue(f, this);
+  mAngleAttributes[ORIENT].SetBaseValue(f, this, true);
 
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 // nsIContent methods
 
 NS_IMETHODIMP_(bool)
--- a/content/svg/content/src/nsSVGNumber2.cpp
+++ b/content/svg/content/src/nsSVGNumber2.cpp
@@ -144,28 +144,31 @@ nsSVGNumber2::SetBaseValueString(const n
 void
 nsSVGNumber2::GetBaseValueString(nsAString & aValueAsString)
 {
   aValueAsString.Truncate();
   aValueAsString.AppendFloat(mBaseVal);
 }
 
 void
-nsSVGNumber2::SetBaseValue(float aValue,
-                           nsSVGElement *aSVGElement)
+nsSVGNumber2::SetBaseValue(float aValue, nsSVGElement *aSVGElement)
 {
+  if (mIsBaseSet && aValue == mBaseVal) {
+    return;
+  }
+
   mBaseVal = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeNumber(mAttrEnum, true);
+  aSVGElement->DidChangeNumber(mAttrEnum);
 }
 
 void
 nsSVGNumber2::SetAnimValue(float aValue, nsSVGElement *aSVGElement)
 {
   mAnimVal = aValue;
   mIsAnimated = true;
   aSVGElement->DidAnimateNumber(mAttrEnum);
--- a/content/svg/content/src/nsSVGNumberPair.cpp
+++ b/content/svg/content/src/nsSVGNumberPair.cpp
@@ -116,64 +116,72 @@ nsSVGNumberPair::SetBaseValueString(cons
   if (!mIsAnimated) {
     mAnimVal[0] = mBaseVal[0];
     mAnimVal[1] = mBaseVal[1];
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
 
-  // We don't need to call DidChange* here - we're only called by
+  // We don't need to call Will/DidChange* here - we're only called by
   // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
   // which takes care of notifying.
   return NS_OK;
 }
 
 void
-nsSVGNumberPair::GetBaseValueString(nsAString &aValueAsString)
+nsSVGNumberPair::GetBaseValueString(nsAString &aValueAsString) const
 {
   aValueAsString.Truncate();
   aValueAsString.AppendFloat(mBaseVal[0]);
   if (mBaseVal[0] != mBaseVal[1]) {
     aValueAsString.AppendLiteral(", ");
     aValueAsString.AppendFloat(mBaseVal[1]);
   }
 }
 
 void
 nsSVGNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex,
                               nsSVGElement *aSVGElement)
 {
   PRUint32 index = (aPairIndex == eFirst ? 0 : 1);
+  if (mIsBaseSet && mBaseVal[index] == aValue) {
+    return;
+  }
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum);
   mBaseVal[index] = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[index] = aValue;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeNumberPair(mAttrEnum, true);
+  aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue);
 }
 
 void
 nsSVGNumberPair::SetBaseValues(float aValue1, float aValue2,
                                nsSVGElement *aSVGElement)
 {
+  if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) {
+    return;
+  }
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum);
   mBaseVal[0] = aValue1;
   mBaseVal[1] = aValue2;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[0] = aValue1;
     mAnimVal[1] = aValue2;
   }
   else {
     aSVGElement->AnimationNeedsResample();
   }
-  aSVGElement->DidChangeNumberPair(mAttrEnum, true);
+  aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue);
 }
 
 void
 nsSVGNumberPair::SetAnimValue(const float aValue[2], nsSVGElement *aSVGElement)
 {
   mAnimVal[0] = aValue[0];
   mAnimVal[1] = aValue[1];
   mIsAnimated = true;
--- a/content/svg/content/src/nsSVGNumberPair.h
+++ b/content/svg/content/src/nsSVGNumberPair.h
@@ -62,17 +62,17 @@ public:
     mAnimVal[1] = mBaseVal[1] = aValue2;
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
     mIsBaseSet = false;
   }
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement);
-  void GetBaseValueString(nsAString& aValue);
+  void GetBaseValueString(nsAString& aValue) const;
 
   void SetBaseValue(float aValue, PairIndex aIndex, nsSVGElement *aSVGElement);
   void SetBaseValues(float aValue1, float aValue2, nsSVGElement *aSVGElement);
   float GetBaseValue(PairIndex aIndex) const
     { return mBaseVal[aIndex == eFirst ? 0 : 1]; }
   void SetAnimValue(const float aValue[2], nsSVGElement *aSVGElement);
   float GetAnimValue(PairIndex aIndex) const
     { return mAnimVal[aIndex == eFirst ? 0 : 1]; }
--- a/content/svg/content/src/nsSVGViewBox.cpp
+++ b/content/svg/content/src/nsSVGViewBox.cpp
@@ -122,20 +122,26 @@ nsSVGViewBox::SetAnimValue(float aX, flo
   }
   aSVGElement->DidAnimateViewBox();
 }
 
 void
 nsSVGViewBox::SetBaseValue(float aX, float aY, float aWidth, float aHeight,
                            nsSVGElement *aSVGElement)
 {
+  if (mHasBaseVal && mBaseVal == nsSVGViewBoxRect(aX, aY, aWidth, aHeight)) {
+    return;
+  }
+
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeViewBox();
+
   mBaseVal = nsSVGViewBoxRect(aX, aY, aWidth, aHeight);
   mHasBaseVal = true;
 
-  aSVGElement->DidChangeViewBox(true);
+  aSVGElement->DidChangeViewBox(emptyOrOldValue);
   if (mAnimVal) {
     aSVGElement->AnimationNeedsResample();
   }
 }
 
 static nsresult
 ToSVGViewBoxRect(const nsAString& aStr, nsSVGViewBoxRect *aViewBox)
 {
@@ -180,17 +186,17 @@ nsSVGViewBox::SetBaseValueString(const n
   nsresult res = ToSVGViewBoxRect(aValue, &viewBox);
   if (NS_SUCCEEDED(res)) {
     mBaseVal = nsSVGViewBoxRect(viewBox.x, viewBox.y, viewBox.width, viewBox.height);
     mHasBaseVal = true;
 
     if (mAnimVal) {
       aSVGElement->AnimationNeedsResample();
     }
-    // We don't need to call DidChange* here - we're only called by
+    // We don't need to call Will/DidChange* here - we're only called by
     // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
     // which takes care of notifying.
   }
   return res;
 }
 
 void
 nsSVGViewBox::GetBaseValueString(nsAString& aValue) const
--- a/content/svg/content/test/Makefile.in
+++ b/content/svg/content/test/Makefile.in
@@ -45,31 +45,33 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 # Disabled:
 #		bbox-helper.svg \
 #		test_length.xhtml \
 
 _TEST_FILES = \
 		matrixUtils.js \
+		MutationEventChecker.js \
 		test_a_href_01.xhtml \
 		test_a_href_02.xhtml \
 		a_href_destination.svg \
 		a_href_helper_01.svg \
 		a_href_helper_02_03.svg \
 		a_href_helper_04.svg \
 		test_animLengthObjectIdentity.xhtml \
 		test_animLengthReadonly.xhtml \
 		test_animLengthUnits.xhtml \
 		test_bbox.xhtml \
 		test_bbox-with-invalid-viewBox.xhtml \
 		test_bounds.html \
 		bbox-helper.svg \
 		bounds-helper.svg \
 		test_dataTypes.html \
+		test_dataTypesModEvents.html \
 		dataTypes-helper.svg \
 		getCTM-helper.svg \
 		test_getCTM.html \
 		test_getElementById.xhtml \
 		test_getSubStringLength.xhtml \
 		getSubStringLength-helper.svg \
 		test_isSupported.xhtml \
 		test_nonAnimStrings.xhtml \
@@ -83,17 +85,19 @@ include $(topsrcdir)/config/rules.mk
 		test_SVGAnimatedImageSMILDisabled.html \
 		animated-svg-image-helper.html \
 		animated-svg-image-helper.svg \
 		test_stroke-linecap-hit-testing.xhtml \
 		test_SVG_namespace_ids.html \
 		test_SVGLengthList.xhtml \
 		test_SVGLengthList-2.xhtml \
 		test_SVGMatrix.xhtml \
+		test_SVGNumberList.xhtml \
 		test_SVGPathSegList.xhtml \
+		test_SVGPointList.xhtml \
 		test_SVGStyleElement.xhtml \
 		test_SVGStringList.xhtml \
 		test_SVGTransformList.xhtml \
 		test_SVGTransformListAddition.xhtml \
 		test_SVGxxxList.xhtml \
 		test_SVGxxxListIndexing.xhtml \
 		test_switch.xhtml \
 		switch-helper.svg \
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/MutationEventChecker.js
@@ -0,0 +1,245 @@
+// Helper class to check DOM MutationEvents
+//
+// Usage:
+//
+// * Create a new event checker:
+//     var eventChecker = new MutationEventChecker;
+// * Set the attribute to watch
+//     eventChecker.watchAttr(<DOM element>, "<attribute name>");
+// * Set the events to expect (0..n)
+//     eventChecker.expect("add", "modify");
+//     OR
+//     eventChecker.expect("add modify");
+//     OR
+//     eventChecker.expect(MutationEvent.ADDITION, MutationEvent.MODIFICATION);
+//
+//  An empty string or empty set of arguments is also fine as a way of checking
+//  that all expected events have been received and indicating no events are
+//  expected from the following code, e.g.
+//
+//    eventChecker.expect("");
+//    // changes that are not expected to generate events
+//    eventChecker.expect("modify");
+//    // change that is expected to generate an event
+//    ...
+//
+// * Either finish listening or set the next attribute to watch
+//     eventChecker.finish();
+//     eventChecker.watchAttr(element, "nextAttribute");
+//
+//   In either case a check is performed that all expected events have been
+//   received.
+//
+// * Event checking can be temporarily disabled with ignoreEvents(). The next
+//   call to expect() will cause it to resume.
+
+function MutationEventChecker()
+{
+  this.expectedEvents = [];
+
+  this.watchAttr = function(element, attr)
+  {
+    if (this.attr) {
+      this.finish();
+    }
+
+    this.expectedEvents = [];
+    this.element        = element;
+    this.attr           = attr;
+    this.oldValue       = element.getAttribute(attr);
+    this.giveUp         = false;
+    this.ignore         = false;
+
+    this.element.addEventListener('DOMAttrModified', this._listener, false);
+  }
+
+  this.expect = function()
+  {
+    if (this.giveUp) {
+      return;
+    }
+
+    ok(this.expectedEvents.length == 0,
+       "Expecting new events for " + this.attr +
+       " but the following previously expected events have still not been " +
+       "received: " + this._stillExpecting());
+    if (this.expectedEvents.length != 0) {
+      this.giveUp = true;
+      return;
+    }
+
+    this.ignore = false;
+
+    if (arguments.length == 0 ||
+        arguments.length == 1 && arguments[0] == "") {
+      return;
+    }
+
+    // Turn arguments object into an array
+    var args = Array.prototype.slice.call(arguments);
+    // Check for whitespace separated keywords
+    if (args.length == 1 && typeof args[0] === 'string' &&
+        args[0].indexOf(' ') > 0) {
+      args = args[0].split(' ');
+    }
+    // Convert strings to event Ids
+    this.expectedEvents = args.map(this._argToEventId);
+  }
+
+  // Temporarily disable event checking
+  this.ignoreEvents = function()
+  {
+    // Check all events have been received
+    ok(this.giveUp || this.expectedEvents.length == 0,
+      "Going to ignore subsequent events on " + this.attr +
+      " attribute, but we're still expecting the following events: " +
+      this._stillExpecting());
+
+    this.ignore = true;
+  }
+
+  this.finish = function()
+  {
+    // Check all events have been received
+    ok(this.giveUp || this.expectedEvents.length == 0,
+      "Finishing listening to " + this.attr +
+      " attribute, but we're still expecting the following events: " +
+      this._stillExpecting());
+
+    this.element.removeEventListener('DOMAttrModified', this._listener, false);
+    this.attr = "";
+  }
+
+  this._receiveEvent = function(e)
+  {
+    if (this.giveUp || this.ignore) {
+      this.oldValue = e.newValue;
+      return;
+    }
+
+    // Make sure we're expecting something at all
+    if (this.expectedEvents.length == 0) {
+      ok(false, 'Unexpected ' + this._eventToName(e.attrChange) +
+         ' event when none expected on ' + this.attr + ' attribute.');
+      return;
+    }
+
+    var expectedEvent = this.expectedEvents.shift();
+
+    // Make sure we got the event we expected
+    if (e.attrChange != expectedEvent) {
+      ok(false, 'Unexpected ' + this._eventToName(e.attrChange) +
+        ' on ' + this.attr + ' attribute. Expected ' +
+        this._eventToName(expectedEvent) + ' (followed by: ' +
+        this._stillExpecting() + ")");
+      // If we get events out of sequence, it doesn't make sense to do any
+      // further testing since we don't really know what to expect
+      this.giveUp = true;
+      return;
+    }
+
+    // Common param checking
+    is(e.target, this.element,
+       'Unexpected node for mutation event on ' + this.attr + ' attribute');
+    is(e.attrName, this.attr, 'Unexpected attribute name for mutation event');
+
+    // Don't bother testing e.relatedNode since Attr nodes are on the way
+    // out anyway (but then, so are mutation events...)
+
+    // Event-specific checking
+    if (e.attrChange == MutationEvent.MODIFICATION) {
+      ok(this.element.hasAttribute(this.attr),
+         'Attribute not set after modification');
+      is(e.prevValue, this.oldValue,
+         'Unexpected old value for modification to ' + this.attr +
+         ' attribute');
+      isnot(e.newValue, this.oldValue,
+         'Unexpected new value for modification to ' + this.attr +
+         ' attribute');
+    } else if (e.attrChange == MutationEvent.REMOVAL) {
+      ok(!this.element.hasAttribute(this.attr), 'Attribute set after removal');
+      is(e.prevValue, this.oldValue,
+         'Unexpected old value for removal of ' + this.attr +
+         ' attribute');
+      // DOM 3 Events doesn't say what value newValue will be for a removal
+      // event but generally empty strings are used for other events when an
+      // attribute isn't relevant
+      ok(e.newValue === "",
+         'Unexpected new value for removal of ' + this.attr +
+         ' attribute');
+    } else if (e.attrChange == MutationEvent.ADDITION) {
+      ok(this.element.hasAttribute(this.attr),
+         'Attribute not set after addition');
+      // DOM 3 Events doesn't say what value prevValue will be for an addition
+      // event but generally empty strings are used for other events when an
+      // attribute isn't relevant
+      ok(e.prevValue === "",
+         'Unexpected old value for addition of ' + this.attr +
+         ' attribute');
+      ok(typeof(e.newValue) == 'string' && e.newValue !== "",
+         'Unexpected new value for addition of ' + this.attr +
+         ' attribute');
+    } else {
+      ok(false, 'Unexpected mutation event type: ' + e.attrChange);
+      this.giveUp = true;
+    }
+    this.oldValue = e.newValue;
+  }
+  this._listener = this._receiveEvent.bind(this);
+
+  this._stillExpecting = function()
+  {
+    if (this.expectedEvents.length == 0) {
+      return "(nothing)";
+    }
+    var eventNames = [];
+    for (var i=0; i < this.expectedEvents.length; i++) {
+      eventNames.push(this._eventToName(this.expectedEvents[i]));
+    }
+    return eventNames.join(", ");
+  }
+
+  this._eventToName = function(evtId)
+  {
+    switch (evtId)
+    {
+    case MutationEvent.MODIFICATION:
+      return "modification";
+    case MutationEvent.ADDITION:
+      return "addition";
+    case MutationEvent.REMOVAL:
+      return "removal";
+    }
+  }
+
+  this._argToEventId = function(arg)
+  {
+    if (typeof arg === 'number')
+      return arg;
+
+    if (typeof arg !== 'string') {
+      ok(false, "Unexpected event type: " + arg);
+      return 0;
+    }
+
+    switch (arg.toLowerCase())
+    {
+    case "mod":
+    case "modify":
+    case "modification":
+      return MutationEvent.MODIFICATION;
+
+    case "add":
+    case "addition":
+      return MutationEvent.ADDITION;
+
+    case "removal":
+    case "remove":
+      return MutationEvent.REMOVAL;
+
+    default:
+      ok(false, "Unexpected event name: " + arg);
+      return 0;
+    }
+  }
+}
--- a/content/svg/content/test/test_SVGLengthList.xhtml
+++ b/content/svg/content/test/test_SVGLengthList.xhtml
@@ -1,15 +1,16 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=515116
 -->
 <head>
   <title>Tests specific to SVGLengthList</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="MutationEventChecker.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=515116">Mozilla Bug 515116</a>
 <p id="display"></p>
 <div id="content" style="display:none;">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
   <text id="text" x="10cm 20cm 30mc"/>
@@ -31,21 +32,41 @@ function run_tests()
 {
   document.getElementById('svg').pauseAnimations();
 
   var text = document.getElementById("text");
   var lengths = text.x.baseVal;
 
   is(lengths.numberOfItems, 0, 'Checking numberOfItems');
 
-/*
-  is(lengths.numberOfItems, 2, 'Checking numberOfItems');
-  is(lengths.getItem(1).valueInSpecifiedUnits == 20, 'Checking the value of the second length');
-  is(lengths.getItem(1).unitType == SVGLength.SVG_LENGTHTYPE_CM, 'Checking the unit of the second length');
-*/
+  // Test mutation events
+  // --- Initialization
+  eventChecker = new MutationEventChecker;
+  eventChecker.watchAttr(text, "x");
+  eventChecker.expect("modify");
+  text.textContent = "abc";
+  text.setAttribute("x", "10 20 30");
+  is(lengths.numberOfItems, 3, 'Checking numberOfItems');
+  // -- Actual changes
+  eventChecker.expect("modify modify modify modify modify");
+  lengths[0].value = 8;
+  lengths[0].valueInSpecifiedUnits = 9;
+  lengths[0].valueAsString = "10";
+  lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM);
+  lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM, 11);
+  // -- Redundant changes
+  eventChecker.expect("modify");
+  lengths[0].valueAsString = "10";
+  eventChecker.expect("");
+  lengths[0].value = 10;
+  lengths[0].valueInSpecifiedUnits = 10;
+  lengths[0].valueAsString = "10";
+  lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER);
+  lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER, 10);
+  eventChecker.finish();
 
   SimpleTest.finish();
 }
 
 window.addEventListener("load", run_tests, false);
 
 ]]>
 </script>
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/test_SVGNumberList.xhtml
@@ -0,0 +1,64 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629200
+-->
+<head>
+  <title>Tests specific to SVGNumberList</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="MutationEventChecker.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=629200">Mozilla Bug 629200</a>
+<p id="display"></p>
+<div id="content" style="display:none;">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
+  <text id="text" rotate="10 20 30">abc</text>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+/*
+This file runs a series of SVGNumberList specific tests. Generic SVGXxxList
+tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized
+to other list types belongs there.
+*/
+
+function run_tests()
+{
+  document.getElementById('svg').pauseAnimations();
+
+  var text = document.getElementById("text");
+  var numbers = text.rotate.baseVal;
+
+  is(numbers.numberOfItems, 3, 'Checking numberOfItems');
+
+  // Test mutation events
+  // --- Initialization
+  eventChecker = new MutationEventChecker;
+  eventChecker.watchAttr(text, "rotate");
+  // -- Actual changes
+  eventChecker.expect("modify modify");
+  numbers[0].value = 15;
+  text.setAttribute("rotate", "17 20 30");
+  // -- Redundant changes
+  eventChecker.expect("");
+  numbers[0].value = 17;
+  numbers[1].value = 20;
+  text.setAttribute("rotate", "17 20 30");
+  eventChecker.finish();
+
+  SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests, false);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
--- a/content/svg/content/test/test_SVGPathSegList.xhtml
+++ b/content/svg/content/test/test_SVGPathSegList.xhtml
@@ -1,15 +1,16 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=611138
 -->
 <head>
   <title>Generic tests for SVG animated length lists</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="MutationEventChecker.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=611138">Mozilla Bug 611138</a>
 <p id="display"></p>
 <div id="content" style="display:none;">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
   <path id="path"/>
@@ -81,27 +82,35 @@ function run_tests()
   ok(list.numberOfItems == 3 && list.getItem(1) == seg,
     'insertItemBefore should be able insert items into an invalid path');
 
   seg = path.createSVGPathSegLinetoAbs(1, 2);
   list.initialize(seg);
   ok(list.numberOfItems == 1 && list.getItem(0) == seg,
     'initialize should be able initialize an invalid path with a non-moveto item');
 
+  // Test mutation events
+
+  eventChecker = new MutationEventChecker;
   d = 'M0,0 L12,34'
   path.setAttribute('d', d);
-  function check_old_value(e) {
-    is(e.target, path, 'check mutation event is for expected node');
-    is(e.attrName, 'd', 'check mutation event is for expected attribute');
-    is(e.prevValue, d, 'check old attribute value is correctly reported');
-    isnot(e.newValue, d, 'check attribute value has changed');
-  }
-  path.addEventListener('DOMAttrModified', check_old_value, false);
-  list.getItem(1).y = 35;
-  path.removeEventListener('DOMAttrModified', check_old_value, false);
+  eventChecker.watchAttr(path, "d");
+
+  // -- Actual changes
+  eventChecker.expect("modify modify modify");
+  list[0].x = 10;
+  list[0].y = 5;
+  path.setAttribute("d", "M20,5 L12,34");
+
+  // -- Redundant changes
+  eventChecker.expect("");
+  list[0].x = 20;
+  list[1].y = 34;
+  path.setAttribute("d", "M20,5 L12,34");
+  eventChecker.finish();
 
   SimpleTest.finish();
 }
 
 window.addEventListener("load", run_tests, false);
 
 ]]>
 </script>
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/test_SVGPointList.xhtml
@@ -0,0 +1,64 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629200
+-->
+<head>
+  <title>Tests specific to SVGPointList</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="MutationEventChecker.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=629200">Mozilla Bug 629200</a>
+<p id="display"></p>
+<div id="content" style="display:none;">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
+  <polyline id="polyline" points="50,375 150,380"/>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+/*
+This file runs a series of SVGPointList specific tests. Generic SVGXxxList
+tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized
+to other list types belongs there.
+*/
+
+function run_tests()
+{
+  document.getElementById('svg').pauseAnimations();
+
+  var polyline = document.getElementById("polyline");
+  var points = polyline.points;
+
+  is(points.numberOfItems, 2, 'Checking numberOfItems');
+
+  // Test mutation events
+  // --- Initialization
+  eventChecker = new MutationEventChecker;
+  eventChecker.watchAttr(polyline, "points");
+  // -- Actual changes
+  eventChecker.expect("modify modify");
+  points[0].x = 40;
+  polyline.setAttribute("points", "30,375 150,380");
+  // -- Redundant changes
+  eventChecker.expect("");
+  points[0].x = 30;
+  points[1].y = 380;
+  polyline.setAttribute("points", "30,375 150,380");
+  eventChecker.finish();
+
+  SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests, false);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
--- a/content/svg/content/test/test_SVGTransformList.xhtml
+++ b/content/svg/content/test/test_SVGTransformList.xhtml
@@ -2,16 +2,17 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=602759
 -->
 <head>
   <title>Tests specific to SVGTransformList</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="matrixUtils.js"></script>
+  <script type="text/javascript" src="MutationEventChecker.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=602759">
   Mozilla Bug 602759</a>
 <p id="display"></p>
 <div id="content" style="display:none;">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"
@@ -36,17 +37,18 @@ function main()
   var g = $('g');
   var tests =
     [ testConsolidateMatrix,
       testConsolidateMatrixOneElem,
       testConsolidateMatrixZeroElem,
       testCreateSVGTransformFromMatrix,
       testReadOnly,
       testOrphan,
-      testFailedSet
+      testFailedSet,
+      testMutationEvents
     ];
   for (var i = 0; i < tests.length; i++) {
     tests[i](g);
   }
   SimpleTest.finish();
 }
 
 function testConsolidateMatrix(g)
@@ -350,15 +352,85 @@ function testFailedSet(g)
   // Attempt to set bad value
   g.setAttribute("transform", "translate(40 50) scale(a)");
   is(list.numberOfItems, 0,
      "Transform list should be empty after setting bad value");
   is(g.transform.animVal.numberOfItems, 0,
      "Animated transform list should also be empty after setting bad value");
 }
 
+function testMutationEvents(g)
+{
+  // Check mutation events
+
+  // Set initial value
+  g.setAttribute("transform", "translate(50 90)");
+  var list = g.transform.baseVal;
+  is(list.numberOfItems, 1, "Unexpected initial length of list");
+  eventChecker = new MutationEventChecker;
+  eventChecker.watchAttr(g, "transform");
+
+  // consolidate
+  //
+  // Consolidate happens to generate two modification events in our
+  // implementation--it's not ideal but it's better than none
+  eventChecker.expect("modify modify modify");
+  g.setAttribute("transform", "translate(10 10) translate(10 10)");
+  list.consolidate();
+
+  // In the following, each of the operations is performed twice but only one
+  // mutation event is expected. This is to check that redundant mutation
+  // events are not sent.
+
+  // transform.setMatrix
+  eventChecker.expect("modify");
+  var mx = $('svg').createSVGMatrix();
+  list[0].setMatrix(mx);
+  list[0].setMatrix(mx);
+
+  // transform.setTranslate
+  eventChecker.expect("modify");
+  list[0].setTranslate(10, 10);
+  list[0].setTranslate(10, 10);
+
+  // transform.setScale
+  eventChecker.expect("modify");
+  list[0].setScale(2, 2);
+  list[0].setScale(2, 2);
+
+  // transform.setRotate
+  eventChecker.expect("modify");
+  list[0].setRotate(45, 1, 2);
+  list[0].setRotate(45, 1, 2);
+
+  // transform.setSkewX
+  eventChecker.expect("modify");
+  list[0].setSkewX(45);
+  list[0].setSkewX(45);
+
+  // transform.setSkewY
+  eventChecker.expect("modify");
+  list[0].setSkewY(25);
+  list[0].setSkewY(25);
+
+  // transform.matrix
+  eventChecker.expect("modify modify");
+  list[0].matrix.a = 1;
+  list[0].matrix.a = 1;
+  list[0].matrix.e = 5;
+  list[0].matrix.e = 5;
+
+  // setAttribute interaction
+  eventChecker.expect("modify");
+  list[0].setMatrix(mx);
+  eventChecker.expect("");
+  g.setAttribute("transform", "matrix(1, 0, 0, 1, 0, 0)");
+  list[0].setMatrix(mx);
+  eventChecker.finish();
+}
+
 window.addEventListener("load", main, false);
 
 ]]>
 </script>
 </pre>
 </body>
 </html>
--- a/content/svg/content/test/test_SVGxxxList.xhtml
+++ b/content/svg/content/test/test_SVGxxxList.xhtml
@@ -1,16 +1,17 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=515116
 -->
 <head>
   <title>Generic tests for SVG animated length lists</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="matrixUtils.js"></script>
+  <script type="text/javascript" src="MutationEventChecker.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=515116">Mozilla Bug 515116</a>
 <p id="display"></p>
 <div id="content" style="display:none;">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"
      onload="this.pauseAnimations();">
@@ -447,40 +448,78 @@ function get_array_of_list_items(list)
  *   SVGLength removeItem(in unsigned long index)
  *   SVGLength appendItem(in SVGLength newItem)
  *
  * @param t A test from the 'tests' array.
  */
 function run_baseVal_API_tests()
 {
   var res, threw, items;
+  var eventChecker = new MutationEventChecker;
 
   for each (var t in tests) {
 
     // Test .clear():
 
     t.element.setAttribute(t.attr_name, t.attr_val_4);
 
     is(t.baseVal.numberOfItems, 4,
        'The '+t.list_type+' object should contain four list items.');
 
+    eventChecker.watchAttr(t.element, t.attr_name);
+    eventChecker.expect("modify");
     res = t.baseVal.clear();
 
     is(t.baseVal.numberOfItems, 0,
        'The method '+t.list_type+'.clear() should clear the '+t.list_type+
        ' object.');
     is(res, undefined,
        'The method '+t.list_type+'.clear() should not return a value.');
+    ok(t.element.hasAttribute(t.attr_name),
+       'The method '+t.list_type+'.clear() should not remove the attribute.');
+    ok(t.element.getAttribute(t.attr_name) === "",
+       'Cleared '+t.attr_name+' ('+t.list_type+') but did not get an '+
+       'empty string back.');
+
+    eventChecker.expect("");
+    t.baseVal.clear();
+    eventChecker.ignoreEvents();
+
+    // Test empty strings
+
+    t.element.setAttribute(t.attr_name, "");
+    ok(t.element.getAttribute(t.attr_name) === "",
+       'Set an empty attribute value for '+t.attr_name+' ('+t.list_type+
+       ') but did not get an empty string back.');
+
+    // Test removed attributes
+
+    t.element.removeAttribute(t.attr_name);
+    ok(t.element.getAttribute(t.attr_name) === null,
+       'Removed attribute value for '+t.attr_name+' ('+t.list_type+
+       ') but did not get null back.');
+    ok(!t.element.hasAttribute(t.attr_name),
+       'Removed attribute value for '+t.attr_name+' ('+t.list_type+
+       ') but hasAttribute still returns true.');
 
     // Test .initialize():
 
     t.element.setAttribute(t.attr_name, t.attr_val_4);
 
     var item = t.item_constructor();
+    // Our current implementation of 'initialize' for most list types performs
+    // a 'clear' followed by an 'insertItemBefore'. This results in two
+    // modification events being dispatched. SVGStringList however avoids the
+    // additional clear.
+    var expectedModEvents =
+      t.item_type == "DOMString" ? "modify" : "modify modify";
+    eventChecker.expect(expectedModEvents);
     var res = t.baseVal.initialize(item);
+    eventChecker.ignoreEvents();
+
 
     is(t.baseVal.numberOfItems, 1,
        'The '+t.list_type+' object should contain one list item.');
     ok(res === item,
        'The list item returned by '+t.list_type+'.initialize() should be the '+
        'exact same object as the item that was passed to that method, since '+
        'the item that was passed to that method did not already belong to a '+
        'list.');
@@ -510,34 +549,38 @@ function run_baseVal_API_tests()
       res = t.baseVal.initialize(item);
 
       ok(res !== item &&
          t.baseVal.getItem(0) !== item,
          'The method '+t.list_type+'.initialize() should clone the object that '+
          'is passed in, even if that object is the only item in that list.');
       // [SVGWG issue] not what the spec currently says
 
+      eventChecker.expect("");
       threw = false;
       try {
         t.baseVal.initialize({});
       } catch(e) {
         threw = true;
       }
       ok(threw,
          'The method '+t.list_type+'.initialize() should throw if passed an '+
          'object of the wrong type.');
+      eventChecker.ignoreEvents();
     }
 
     // Test .insertItemBefore():
 
     t.element.setAttribute(t.attr_name, t.attr_val_4);
 
     old_items = get_array_of_list_items(t.baseVal);
     item = t.item_constructor();
+    eventChecker.expect("modify");
     res = t.baseVal.insertItemBefore(item, 2);
+    eventChecker.ignoreEvents();
 
     is(t.baseVal.numberOfItems, 5,
        'The '+t.list_type+' object should contain five list items.');
     ok(res === item,
        'The list item returned by '+t.list_type+'.insertItemBefore() should '+
        'be the exact same object as the item that was passed to that method, '+
        'since the item that was passed to that method did not already belong '+
        'to a list.');
@@ -582,34 +625,38 @@ function run_baseVal_API_tests()
     if (t.item_type != "DOMString") {
       ok(res !== item &&
          t.baseVal.getItem(2) !== item,
          'The method '+t.list_type+'.insertItemBefore() should clone the '+
          'object that is passed in, even if that object is the item in '+
          'the list at the index specified.');
       // [SVGWG issue] not what the spec currently says
 
+      eventChecker.expect("");
       threw = false;
       try {
         t.baseVal.insertItemBefore({}, 2);
       } catch(e) {
         threw = true;
       }
       ok(threw,
          'The method '+t.list_type+'.insertItemBefore() should throw if passed '+
          'an object of the wrong type.');
+      eventChecker.ignoreEvents();
     }
 
     // Test .replaceItem():
 
     t.element.setAttribute(t.attr_name, t.attr_val_4);
 
     old_items = get_array_of_list_items(t.baseVal);
     item = t.item_constructor();
+    eventChecker.expect("modify");
     res = t.baseVal.replaceItem(item, 2);
+    eventChecker.ignoreEvents();
 
     is(t.baseVal.numberOfItems, 4,
        'The '+t.list_type+' object should contain four list items.');
     if (t.item_type != "DOMString") {
       ok(res === item,
          'The list item returned by '+t.list_type+'.replaceItem() should be '+
          'the exact same object as the item that was passed to that method, '+
          'since the item that was passed to that method did not already belong '+
@@ -622,25 +669,27 @@ function run_baseVal_API_tests()
        'to a list.');
     ok(t.baseVal.getItem(3) === old_items[3],
        'The list item that was at index 3 should still be at index 3 after '+
        'the item at index 2 was replaced using the '+t.list_type+
        '.replaceItem() method.');
 
     item = t.item_constructor();
 
+    eventChecker.expect("");
     threw = false;
     try {
       t.baseVal.replaceItem(item, 100);
     } catch(e) {
       threw = true;
     }
     ok(threw,
        'The method '+t.list_type+'.replaceItem() should throw if passed '+
        'an index that is out of bounds.');
+    eventChecker.ignoreEvents();
 
     old_items = get_array_of_list_items(t.baseVal);
     item = t.baseVal.getItem(3);
     res = t.baseVal.replaceItem(item, 1);
 
     is(t.baseVal.numberOfItems, 4,
        'The '+t.list_type+' object should contain four list items.');
     if (t.item_type != "DOMString") {
@@ -678,17 +727,19 @@ function run_baseVal_API_tests()
     }
 
     // Test .removeItem():
 
     t.element.setAttribute(t.attr_name, t.attr_val_4);
 
     old_items = get_array_of_list_items(t.baseVal);
     item = t.baseVal.getItem(2);
+    eventChecker.expect("modify");
     res = t.baseVal.removeItem(2);
+    eventChecker.ignoreEvents();
 
     is(t.baseVal.numberOfItems, 3,
        'The '+t.list_type+' object should contain three list items.');
     if (t.item_type != "DOMString") {
       ok(res === item,
          'The list item returned by '+t.list_type+'.removeItem() should be the '+
          'exact same object as the item that was at the specified index.');
     }
@@ -696,33 +747,37 @@ function run_baseVal_API_tests()
        'The list item that was at index 1 should still be at index 1 after '+
        'the item at index 2 was removed using the '+t.list_type+
        '.replaceItem() method.');
     ok(t.baseVal.getItem(2) === old_items[3],
        'The list item that was at index 3 should still be at index 2 after '+
        'the item at index 2 was removed using the '+t.list_type+
        '.replaceItem() method.');
 
+    eventChecker.expect("");
     threw = false;
     try {
       t.baseVal.removeItem(100);
     } catch(e) {
       threw = true;
     }
     ok(threw,
        'The method '+t.list_type+'.removeItem() should throw if passed '+
        'an index that is out of bounds.');
+    eventChecker.ignoreEvents();
 
     // Test .appendItem():
 
     t.element.setAttribute(t.attr_name, t.attr_val_4);
 
     old_items = get_array_of_list_items(t.baseVal);
     item = t.item_constructor();
+    eventChecker.expect("modify");
     res = t.baseVal.appendItem(item);
+    eventChecker.ignoreEvents();
 
     is(t.baseVal.numberOfItems, 5,
        'The '+t.list_type+' object should contain five list items.');
     ok(res === item,
        'The list item returned by '+t.list_type+'.appendItem() should be the '+
        'exact same object as the item that was passed to that method, since '+
        'the item that was passed to that method did not already belong '+
        'to a list.');
@@ -758,26 +813,35 @@ function run_baseVal_API_tests()
     if (t.item_type != "DOMString") {
       ok(res !== item &&
          t.baseVal.getItem(6) !== item,
          'The method '+t.list_type+'.appendItem() should clone the object '+
          'that is passed in, if that object is already the last item in '+
          'that list.');
       // [SVGWG issue] not what the spec currently says
 
+      eventChecker.expect("");
       threw = false;
       try {
         t.baseVal.appendItem({});
       } catch(e) {
         threw = true;
       }
       ok(threw,
          'The method '+t.list_type+'.appendItem() should throw if passed '+
          'an object of the wrong type.');
+      eventChecker.ignoreEvents();
     }
+
+    // Test removal and addition events
+
+    eventChecker.expect("remove add");
+    t.element.removeAttribute(t.attr_name);
+    res = t.baseVal.appendItem(item);
+    eventChecker.finish();
   }
 }
 
 
 /**
  * This function tests the SVGXxxList API for the anim val list (see also the
  * comment for test_baseVal_API).
  */
@@ -875,17 +939,17 @@ function run_animVal_API_tests()
        'The method '+t.list_type+'.appendItem() should throw when called '+
        'on an anim val list, since anim val lists should be readonly.');
   }
 }
 
 
 /**
  * This function runs some basic tests to check the effect of setAttribute()
- * calls on object identidy, without taking SMIL animation into consideration.
+ * calls on object identity, without taking SMIL animation into consideration.
  */
 function run_basic_setAttribute_tests()
 {
   for each (var t in tests) {
 
     // Since the t.prop, t.baseVal and t.animVal objects should never ever
     // change, we leave testing of them to our caller so that it can check
     // them after all the other mutations such as SMIL changes.
--- a/content/svg/content/test/test_dataTypes.html
+++ b/content/svg/content/test/test_dataTypes.html
@@ -33,18 +33,21 @@ function runTests()
   is(filter.className.baseVal, "foo", "className baseVal");
   is(filter.className.animVal, "foo", "className animVal");
   filter.className.baseVal = "bar";
   is(filter.getAttribute("class"), "bar", "class attribute");
   is(filter.className.baseVal, "bar", "className baseVal");
   is(filter.className.animVal, "bar", "className animVal");
   filter.removeAttribute("class");
   is(filter.hasAttribute("class"), false, "class attribute");
+  ok(filter.getAttribute("class") === null, "removed class attribute");
   is(filter.className.baseVal, "", "className baseVal");
   is(filter.className.animVal, "", "className animVal");
+  filter.setAttribute("class", "");
+  ok(filter.getAttribute("class") === "", "empty class attribute");
 
   // length attribute
 
   marker.setAttribute("markerWidth", "12.5");
   is(marker.markerWidth.baseVal.value, 12.5, "length baseVal");
   is(marker.markerWidth.animVal.value, 12.5, "length animVal");
 
   var baseMarkerWidth = marker.markerWidth.baseVal;
@@ -52,26 +55,36 @@ function runTests()
   marker.setAttribute("markerWidth", "15.5");
   is(baseMarkerWidth.value, 15.5, "length baseVal");
   is(animMarkerWidth.value, 15.5, "length animVal");
 
   marker.markerWidth.baseVal.value = 7.5;
   is(marker.markerWidth.animVal.value, 7.5, "length animVal");
   is(marker.getAttribute("markerWidth"), "7.5", "length attribute");
 
+  marker.setAttribute("markerWidth", "");
+  ok(marker.getAttribute("markerWidth") === "", "empty length attribute");
+  marker.removeAttribute("markerWidth");
+  ok(marker.getAttribute("markerWidth") === null, "removed length attribute");
+
   // number attribute
 
   convolve.setAttribute("divisor", "12.5");
   is(convolve.divisor.baseVal, 12.5, "number baseVal");
   is(convolve.divisor.animVal, 12.5, "number animVal");
 
   convolve.divisor.baseVal = 7.5;
   is(convolve.divisor.animVal, 7.5, "number animVal");
   is(convolve.getAttribute("divisor"), "7.5", "number attribute");
 
+  convolve.setAttribute("divisor", "");
+  ok(convolve.getAttribute("divisor") === "", "empty number attribute");
+  convolve.removeAttribute("divisor");
+  ok(convolve.getAttribute("divisor") === null, "removed number attribute");
+
   // number-optional-number attribute
 
   blur.setAttribute("stdDeviation", "20.5");
   is(blur.stdDeviationX.baseVal, 20.5, "number-optional-number first baseVal");
   is(blur.stdDeviationX.animVal, 20.5, "number-optional-number first animVal");
   is(blur.stdDeviationY.baseVal, 20.5, "number-optional-number second baseVal");
   is(blur.stdDeviationY.animVal, 20.5, "number-optional-number second animVal");
 
@@ -84,24 +97,35 @@ function runTests()
   is(blur.getAttribute("stdDeviation"), "8.5", "number-optional-number attribute");
 
   blur.setStdDeviation(24.5, 0.5);
   is(blur.stdDeviationX.baseVal, 24.5, "integer-optional-integer first baseVal");
   is(blur.stdDeviationX.animVal, 24.5, "integer-optional-integer first animVal");
   is(blur.stdDeviationY.baseVal, 0.5, "integer-optional-integer second baseVal");
   is(blur.stdDeviationY.animVal, 0.5, "integer-optional-integer second animVal");
 
+  blur.setAttribute("stdDeviation", "");
+  ok(blur.getAttribute("stdDeviation") === "",
+     "empty number-optional-number attribute");
+  blur.removeAttribute("stdDeviation");
+  ok(blur.getAttribute("stdDeviation") === null,
+     "removed number-optional-number attribute");
+
   // integer attribute
 
   convolve.setAttribute("targetX", "12");
   is(convolve.targetX.baseVal, 12, "integer baseVal");
   is(convolve.targetX.animVal, 12, "integer animVal");
   convolve.targetX.baseVal = 7;
   is(convolve.targetX.animVal, 7, "integer animVal");
   is(convolve.getAttribute("targetX"), "7", "integer attribute");
+  convolve.setAttribute("targetX", "");
+  ok(convolve.getAttribute("targetX") === "", "empty integer attribute");
+  convolve.removeAttribute("targetX");
+  ok(convolve.getAttribute("targetX") === null, "removed integer attribute");
 
   // integer-optional-integer attribute
 
   filter.setAttribute("filterRes", "100");
   is(filter.filterResX.baseVal, 100, "integer-optional-integer first baseVal");
   is(filter.filterResX.animVal, 100, "integer-optional-integer first animVal");
   is(filter.filterResY.baseVal, 100, "integer-optional-integer second baseVal");
   is(filter.filterResY.animVal, 100, "integer-optional-integer second animVal");
@@ -115,58 +139,83 @@ function runTests()
   is(filter.getAttribute("filterRes"), "50", "integer-optional-integer attribute");
 
   filter.setFilterRes(80, 90);
   is(filter.filterResX.baseVal, 80, "integer-optional-integer first baseVal");
   is(filter.filterResX.animVal, 80, "integer-optional-integer first animVal");
   is(filter.filterResY.baseVal, 90, "integer-optional-integer second baseVal");
   is(filter.filterResY.animVal, 90, "integer-optional-integer second animVal");
 
+  filter.setAttribute("filterRes", "");
+  ok(filter.getAttribute("filterRes") === "",
+     "empty integer-optional-integer attribute");
+  filter.removeAttribute("filterRes");
+  ok(filter.getAttribute("filterRes") === null,
+     "removed integer-optional-integer attribute");
+
   // angle attribute
 
   marker.setAttribute("orient", "90deg");
   is(marker.orientAngle.baseVal.value, 90, "angle baseVal");
   is(marker.orientAngle.animVal.value, 90, "angle animVal");
 
   var baseAngle = marker.orientAngle.baseVal;
   var animAngle = marker.orientAngle.animVal;
   marker.setAttribute("orient", "45deg");
   is(baseAngle.value, 45, "angle baseVal");
   is(animAngle.value, 45, "angle animVal");
 
   marker.orientAngle.baseVal.value = 30;
   is(marker.orientAngle.animVal.value, 30, "angle animVal");
   is(marker.getAttribute("orient"), "30deg", "angle attribute");
 
+  marker.setAttribute("orient", "");
+  ok(marker.getAttribute("orient") === "", "empty angle attribute");
+  marker.removeAttribute("orient");
+  ok(marker.getAttribute("orient") === null, "removed angle attribute");
+
   // boolean attribute
 
   convolve.setAttribute("preserveAlpha", "false");
   is(convolve.preserveAlpha.baseVal, false, "boolean baseVal");
   is(convolve.preserveAlpha.animVal, false, "boolean animVal");
   convolve.preserveAlpha.baseVal = true;
   is(convolve.preserveAlpha.animVal, true, "boolean animVal");
   is(convolve.getAttribute("preserveAlpha"), "true", "boolean attribute");
+  convolve.setAttribute("preserveAlpha", "");
+  ok(convolve.getAttribute("preserveAlpha") === "", "empty boolean attribute");
+  convolve.removeAttribute("preserveAlpha");
+  ok(convolve.getAttribute("preserveAlpha") === null,
+     "removed boolean attribute");